Hey All,

I’m in no means a power-shell guru but ive been tasked with identifying shortcut paths for users at our company. I put together this script to try to check a .csv for a username and then search the user directory for the shortcut paths. The script works to get the paths when i run it right to the user directory but i wanted to sort of automate this by having a list of users and it searching said user directory and then outputting the result into another csv. My issue is i cant seem to get it to take the user from the .csv and search in only that user directory. Ive tried using this variable but it searches my local pc instead and errors out. Anyone have any tips or other variables that may help? Hopefully i don’t sound to confusing. Thanks all

\UNCPATHTOUSERPROFILES$env:USERNAME

$Users= (Get-Content C:\temp\Users.csv) -notmatch ‘^\s*$’

foreach ($User in $Users){

function Get-StartMenuShortcuts{
$Shortcuts = Get-ChildItem -Recurse \UNCPATHTOUSERPROFILES$env:USERNAME -Include *.lnk
$Shell = New-Object -ComObject WScript.Shell
foreach ($Shortcut in $Shortcuts)
{
$Properties = @{
ShortcutName = $Shortcut.Name;
ShortcutFull = $Shortcut.FullName;
ShortcutPath = $shortcut.DirectoryName
Target = $Shell.CreateShortcut($Shortcut).targetpath
}
New-Object PSObject -Property $Properties
}

[Runtime.InteropServices.Marshal]::ReleaseComObject($Shell) | Out-Null
}

$Output = Get-StartMenuShortcuts
$Output.Target
}

7 Spice ups

$env:Username is an automatic variable that contains the username of the user logged into the current session (i.e., the Powershell console). You need to use your own $User variable instead.

$Users= (Get-Content C:\temp\Users.csv) -notmatch '^\s*$'

foreach ($User in $Users){ 

function Get-StartMenuShortcuts{
    $Shortcuts = Get-ChildItem -Recurse \\UNCPATHTOUSERPROFILES\$User -Include *.lnk
    $Shell = New-Object -ComObject WScript.Shell
    foreach ($Shortcut in $Shortcuts)
    {
        $Properties = @{
        ShortcutName = $Shortcut.Name;
        ShortcutFull = $Shortcut.FullName;
        ShortcutPath = $shortcut.DirectoryName
        Target = $Shell.CreateShortcut($Shortcut).targetpath
        }
        New-Object PSObject -Property $Properties
    }

[Runtime.InteropServices.Marshal]::ReleaseComObject($Shell) | Out-Null
}

$Output = Get-StartMenuShortcuts
$Output.Target
}
1 Spice up

Ahh of course! Thank you so much for your reply, i’m going to change it when i get in.

@techadmin8

Ugh.

First off, never put function definitions inside a loop, that defeats the purpose of the function altogether.

Second, you are still calling an environment variable instead of the loop variable:

# Wrong
foreach ($User in $Users){
    $Shortcuts = Get-ChildItem -Recurse \\UNCPATHTOUSERPROFILES\$env:USERNAME -Include *.lnk
...
}

# Right
foreach ($User in $Users){
    $Shortcuts = Get-ChildItem -Recurse \\UNCPATHTOUSERPROFILES\$User -Include *.lnk
...
}

Third, you never call the function. Each iteration you only define the function and don’t do anything with it. In all honesty the code is short enough that a function is not really justified, just put the code inside the loop:

$Users = (Get-Content C:\temp\Users.csv) -notmatch '^\s*$'

foreach ($User in $Users) {
    $Shortcuts = Get-ChildItem -Recurse \\UNCPATHTOUSERPROFILES\$User -Include *.lnk
    $Shell = New-Object -ComObject WScript.Shell
    $data = foreach ($Shortcut in $Shortcuts) {
        [PSCustomObject]@{
            ShortcutName = $Shortcut.Name;
            ShortcutFull = $Shortcut.FullName;
            ShortcutPath = $shortcut.DirectoryName
            Target       = $Shell.CreateShortcut($Shortcut).targetpath
            # Always useful to know the user
            User         = $User
        }        
    }
    [Runtime.InteropServices.Marshal]::ReleaseComObject($Shell) | Out-Null
}
$data | Export-Csv report.csv -NoTypeInformation
3 Spice ups

As a further improvement, you can move the shell instantiation and disposal outside of the loop. There is no need to do this multiple times.

1 Spice up

add more?

Function Check-Shortcut{
	[CmdletBinding()]
		Param(
			[Parameter(
				Mandatory=$True,
				HelpMessage="Please enter Path of Shortcut, Example c:\folder",
				Position=1
			)][string]$path
		    )
$ErrorActionPreference='Silentlycontinue'
$Shortcuts = Get-ChildItem -Recurse $path -Include *.lnk -Force
$Shell = New-Object -ComObject WScript.Shell
 Foreach ($Shortcut in $Shortcuts)
    {
      $checks=$shell.CreateShortcut($Shortcut).TargetPath   
     
Foreach($check in $checks){
     $pathcheck=Test-Path $($check)
  if($pathcheck -eq $false){
  [Pscustomobject]@{
  Status       ='Failed'
  Shortcut     =$Shortcut.name
  FailedTopath =$($check)
  }
  }
  elseif($pathcheck -eq $True){
  [Pscustomobject]@{
  Status       ='Sucess'
  Shortcut     =$Shortcut.name
  Workingpath  =$($check)
  }
  }
  }
  }
  }
  
 $Users = (Get-Content C:\temp\Users.csv) -notmatch '^\s*$'
foreach ($User in $Users) {
  $User=$($User)
  Check-Shortcut -path \\UNCPATHTOUSERPROFILES\$User 
  }

Export

Function Check-Shortcut{
	[CmdletBinding()]
		Param(
			[Parameter(
				Mandatory=$True,
				HelpMessage="Please enter Path of Shortcut, Example c:\folder",
				Position=1
			)][string]$path
		    )
$ErrorActionPreference='Silentlycontinue'
$Shortcuts = Get-ChildItem -Recurse $path -Include *.lnk -Force
$Shell = New-Object -ComObject WScript.Shell
 Foreach ($Shortcut in $Shortcuts)
    {
      $checks=$shell.CreateShortcut($Shortcut).TargetPath   
     
Foreach($check in $checks){
     $pathcheck=Test-Path $($check)
  if($pathcheck -eq $false){
  [Pscustomobject]@{
  Status       ='Failed'
  Shortcut     =$Shortcut.name
  FailedTopath =$($check)
  }
  }
  elseif($pathcheck -eq $True){
  [Pscustomobject]@{
  Status       ='Sucess'
  Shortcut     =$Shortcut.name
  Workingpath  =$($check)
  }
  }
  }
  }
  }
  
 $Users = (Get-Content C:\temp\Users.csv) -notmatch '^\s*$'
$data=foreach ($User in $Users) {
  $User=$($User)
  Check-Shortcut -path \\UNCPATHTOUSERPROFILES\$User 
  }
$data|export-csv c:\results.csv -notype

Seriously thank you guys so much, just crazy how knowledgeable y’all are. i’m sorry for my noob moment of putting the function definitions inside a loop but you guys showed me something today! Thanks Jiten too for the improvements, i’m working on trying those out now

@tulioarends @jitensh

Shared “noob moments” are one of the best way to learn!

Seriously, us crusty old hands (tongue very much in cheek, I’m only 41) love it when people we are teaching or mentoring kludge something up and ask why it doesn’t work. It gives us an opportunity to delve in the whys of things.

1 Spice up