#Requires -Version 2.0 param( [Parameter(Mandatory=$true)] [array]$KB ) ## Set the client ID $ClientID = "NinjaRMM Community Hotfix Uninstaller" ## Create a new Update Session and set the ClientApplicationID $UpdateSession = New-Object -ComObject Microsoft.Update.Session $UpdateSession.ClientApplicationID = $ClientID ## Create the a searcher $UpdateSearcher = $UpdateSession.CreateUpdateSearcher() ## Search all historical updates ## This method seems to list all updates that are not recent. ## Not sure yet what qualifies as recent. $HistoricalResults = $UpdateSearcher.QueryHistory(0,$UpdateSearcher.GetTotalHistoryCount()) ## Search all current/pending updates ## This method lists all updates that were either recently installed or are pending installation, ## as well as recent updates that failed. Again, not sure what qualifies as recent. $CurrentResults = $UpdateSearcher.Search("IsInstalled=1") ## Make an empty array to put our own update objects into $Updates = @() ## Get all successfully installed historical updates and add them to $Updates Foreach ( $update in $HistoricalResults ) { ## An Operation code of 1 means the update is installed ## A result code of 2 means the installation was successful and without errors If ( ($update.Operation -eq 1) -and ($update.ResultCode -eq 2) ) { ## Create a new object to hold some properties about the update $u = New-Object -Type PSObject -Property @{ 'Title' = $update.Title 'Description' = $update.Description 'SupportUrl' = $update.SupportUrl 'UpdateId' = $update.UpdateIdentity.UpdateID 'RevisionNumber' = $update.UpdateIdentity.RevisionNumber } ## Add our custom object to $Updates. $Updates += $u } } ## Get all installed current updates For ( $i=13; $i -le ($CurrentResults.Updates.Count - 1); $i++ ) { $update = $CurrentResults.Updates.Item($i) ## This method of searching has a unique property called IsUninstallable. ## I'm not sure exactly how this is used by Windows, but we'll check for it here ## as it may make the uninstall process less error-prone. If ( $update.IsUninstallable -eq 1 ) { ## Create a new object to hold some properties about the update $u = New-Object -Type PSObject -Property @{ 'Title' = $update.Title 'Description' = $update.Description 'SupportUrl' = $update.SupportUrl 'UpdateId' = $update.Identity.UpdateID 'RevisionNumber' = $update.Identity.RevisionNumber } ## Add our custom object to $Updates. $Updates += $u } } ## Uninstall by KB number using WUSA.exe Foreach ( $kbTxt in $KB ) { ## Make sure the KB number we're using is in uppercase so it matches correctly $kbTxt = $kbTxt.ToUpper() ## This will remove the "KB" prefix that may have been added. ## When we invoke wusa.exe we need to specify the KB number without "KB" in front. $kbNum = $kbTxt -replace 'KB', '' ## Uninstall the hotfix ## Note: This exact method to uninstall a hotfix doesn't work in Windows 10 ## If you omit the /quiet switch, it will *say* it uninstalled the update, but it never gets uninstalled. Foreach ( $update in $Updates ) { If ( $update.Title -like "*${kbTxt}*" ) { ## Reverse the comment on the following 2 lines during testing. #Start-Process "wusa.exe" -ArgumentList "/uninstall /kb:${kbNum} /quiet /norestart" -Wait } Write-Output $update.Title } } } ## Query DISM for any packages it has installed to see if our hotfix is there ## This method is probably more for Windows 10 endpoints, ## but we'll target Vista and up just to cover the bases. If ( ([System.Environment]::OSVersion.Version).Major -ge 6 ) { Foreach ( $kbTxt in $KB ) { ## Make sure the KB number we're using is in uppercase so it matches correctly $kbTxt = $kbTxt.ToUpper() ## We're using DISM.exe directly to maintain compatibility with older PowerShell versions ## that don't have the newer DISM cmdlets. $DISMPackages = ( DISM.exe /Online /Get-Packages | findstr "${kbTxt}" ) ## Cleanup the packages, if any are found. If ( $DISMPackages ) { $DISMPackages.Replace('Package Identity : ','').Trim() } ## Uninstall each package. Foreach ( $package in $DISMPackages ) { ## Reverse the comment on the following 2 lines during testing. #Start-Process "DISM.exe" -ArgumentList "/Online /Remove-Package /PackageName:${package} /quiet /norestart" -Wait Write-Host $package } } } ## TODO: ## Search for all manually installed hotfixes/updates ## I guess a good place to start is MSI, MSU, and CAB packages installed by a user. ## (possibly by searching for MSI property strings of installed packages...??)