Skip to main content

Hey Guys,



This is one I’ve been thinking about how best to write for a bit. It took some consideration on how to handle the 64-bit scenario because the Automox Agent is a 32-bit process. The ScriptBlock method I used here worked wonders–and can be re-purposed for anything that needs a 64-bit shell.



So all you have to do here is define $appName in both blocks (make sure they match!). If it finds any matches, the Evaluation returns as Non-Compliant (since uninstalled is the desired state). It uses similar detection in the Remediation to find the applications “UninstallString” which can be used to run an uninstall–but adding parameters/arguments for silent execution. Since EXE arguments are non-standard, you may need to customize that bit, but /S works for most of them.



There are a ton of comments inside to make it clear what’s happening, but feel free to ask any questions that might come up.



Evaluation



<#

.SYNOPSIS

Check for presence of specified application on the target device



.DESCRIPTION

Read 32-bit and 64-bit registry to find matching applications



Exits with 0 for compliance, 1 for Non-Compliance.

Non-Compliant devices will run Remediation Code at the Policy's next scheduled date.



.NOTES

A scriptblock is used to workaround the limitations of 32-bit powershell.exe.

This allows us to redirect the operations to a 64-bit powershell.exe and read

the 64-bit registry without .NET workarounds.



.LINK

http://www.automox.com

#>



# The ScriptBlock method used here is to allow a 32-bit agent process

# to access the 64-bit registry on 64-bit Windows. This is necessary if the application

# isn't known to be 32-bit only.





$scriptblock = {

#Define Registry Location for the 64-bit and 32-bit Uninstall keys

$uninstReg = @('HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall','HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall')



# Define the App Name to look for

# Look at a machine with the application installed unless you're sure the formatting of the name/version

# Specifically the DisplayName. This is what you see in Add/Remove Programs. This doesn't have to be exact.

# Default behavior uses -match which is essentially "DisplayName contains VLC"

##################

$appName = 'Steam'

##################



# Get all entries that match our criteria. DisplayName matches $appname

$installed = @(Get-ChildItem $uninstReg -ErrorAction SilentlyContinue | Get-ItemProperty | Where-Object { ($_.DisplayName -match $appName) })



# If any matches were present, $installed will be populated. If none, then $installed is NULL and this IF statement will be false.

# The return value here is what the ScriptBlock will send back to us after we run it.

# 1 for Non-Compliant, 0 for Compliant

if ($installed) {

return 1

} else { return 0 }

}





$exitCode = & "$env:SystemRoot\sysnative\WindowsPowerShell\v1.0\powershell.exe" -ExecutionPolicy Bypass -WindowStyle Hidden -NoProfile -NonInteractive -Command $scriptblock

Exit $exitCode



Remediation



<#

.SYNOPSIS

Uninstall matching applications from the target device.



.DESCRIPTION

Read 32-bit and 64-bit registry to determine the UninstallString for

each matching application. Then use the UninstallString to uninstall

the matching applications.



Exits with 0 for Success, 1 for Failure.



.NOTES

A scriptblock is used to workaround the limitations of 32-bit powershell.exe.

This allows us to redirect the operations to a 64-bit powershell.exe and read

the 64-bit registry without .NET workarounds.



.LINK

http://www.automox.com

#>



# BIG CAVEAT HERE

# If your application uses an EXE instead of "msiexec" in it's

# UninstallString, the SILENT argument isn't standardized.

# It's also possible that a reboot suppressing argument can be important

# You may need to look that up in the vendor's documentation.



# Change this in the ArgumentList below on this line

# $process = Start-Process $uninstString -ArgumentList '/S /NORESTART'



# The current ArgumentList is specific to Steam which uses /S for silent

# If your application doesn't have a standard UninstallString, you can

# define it directly, but that is rare.



$scriptblock = {

#Define Registry Location for Uninstall keys

$uninstReg = @('HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall','HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall')



# Look at a machine with the application installed unless you're sure the formatting of the name/version

##################

$appName = 'Steam'

##################



# Get all entries that match our criteria. DisplayName matches $appname, DisplayVersion less than current

$installed = @(Get-ChildItem $uninstReg -ErrorAction SilentlyContinue | Get-ItemProperty | Where-Object { ($_.DisplayName -match $appName) })



# Initialize an array to store the uninstalled app information

$uninstalled = @()



# Start a loop in-case you get more than one match, uninstall each.

foreach ($version in $installed) {

#For every version found, run the uninstall string

$uninstString = $version.UninstallString

#If exe run as written + silent argument, if msiexec run as msi using the name of the reg key as the msi guid.

if ($uninstString -match 'msiexec') {

$process = Start-Process msiexec.exe -ArgumentList "/x $($version.PSChildName) /qn REBOOT=ReallySuppress" -Wait -PassThru

} else {

$process = Start-Process $uninstString -ArgumentList '/S' -Wait -PassThru

}



# Check exit code for success/fail

# Using 3 "-eq" statements becuase older PowerShell doesn't support "-in"

# If unsuccessful, don't add to uninstalled list.

if ( ($process.ExitCode -eq '0') -or ($process.ExitCode -eq '1641') -or ($process.ExitCode -eq '3010') ) {

$uninstalled += $version.PSPath

}

}



return $uninstalled

}





$uninstalledApps = & "$env:SystemRoot\sysnative\WindowsPowerShell\v1.0\powershell.exe" -ExecutionPolicy Bypass -WindowStyle Hidden -NoProfile -NonInteractive -Command $scriptblock



# Use Write-Output so you can see a result in the Activity Log

Write-Output "$uninstalledApps"



if ($uninstalledApps) {

Exit 0

} else { Exit 1 }



Edited to add success/fail check on the uninstall process.

Thanks for this awesome worklet i was trying to use it to remove zoom so i changed the variables that stated Steam to Zoom but when run i see “COMMAND TIMED OUT.” in the activity log and and see that it fails on the device level.



Any pointers or directions i need to look at?


Do you happen to know if Zoom was running at the time the worklet attempted the uninstall?


I Don’t know to be honest, downside is zoom normally starts with the computer, force closing would be ideal.


You might need to force close Zoom by adding some code to the worklet. I know that our Zoom patching support has to check both to see if Zoom is running and also whether you are in the middle of a meeting. If you are in a meeting then Automox won’t force close Zoom to do the update, since that would interrupt what the end user is doing. The other option would be to remove Zoom from the computer’s autostart section, reboot, then run the worklet as is. Let me know if you need help with writing some code to do any of the above and I can give it a shot.


if you can share to code that would be awesome, force closing would be sufficient for my purpose would be ideal if it would use the existing variable.


Cool - I’ll work on that this coming week and post back when I have something that I can get to work at uninstalling Zoom.


awesome have a great weekend!


Hi there. I have never had any luck with this script actually and have just tried again with short name and long name ‘Meraki’ again with ‘Meraki Systems Manager Agent’.



In the activity log I get COMMAND TIMED OUT.



in the amagent log I get 2021/01/26 09:18:37 cmd_windows.go:135: runScript: Error in Wait exit status 1



I used copied and pasted the script at the top and used it exactly. Windows 10 2004. The Agent was originally installed using an MSI.



Any suggestions? Or has anyone else had this?



Thanks


Hi Djvj,


That usually happens when Automox is not able to find the app you are trying to uninstall. There are a couple causes for this. Just to be sure have you confirmed the name in HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall?



also, is this installed by an MSI or a .exe?


The name is not in that registry location. The app is still viewable in add remove programs, and there is still the meraki agent listed in running services.msc.



It was installed using MSI.




I haven’t had the opportunity to try this, but I am going to make a guess. According to the following Meraki documentation, it is using this registry location rather than the standard placement: HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Meraki



If this is true, it may be useful to build out a Worklet eval and remediation based on the info here…


Testing this out is a good idea, haha 🙂




Hi @rich,



Thank you for this script. I tried using as a worklet and try to uninstall OneDrive (Microsoft OneDrive) but in the report it flagged me this error (See below). Your feedback and advise will be much appreciated.



powershell.exe : This command cannot be run due to the error: The system cannot find the file specified. At C:\ProgramData\amagent\execDir313272263\execcmd382567546.ps1:72 char:20 + … alledApps = & "$env:SystemRoot\sysnative\WindowsPowerShell\v1.0\power … + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (🙂 4Start-Process], InvalidOperationException + FullyQualifiedErrorId : InvalidOperationException,Microsoft.PowerShell.Commands.StartProcessCommand COMMAND TIMED OUT.



Thanks!


U


Hi Nic,



I’ve been able to use the worklet to disable Skype for Business, but I’m trying to remove Skype that is from the App store as well and when I run the script I get Timed Out as well.




I don’t believe we can touch anything that comes from the app store. I know we can’t patch anything that comes from there.


I have encountered the same error message while trying to remove this software known as “Web Companion” by Lavasoft.



Please advise thank you.