Public/Get-OrphanedPatch.ps1
function Get-OrphanedPatch { <# .SYNOPSIS Scans the "C:\Windows\Installer" directory for all orpaned msp files. .DESCRIPTION Office installations can leave behind a large amount of orphaned patches which can take up many GBs of disk space. Using the "Get-MSIPatchInfo" cmdlet from the "MSI" module we can determine which msp files are currently installed. From there we can calculate the amount and size of the orpaned msp files. This can be run on it's own to list the orphaned patches, or piped to Move-OrphanedPatches or Remove-OrphanedPatches. .EXAMPLE Get-OrphanedPatch Simply lists orphaned msp files in "C:\Windows\Installer" .EXAMPLE Get-OrphanedPatch | Move-OrphanedPatch -Destination <String> If you want to free up space I recommend moving the orphaned msp files to another location so they can easily be restored. Supports the "-Whatif" and "Verbose" paramters. .EXAMPLE Get-OrphanedPatch | Remove-OrphanedPatch This will permanently delete the orphaned msp files from "C:\Windows\OInstaller". Supports the "-Whatif" and "Verbose" paramters. .NOTES Author: Mark Kerry Date: 08/01/2018 #> [CmdletBinding()] [OutputType([System.IO.FileInfo])] Param() # Begin by checking the user in running the function from elevated priviledges if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) { Write-Warning "You need to run this script from an elevated PowerShell prompt!`nPlease start PowerShell as an Administrator..." break } if ($PSVersionTable.PSEdition -eq 'Core') { Write-Warning "This function is not compatible with PowerShell Core as it reiles on the MSI module binaries." break } # List all msp files in %windir%\Installer and convert to string $strAllMsp = Get-ChildItem -Path C:\Windows\Installer\*.msp | Select-Object Name -ExpandProperty Name # List all currently installed msp files and convert to string $strInstalledMsps = Get-MSIPatchInfo | Select-Object LocalPackage -ExpandProperty LocalPackage # Create a new array of all the installed msp files but run a regex query to strip the path from the string so the format is the same as the strAllMsp variable $array = @() foreach ($Msp in $strInstalledMsps) { $a = $Msp -creplace '(?s)^.*\\', '' $array += $a } # Remove any duplicate values from the array $array = $array | Select-Object -uniq $obinst = @() foreach ($x in $strAllMsp) { if (!($array.Contains($x))) { $obinst += $x } } $obinst | ForEach-Object {Get-ChildItem -Path C:\Windows\Installer\$_} -ov item } |