Can KACE somehow inspect the Properties of an application shortcut (.lnk)?
Our company is gearing up a project where we need to replace shortcuts in users' start menus, desktops, etc. with new versions of the shortcuts that point to different locations.
Unfortunately, using a custom inventory rule that tries to find the shortcuts in a series of locations isn't really viable because of the sheer number of shortcuts, as well as the way the users have them scattered and even renamed.
Is there a way for KACE to inspect the Properties sheet of a shortcut to find the Target of the shortcut? Those Targets are fairly unique and point to only a couple of locations on a couple of servers in our Enterprise.
Is there possibly another way entirely to use KACE to find and perhaps replace these shortcuts?
Thanks.
Answers (4)
Are you replacing the shortcut, or just the location that it points to? For the former:
It would be more difficult if it's on the user's desktop as the agent doesn't typically have access to it, but if you put it in All Users or Public, you could create a dummy software item to use a custom inventory rule to see if it's there.
If so, something like this might work as a custom inventory rule to get the string: ShellCommandTextReturn(type location-to\shortcut.lnk|find "\"|findstr/b "[a-z]:[\\]")
To target the ones that need to be replaced, create a smart label that only targets the old link and have your script/managed install deploy to those machines.
If it has to be on the user's desktop, you could use a script that runs as logged in user and have it deploy the shortcut.
In my experience, the easiest way to deploy a shortcut is to zip it up and use a script to unzip it into location in All Users or Public. You could add a step to delete the old one.
If you need to change the location that the link points to, perhaps you can use a kscript to call a vb script like this: http://stackoverflow.com/questions/416957/change-a-shortcuts-target-from-command-prompt
http://www.kace.com/support/resources/kb/article/how-to-call-a-visual-basic-script
Comments:
-
jnox,
The main concern is finding the machines that have all these shortcuts, and using KACE to delete them (if that's even possible). The replacements we'll deploy in a standard location for all machines, probably an All Users location, not specific to the individual.
We need to know what machines need the old shortcuts removed and to completely clean out those old shortcuts. Once we know which machines have the old ones, we'll know which ones need the new ones.
The main problem we have right now is how these shortcuts will be scattered throughout multiple user profiles on each machine, and that they'll often be renamed by the user. They'll all point to a common Target, and also the Start In folders will be unique, so we can use that property as well, if Target isn't viable.
Thanks. - KFox 11 years ago-
It's convoluted, but it's possible. You would have to run a bat or vbs on the client that outputs the target of the shortcut to say a text file or a dummy registry key. You could then use a custom inventory rule to report on that.
That's why I'd suggest using the All Users desktop as you can just make one change in the future. - jknox 11 years ago
I couldn't get it to work in vbscript. Although after doing it in Powershell, I could do it with vbscript if instead of using filesystemobject, I would use cmd to get the file list.
Modify the targetpaths to your target paths, and to actually delete the file you would remove the whatif near the end. Or you could use this to just report on the files found and delete them manually.
# Remove shortcuts if they point to specified TargetPaths.
# Shortcuts that have these targetpaths should be deleted.
$targetPath = @()
$targetPath += "C:\Program Files (x86)\Mozilla Firefox\firefox.exe"
$targetPath += "C:\Program Files\CCleaner\CCleaner.exe"
$targetPath += "C:\Program Files (x86)\ImgBurn\ImgBurn.exe"
# Start folders to search from.
$searchFolders = @("C:\Users\*\Desktop","C:\Documents and Settings\*\Desktop","$env:allusersprofile\Start Menu\Programs")
$shortCuts = ForEach ($searchFolder in $searchFolders) {
Get-ChildItem "$searchFolder" -include *.lnk -Recurse -Force -ErrorAction SilentlyContinue
}
If ($shortCuts.count -eq 0) {Exit}
# Needed to access TargetPath property in the shortcuts.
$WshShell = New-Object -ComObject WScript.Shell
$ShortCuts | ForEach-Object {
# Use undocumented createshortcut method to get Targetpath property.
$link = $WshShell.CreateShortcut($_.FullName)
ForEach ($target in $targetPath) {
If ($link.TargetPath -eq $target) {
# Write-Output $_.FullName
Remove-Item "$_" -Force -WhatIf
}
}
$link = ""
}
I modified the script because it was not searching the user start menus. I made 1 in powershell and another in vbscript.
# Remove shortcuts if they point to specified TargetPaths.
Set-StrictMode -version Latest
# Shortcuts that have these targetpaths should be deleted.
$targetPath = @()
$targetPath += "C:\Program Files (x86)\Mozilla Firefox\firefox.exe"
$targetPath += "C:\Program Files\CCleaner\CCleaner.exe"
$targetPath += "C:\Program Files (x86)\ImgBurn\ImgBurn.exe"
$key = "REGISTRY::HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion"
switch ((Get-ItemProperty -Path $key -Name CurrentVersion).CurrentVersion) {
5.0 {$osVer = "2k"}
5.1 {$osVer = "xp"}
6.0 {$osVer = "vista"}
6.1 {$osVer = "win7"}
default {Exit}
}
# Start folders to search from.
$searchFolders = @()
If (($osVer -eq "2k") -or ($osVer -eq "xp")) {
$searchFolders += "C:\Documents and Settings\*\Desktop"
$searchFolders += "C:\Documents and Settings\*\Start Menu"
} ElseIf (($osVer -eq "vista") -or ($osVer -eq "win7")) {
$searchFolders += "C:\ProgramData\Microsoft\Windows\Start Menu"
$searchFolders += "C:\Users\*\Desktop"
$searchFolders += (Get-ChildItem "C:\Users") | ForEach-Object {"C:\Users\$_.Name\AppData\Roaming\Microsoft\Windows\Start Menu"}
} Else {
Exit
}
$shortCuts = ForEach ($searchFolder in $searchFolders) {
Get-ChildItem "$searchFolder" -include *.lnk -Recurse -Force -ErrorAction SilentlyContinue
}
If ($shortCuts.count -eq 0) {Exit}
# Needed to access TargetPath property in the shortcuts.
$WshShell = New-Object -ComObject WScript.Shell
$ShortCuts | ForEach-Object {
# Use undocumented createshortcut method to get Targetpath property.
$link = $WshShell.CreateShortcut($_.FullName)
ForEach ($target in $targetPath) {
If (($link.TargetPath -ne "") -and ($link.TargetPath -eq $target)) {
Write-Output $_.FullName
# Remove-Item "$_" -Force -WhatIf
}
}
$link = ""
}
' Remove shortcuts if they point to specified TargetPaths.
Option Explicit
On Error Resume Next
Dim WshShell
Set WshShell = CreateObject("WScript.Shell")
' Shortcuts that have these targetpaths should be deleted.
Dim TargetPath(3)
TargetPath(0) = "C:\Program Files (x86)\Mozilla Firefox\firefox.exe"
TargetPath(1) = "C:\Program Files\CCleaner\CCleaner.exe"
TargetPath(2) = "C:\Program Files (x86)\ImgBurn\ImgBurn.exe"
Dim osVer, searchFolders
searchFolders = Array
GetOS()
Dim oExec, arrLines, folder
If (osVer = "2k" or osVer = "xp") Then
redim preserve searchFolders(2)
searchFolders(0) = "C:\Documents and Settings\*\Desktop"
searchFolders(1) = "C:\Documents and Settings\*\Start Menu"
ElseIf (osVer = "vista" or osVer = "win7") Then
redim preserve searchFolders(1)
searchFolders(0) = "C:\ProgramData\Microsoft\Windows\Start Menu"
Set oExec = WshShell.Exec("cmd /q /c dir /b C:\Users")
While Not (oExec.StdOut.AtEndOfStream)
folder = Trim(oExec.StdOut.ReadLine())
redim preserve searchFolders(UBound(searchFolders) + 2)
searchFolders(UBound(searchFolders) - 2) = "C:\Users\" & folder & "\Desktop"
searchFolders(UBound(searchFolders) - 1) = "C:\Users\" & folder & "\AppData\Roaming\Microsoft\Windows\Start Menu"
Wend
Set oExec = Nothing
Else
WScript.Quit
End If
Dim count
arrLines = Array
For count = 0 to UBound(SearchFolders)
Set oExec = WshShell.Exec("cmd /q /c dir /a /b /s " & Chr(34) & SearchFolders(count) & "\*.lnk" & Chr(34))
While Not (oExec.StdOut.AtEndOfStream)
redim preserve arrLines(UBound(arrLines) + 1)
arrLines(UBound(arrLines) - 1) = Trim(oExec.StdOut.ReadLine())
Wend
Set oExec = Nothing
Next
Dim x
for x = 0 to UBound(arrLines)
CheckShortCut arrLines(x)
Next
WScript.Quit
Sub CheckShortCut(FileToCheck)
Dim link, i
Set link = WshShell.CreateShortcut(FileToCheck)
For i = 0 to UBound(TargetPath)
If link.TargetPath <> "" And link.TargetPath = TargetPath(i) Then
WScript.Echo FileToCheck
'WshShell.Run ("cmd /q /c del /q /f Chr(34) & FileToCheck & Chr(34), 0, true)
End If
Next
Set link = nothing
End Sub
Function GetOS()
Dim osFound
osFound = WshShell.RegRead("HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\CurrentVersion")
Select Case osFound
Case "5.0" osVer = "2k"
Case "5.1" osVer = "xp"
Case "6.0" osVer = "vista"
Case "6.1" osVer = "win7"
Case Else WScript.Quit
End Select
End Function
flip,
One of our company's application developers came up with a quick and dirty PowerShell script to find the .lnks we're looking for. I'll let him know about the scripts you posted here as he may find those very useful. Here's his script:
foreach ($t in (Get-ChildItem -Path "C:\Documents And Settings\" -Filter *.lnk -Recurse))
{
#echo $t.FullName
$shell = New-Object -COM WScript.Shell
$shortcut = $shell.CreateShortcut($t.FullName)
#echo $shortcut.TargetPath.ToUpper()
if (($shortcut.TargetPath.ToUpper().StartsWith("\\server01\directory".ToUpper())) -or ($shortcut.TargetPath.ToUpper().StartsWith("\\server02\directory01".ToUpper())) -or ($shortcut.TargetPath.ToUpper().StartsWith("\\server02\directory02".ToUpper())))
{
$shortcut.Arguments
}
}
I'm having another issue, unrelated to PowerShell. I created a Custom Inventory Rule in the K1000 to find PCs that have the apps the shortcuts point to installed. Since the apps do not write to the registry at all, I have to search for a particular directory that's unique to the apps. But the CIR isn't working and I'm not sure why. It's just one simple command:
DirectoryExists(C:\directory\subdirectory)
I let it run overnight last night but got zero results even though lots of machines that have these directories checked in. Any thoughts? Something wrong with my syntax? Do I need another line that gives an output?
Thanks.
Comments:
-
Well that code is much leaner than mine lol...
For the CIR, as long as you typed it in the CIR field, and not the notes field, and also selected the appropriate OS's, it should work. In the admin guide it shows in the format DirectoryExists(C:\Windows\), with a trailing backslash. - flip1001 11 years ago-
Well, I found out what was wrong and it had nothing to do with my CIR. It seems that in 5.4 SP1 if a CIR fails on a client with a syntax errors, all CIRs that run after that one will fail with a syntax error, whether or not there actually is a syntax error.
I had some CIRs for finding IE10 and IE9 installs, but they don't work, and that was causing my new CIR to fail as well. Once I deleted those CIRs, the new one started working. - KFox 11 years ago -
Also, our application developer said he liked a lot of things in your code and they'd be useful when it comes time to actually deleting the .lnks. Right now his simple code just finds them but doesn't really do anything with them. It was more of a test as he's learning to use PowerShell (and really likes it).
I suppose I should start to learn it as well. Heh. - KFox 11 years ago