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