/Unregister-MissingMSIs.ps1 Secret
Last active
December 25, 2023 18:22
-
-
Save BrentHumphreys/111ac670e577a94bf1531cc4d9a9d8a0 to your computer and use it in GitHub Desktop.
Removes Windows Installer product registration for products where the locally cached MSI is missing and no registered source can be found.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#Requires -Version 3 | |
# The MIT License (MIT) | |
# Copyright (C) Microsoft Corporation. All rights reserved. | |
# | |
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | |
# | |
# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | |
# | |
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
[CmdletBinding(SupportsShouldProcess = $true)] | |
param () | |
$ErrorActionPreference = 'Stop' | |
[int[]] $translation = 7,6,5,4,3,2,1,0,11,10,9,8,15,14,13,12,17,16,19,18,21,20,23,22,25,24,27,26,29,28,31,30 | |
$loc = data { | |
ConvertFrom-StringData @' | |
Error_Elevation_Required = You must run this script in an elevated command prompt | |
Error_64Bit_Required = You must run this in a 64-bit command prompt | |
Error_PackageManagement_Required = Please install PackageManagement from http://go.microsoft.com/fwlink/?LinkID=746217 | |
Process_Remove_Args1 = Remove registration for {0} | |
Verbose_Install_MSI = Installing the "MSI" module | |
Verbose_Remove_Key_Args1 = Removing registry key {0} | |
Verbose_Remove_Value_Args2 = Removing registry value {1} from {0} | |
Verbose_Remove_Source_Reg = Removing source registration | |
Verbose_Remove_Product_Reg = Removing product registration | |
Verbose_Remove_Upgrade_Reg = Removing upgrade registration | |
Verbose_Remove_Component_Reg = Removing component registration | |
Verbose_Found_Source_Args2 = Cache missing for {0} but found source at {1} | |
'@ | |
} | |
$identity = [System.Security.Principal.WindowsIdentity]::GetCurrent() | |
$principal = New-Object System.Security.Principal.WindowsPrincipal $identity | |
if (!$principal.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)) { | |
throw $loc.Error_Elevation_Required | |
} | |
if ([System.Environment]::Is64BitOperatingSystem) { | |
if (![System.Environment]::Is64BitProcess) { | |
throw $loc.Error_64Bit_Required | |
} | |
} | |
$pack = { | |
param ( | |
[string] $Guid | |
) | |
if (!$Guid) { | |
return | |
} | |
$Guid = (New-Object System.Guid $Guid).ToString("N").ToUpperInvariant() | |
$sb = New-Object System.Text.StringBuilder $translation.Count | |
foreach ($i in $translation) { | |
$null = $sb.Append($Guid[$i]) | |
} | |
$sb.ToString() | |
} | |
$test = { | |
param ( | |
$Product | |
) | |
if ($Product | Test-Path -ErrorAction continue) { | |
return $true | |
} | |
if ($Product.PackageName) { | |
$Product | Get-MSISource | ForEach-Object { | |
$path = Join-Path $_.Path $Product.PackageName | |
if ($path | Test-Path) { | |
Write-Verbose ($loc.Verbose_Found_Source_Args2 -f $Product.ProductCode, $path) | |
return $true | |
} | |
} | |
} | |
$false | |
} | |
$remove = { | |
param ( | |
[string] $Key | |
) | |
if (Test-Path $Key) { | |
Write-Verbose ($loc.Verbose_Remove_Key_Args1 -f $Key) | |
Remove-Item -Recurse -Force -UseTransaction $Key | |
} | |
} | |
$removeChild = { | |
param ( | |
[string] $Key, | |
[string] $Name | |
) | |
if (Test-Path $Key) { | |
Get-ChildItem $Key | ForEach-Object { | |
$obj = $_ | Get-ItemProperty | |
if ($obj.$Name -ne $null) { | |
Write-Verbose ($loc.Verbose_Remove_Value_Args2 -f $_.Name, $Name) | |
Remove-ItemProperty -Force -UseTransaction -Name $Name -LiteralPath $_.PSPath | |
$obj = Get-ItemProperty -UseTransaction -LiteralPath $_.PSPath | |
if (!$obj) { | |
Write-Verbose ($loc.Verbose_Remove_Key_Args1 -f $_.Name) | |
Remove-Item -Recurse -Force -UseTransaction -LiteralPath $_.PSPath | |
} | |
} | |
} | |
} | |
} | |
# Install the MSI module if missing. | |
if (!(Get-Module -ListAvailable MSI)) { | |
Write-Verbose $loc.Verbose_Install_MSI | |
# Make sure PackageManagement is installed (comes with WMF 5.0 / Windows 10). | |
if (!(Get-Module -ListAvailable PackageManagement)) { | |
throw $loc.Error_PackageManagement_Required | |
} | |
Install-Module MSI -Scope CurrentUser -SkipPublisherCheck -Force | |
} | |
foreach ($msi in (Get-MSIProductInfo -UserContext Machine)) { | |
if (!(&$test $msi)) { | |
if ($PSCmdlet.ShouldProcess($msi, $loc.Process_Remove_Args1 -f $msi.ProductCode)) { | |
$packedProductCode = &$pack $msi.ProductCode | |
Start-Transaction | |
Write-Verbose $loc.Verbose_Remove_Source_Reg | |
&$remove "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Installer\Products\$packedProductCode" | |
&$remove "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Installer\Features\$packedProductCode" | |
Write-Verbose $loc.Verbose_Remove_Product_Reg | |
&$remove "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\$packedProductCode" | |
&$remove "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$($msi.ProductCode)" | |
&$remove "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\$($msi.ProductCode)" | |
Write-Verbose $loc.Verbose_Remove_Upgrade_Reg | |
&$removeChild "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UpgradeCodes" $packedProductCode | |
Write-Verbose $loc.Verbose_Remove_Component_Reg | |
&$removeChild "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components" $packedProductCode | |
Complete-Transaction | |
} | |
} | |
} |
The problem is because of corrupt MSI registration. I'll fix the original script to skip these.
The 'Get-MSIProductInfo' command was found in the module 'MSI', but the module could not be loaded. For more
information, run 'Import-Module MSI'.
+ CategoryInfo : ObjectNotFound: (Get-MSIProductInfo:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CouldNotAutoloadMatchingModule
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Tried using this script. Getting an error though: Join-Path : Cannot find drive. A drive with the name 'm' does not exist.
At C:\Unregister-MissingMSIs.ps1:77 char:21
Help would be appreciated.