Files
management-scripts/Uninstall-Hotfix.ps1
2019-11-11 19:22:39 -05:00

129 lines
4.8 KiB
PowerShell

#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...??)