Find windows registry uninstall keys with PowerShell

The function Find-RegistryUninstallKey is PowerShell logic that searches the Windows registry for uninstall keys that match a specified string. It can be useful for troubleshooting and removing software that may be causing issues on a Windows machine.

The function takes two parameters: $SearchFor, which is the string to search for in the registry, and $Wow6432Node, which is an optional switch that indicates whether to search for 32-bit or 64-bit applications.

If the $Wow6432Node switch is set to true, the function performs a second search for uninstall keys under HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall, which is where 32-bit applications are registered in the registry.

Add the function to your PowerShell profile (with VS Code):

code $profile
Open a PowerShell window and run the above command. This will open or create your user's PowerShell profile.

Paste the following code into your profile:

function Find-RegistryUninstallKey {
    param($SearchFor, [switch]$Wow6432Node)
    $results = @()
    $keys = Get-ChildItem HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall |
    ForEach-Object {
        class x64 {
            [string]$GUID
            [string]$Publisher
            [string]$DisplayName
            [string]$DisplayVersion
            [string]$InstallLocation
            [string]$InstallDate
            [string]$UninstallString
            [string]$Wow6432Node
        }
        $x64 = [x64]::new()
        $x64.GUID = $_.pschildname
        $x64.Publisher = $_.GetValue('Publisher')
        $x64.DisplayName = $_.GetValue('DisplayName')
        $x64.DisplayVersion = $_.GetValue('DisplayVersion')
        $x64.InstallLocation = $_.GetValue('InstallLocation')
        $x64.InstallDate = $_.GetValue('InstallDate')
        $x64.UninstallString = $_.GetValue('UninstallString')
        if ($Wow6432Node) { $x64.Wow6432Node = 'No' }
        $results += $x64
    }
    if ($Wow6432Node) {
        $keys = Get-ChildItem HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall |
        ForEach-Object {
            class x86 {
                [string]$GUID
                [string]$Publisher
                [string]$DisplayName
                [string]$DisplayVersion
                [string]$InstallLocation
                [string]$InstallDate
                [string]$UninstallString
                [string]$Wow6432Node
            }
            $x86 = [x86]::new()
            $x86.GUID = $_.pschildname
            $x86.Publisher = $_.GetValue('Publisher')
            $x86.DisplayName = $_.GetValue('DisplayName')
            $x86.DisplayVersion = $_.GetValue('DisplayVersion')
            $x86.InstallLocation = $_.GetValue('InstallLocation')
            $x86.InstallDate = $_.GetValue('InstallDate')
            $x86.UninstallString = $_.GetValue('UninstallString')
            $x86.Wow6432Node = 'Yes'
            $results += $x86
        }
    }
    $results | Sort-Object DisplayName | Where-Object { $_.DisplayName -match $SearchFor }
}
Entire Function

Now open a new PowerShell session and give it a try.

This can be leveraged further by incorporating into scripts to for scenarios like:

  • Scripted detection logic for Configuration Manager deployed applications.
  • Detection logic for apps deployed via Intune.
  • Uninstall scripts.

Detect an application installed by version

Here we search for "notepad" and filter by display version to return a result only if the version matches:

$installed = Find-RegistryUninstallKey -SearchFor "notepad" -Wow6432Node | Where-Object { $_.DisplayVersion -eq "8.5.2" } 

if ($installed){
    return "installed"
}

Full detection.ps1

function Find-RegistryUninstallKey {
    param($SearchFor, [switch]$Wow6432Node)
    $results = @()
    $keys = Get-ChildItem HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall |
    ForEach-Object {
        class x64 {
            [string]$GUID
            [string]$Publisher
            [string]$DisplayName
            [string]$DisplayVersion
            [string]$InstallLocation
            [string]$InstallDate
            [string]$UninstallString
            [string]$Wow6432Node
        }
        $x64 = [x64]::new()
        $x64.GUID = $_.pschildname
        $x64.Publisher = $_.GetValue('Publisher')
        $x64.DisplayName = $_.GetValue('DisplayName')
        $x64.DisplayVersion = $_.GetValue('DisplayVersion')
        $x64.InstallLocation = $_.GetValue('InstallLocation')
        $x64.InstallDate = $_.GetValue('InstallDate')
        $x64.UninstallString = $_.GetValue('UninstallString')
        if ($Wow6432Node) { $x64.Wow6432Node = 'No' }
        $results += $x64
    }
    if ($Wow6432Node) {
        $keys = Get-ChildItem HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall |
        ForEach-Object {
            class x86 {
                [string]$GUID
                [string]$Publisher
                [string]$DisplayName
                [string]$DisplayVersion
                [string]$InstallLocation
                [string]$InstallDate
                [string]$UninstallString
                [string]$Wow6432Node
            }
            $x86 = [x86]::new()
            $x86.GUID = $_.pschildname
            $x86.Publisher = $_.GetValue('Publisher')
            $x86.DisplayName = $_.GetValue('DisplayName')
            $x86.DisplayVersion = $_.GetValue('DisplayVersion')
            $x86.InstallLocation = $_.GetValue('InstallLocation')
            $x86.InstallDate = $_.GetValue('InstallDate')
            $x86.UninstallString = $_.GetValue('UninstallString')
            $x86.Wow6432Node = 'Yes'
            $results += $x86
        }
    }
    $results | Sort-Object DisplayName | Where-Object { $_.DisplayName -match $SearchFor }
}

$installed = Find-RegistryUninstallKey -SearchFor "notepad" -Wow6432Node | Where-Object { $_.DisplayVersion -eq "8.5.2" } 

if ($installed){
    return "installed"
}
detection.ps1

Uninstall an application by searching for uninstall string

Here we search for "notepad" filtering for the specific version we want to target and return the Uninstall String as a string object assigned to a variable

$UninstallString = Find-RegistryUninstallKey -SearchFor "notepad" -Wow6432Node | Where-Object { $_.DisplayVersion -eq "8.5.2" }| Select-Object -ExpandProperty UninstallString | Out-String

Next we use the returned string to construct an uninstall command that we can run via script.

# Remove any trailing whitespaces
$UninstallString = $UninstallString.trim() 

# Run uninstaller with silent flag
Start-Process $UninstallString -ArgumentList "/S" -Wait 

Full uninstall.ps1

function Find-RegistryUninstallKey {
    param($SearchFor, [switch]$Wow6432Node)
    $results = @()
    $keys = Get-ChildItem HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall |
    ForEach-Object {
        class x64 {
            [string]$GUID
            [string]$Publisher
            [string]$DisplayName
            [string]$DisplayVersion
            [string]$InstallLocation
            [string]$InstallDate
            [string]$UninstallString
            [string]$Wow6432Node
        }
        $x64 = [x64]::new()
        $x64.GUID = $_.pschildname
        $x64.Publisher = $_.GetValue('Publisher')
        $x64.DisplayName = $_.GetValue('DisplayName')
        $x64.DisplayVersion = $_.GetValue('DisplayVersion')
        $x64.InstallLocation = $_.GetValue('InstallLocation')
        $x64.InstallDate = $_.GetValue('InstallDate')
        $x64.UninstallString = $_.GetValue('UninstallString')
        if ($Wow6432Node) { $x64.Wow6432Node = 'No' }
        $results += $x64
    }
    if ($Wow6432Node) {
        $keys = Get-ChildItem HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall |
        ForEach-Object {
            class x86 {
                [string]$GUID
                [string]$Publisher
                [string]$DisplayName
                [string]$DisplayVersion
                [string]$InstallLocation
                [string]$InstallDate
                [string]$UninstallString
                [string]$Wow6432Node
            }
            $x86 = [x86]::new()
            $x86.GUID = $_.pschildname
            $x86.Publisher = $_.GetValue('Publisher')
            $x86.DisplayName = $_.GetValue('DisplayName')
            $x86.DisplayVersion = $_.GetValue('DisplayVersion')
            $x86.InstallLocation = $_.GetValue('InstallLocation')
            $x86.InstallDate = $_.GetValue('InstallDate')
            $x86.UninstallString = $_.GetValue('UninstallString')
            $x86.Wow6432Node = 'Yes'
            $results += $x86
        }
    }
    $results | Sort-Object DisplayName | Where-Object { $_.DisplayName -match $SearchFor }
}

# Find installed application by version
$UninstallString = Find-RegistryUninstallKey -SearchFor "notepad" -Wow6432Node | Where-Object { $_.DisplayVersion -eq "8.5.2" } | Select-Object -ExpandProperty UninstallString | Out-String

# Remove any trailing whitespaces
$UninstallString = $UninstallString.trim() 

# Run uninstaller with silent flag
Start-Process $UninstallString -ArgumentList "/S" -Wait 

Ultimately, the convenience of the function is the ability to customize it as needed.

Hopefully this helps you out.

> Jorgeasaurus

Subscribe to > Jorgeasaurus

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
jamie@example.com
Subscribe