It’s December 2nd and Christmas is just around the corner! Yesterday Jakob kicked off the Coretech December Calendar with a great post about “Triggering a webhook from a SharePoint workflow using Out-of-the-box Activities” (Check out his blog post here: http://bit.ly/1N16fte). That was the first, this is the second post in the Coretech blog series that will continue until December 24th – Christmas Eve! 🙂
In Part 1 (http://bit.ly/1PlnDPr) I explained how you can utilize ConfigMgr Compliance Baselines for uninstalling software like Java, Adobe etc. with the help of PowerShell and WMI. Well in Part 1 we used the Win32_Product class which is not recommended by many as it runs a consistency check against each installation and this can cause real performance issues and in worst case system failure.
Quote from Greg Ramsey blog: http://gregramsey.net/2012/02/20/win32_product-is-evil/
“
Win32_Product will return some great information about each windows installer-based application. In fact, you can even view additional properties by running get-wmiobject win32_product | select * . However, even though you called a basic query of the Win32_Product class, you actually performed a consistency check of each installation.
“
The solution:
So how can we create a workaround for this. Well we can use basic PowerShell scripting and utilize a different class like SMS_InstalledSoftware which we are going to use in this example. There is just one catch, and that is that SMS_InstalledSoftware do not have a Uninstall Method like Win32_Product has (https://msdn.microsoft.com/en-us/library/cc144824.aspx). So we need a different method for the actual uninstallation part.
The SMS_InstalledSoftware Class belongs to the Asset Intelligence features in ConfigMgr and is not enabled in Windows by default. You need to 1. have the ConfigMgr Agent installed and 2. enable the “Installed Software – Asset Intelligence (SMS_InstalledSoftware)” class under Hardware Inventory and Classes on your Client Setting Policy. Like this:
In your ConfigMgr Console, go to your Client Setting Policy, then Hardware Inventory and choose Set Classes.
Enable Installed Software – Asset Intelligence (SMS_InstalledSoftware)
Now that is done we can create a Compliance Settings Item and a Compliance Baseline like in Part 1 (http://bit.ly/1PlnDPr), but instead of utilizing WIN32_Product, let us use SMS_InstalledSoftware instead. Just change the Detection Script and Remediation script to the examples below and you should be good to go.
Detection Script:
#----------------- #Define Variables #----------------- $Software = "Java" #----------------- #Search software #----------------- Get-WmiObject -class SMS_InstalledSoftware -Namespace "root\cimv2\sms" | Where-Object {$PSItem.ProductName -like "*$Software*"}
Remediation Script:
<# .NOTES =========================================================================== Created on: 26.11.2015 Created by: Marius A. Skovli Organization: Coretech Filename: UninstallAppsWithWMI.ps1 =========================================================================== .DESCRIPTION This Script will search for the software defined as ProductName in the SMS_InstalledSoftware WMI Class store it in a variable ($Product) and uninstall the software. In this Example Java has been Used. #> #----------------- #Define Variables #----------------- $Software = "Java" #----------------- #Search software #----------------- $Product = Get-WmiObject -class SMS_InstalledSoftware -Namespace "root\cimv2\sms" | Where-Object {$PSItem.ProductName -like "*$Software*"} #----------------- #Uninstall software #----------------- ForEach ($ObjItem in $Product) { #----------------- #Define Variables #----------------- $ID = $ObjItem.SoftwareCode $SoftwareName = $ObjItem.ProductName #----------------- #Uninstall #----------------- $Uninstall = "/x" + "$ID /qn" $SP = (Start-Process -FilePath "msiexec.exe" $Uninstall -Wait -Passthru).ExitCode Write-Output "Uninstalled $SoftwareName" } Write-Output "Done!"
The remediation script will search for the software defined as ProductName in the SMS_InstalledSoftware WMI Class store it in a variable ($Product) and uninstall the software using basic msiexec.exe /x command. If you have multiple Java versions installed it wil run the uninstallation command ForEach java version it finds.
The result when ran from PowerShell:
This is a far better method than using Win32_Product. A a big shout-out goes to Kaido Järvemets for helping me on this little project.
Don’t hesitate to comment below if you have any questions or additional thoughts you want to add.
Have a great December – a Merry Christmas and Happy New Year to you all!
Wow, are we able to target multiple specific versions using this. E.G. java 7 update 11 and java 6 update 85.
Yes, you can! Change this part $Software = “Java” to $Software = “Java 7 update 11”. And it will only uninstall that particular version. If you want to uninstall 2, 3 or more versions just create multiple Compliance Items and add them to the same Baseline! 🙂
This is great. In my experience with Java in particular though, it typically requires a little more effort to successfully remove in an enterprise environment. I personally like to kill any running Java processes first which can prevent the uninstallation:
# Kill Java processes
try
{ Get-Process | Select ProcessName,Description | Where {$_.Description -like ‘*Java*’} | Stop-Process -Force -Verbose -ErrorAction Stop}
catch
{ $_.Exception.Message }
Then even kill running browsers:
# kill Browser processes
$Browsers = “iexplore”,”firefox”,”chrome”
foreach ($browser in $browsers)
{
try
{ Get-Process -Name $browser | Stop-Process -Force -Verbose -ErrorAction Stop}
catch
{ $_.Exception.Message }
}
Finally to uninstall, I like to search the registry uninstall key, provide any exclusions with a “where” filter, then uninstall as a job with a loop which watches for and kills any popups like the one described in this Java bug: https://bugs.openjdk.java.net/browse/JDK-8028779
# Search for and uninstall old Java versions
$RegLocations = “HKLM:SOFTWAREMicrosoftWindowsCurrentVersionUninstall”,”HKLM:SOFTWAREWow6432NodeMicrosoftWindowsCurrentVersionUninstall”
foreach ($location in $RegLocations)
{
if (test-path $location)
{
$InstalledJava = @(Get-ChildItem “$location” -Recurse | ForEach-Object { Get-ItemProperty $_.pspath } |
# Search only for Java RE by excluding any development kits or the auto updater
Where-Object {$_.DisplayName -match “Java ” -and $_.DisplayName -notmatch “Development” -and $_.DisplayName -notmatch “Auto”} | Select DisplayName,DisplayVersion,PSChildName)
write-host “Found the following Java versions installed:”
foreach ($java in $InstalledJava)
{ $java.DisplayName }
foreach ($java in $InstalledJava)
{
$MSICode = $java.PSChildName
try
{
write-host “Uninstalling “$java.Displayname
Start-Job -Name $java.DisplayName -ArgumentList $MSICode -ScriptBlock `
{
param($MSICode)
Start-Process -FilePath msiexec.exe -ArgumentList “/x $MSICode /qn REBOOT=ReallySuppress” -Wait -NoNewWindow
}
Wait-job -Name $java.DisplayName -Timeout 20 | Out-null
$Timeout = 0
while (1)
{
if ((Get-Job -name $java.DisplayName).State -eq ‘Completed’)
{ break }
Else
{
Get-Process rundll32 -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue
Start-Sleep -Seconds 5
$Timeout += 5
if ($timeout -ge 20)
{
write-host “Gave up waiting for rundll32.exe to exit”
break
}
}
Wait-Job -Name $java.DisplayName -Timeout 5 | out-null
}
Remove-Job -Name $java.DisplayName
}
catch
{ $_.Exception.Message }
}
}
}
Finally, I like to clean up some environment variables, directories and registry keys that Java can leave behind:
# Remove some registry keys and directories that can cause install problems
$JavaLocations = @(
“$env:ProgramFilesJava”,
“${env:ProgramFiles(x86)}Java”,
“$env:ProgramDataMicrosoftWindowsStart MenuProgramsJava”,
“HKLM:SOFTWAREJavaSoftJava Runtime Environment”,
“HKLM:SOFTWAREWow6432NodeJavaSoftJava Runtime Environment”)
foreach ($Location in $JavaLocations)
{
if (test-path $location)
{
try
{
Remove-Item $location -Recurse -Force
}
catch
{ $_.Exception.Message }
}
}
# Remove some more reg keys in the HKCR and HKLM
New-PSDrive -PSProvider registry -Name HKCR -Root HKEY_CLASSES_ROOT | Out-Null
try
{
Get-ChildItem “HKCR:InstallerProducts” | Where-Object {$_.PSChildName -like “4EA42A62D9304AC4784BF*”} | Select PSPath | Remove-Item -Recurse -Force
Get-ChildItem “HKLM:SOFTWAREWow6432NodeMicrosoftWindowsCurrentVersionUninstall” | Where-Object {$_.PSChildName -like “{26A24AE4-*”} | Select PSPath | Remove-Item -Recurse -Force
Get-ChildItem “HKLM:SOFTWAREMicrosoftWindowsCurrentVersionUninstall” | Where-Object {$_.PSChildName -like “{26A24AE4-*”} | Select PSPath | Remove-Item -Recurse -Force
}
catch
{ $_.Exception.Message }
Full script is included in my book for managing Java with SCCM 🙂
http://www.amazon.com/Runtime-Environment-Enterprise-Configuration-PowerShell/dp/1514106019/ref=tmm_pap_swatch_0?_encoding=UTF8&sr=8-1&qid=1436977634
Great writeup. I am trying to incorporate this at my company in order to remove QuickTime since it is no longer supported by Apple and with the latest CVE that is out, its best to remove it entirely from the environment.
I have all the components configured as stated in the blog, but no matter what, my test systems always return a Compliant if quicktime is installed. Also, the script doesn’t seem to uninstall quicktime correctly. I’ve tried running the remediation script on its own, but if fails in finding quicktime installed. Any tips would be appreciated.
Nevermind – had an older version of Management Framework on my test systems! Upgraded to WMF 3.0 and it worked.
Hi Mario, I am glad i figured it out. Let me know if there is anything else! 🙂
How would one build in a exclusion for a certain version? I would love to run this against all endpoints but if it detects a certain version it should not uninstall anything.