Script to List All Things in "Add/Remove Programs"
This article provides a PowerShell script that lists the applications shown in Add or Remove Programs on a Windows device, by reading the same registry locations Windows itself uses. This is the recommended way to reconcile what is installed on a device against what Automox reports.
Script
$paths = @(
'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*',
'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*',
'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*'
)
Get-ItemProperty -Path $paths -ErrorAction SilentlyContinue |
Where-Object { $_.DisplayName } |
Select-Object DisplayName, DisplayVersion, Publisher, InstallLocation |
Sort-Object DisplayName |
Format-Table -AutoSizeExplanation
- The three registry paths are where Windows records installed applications. The first holds 64-bit applications, the second holds 32-bit applications on 64-bit Windows, and the third holds applications installed for the current user only.
- Get-ItemProperty reads the values under each uninstall key.
- Where-Object { $_.DisplayName } filters out keys with no display name, which are not shown as applications.
- Sort-Object and Format-Table present the results alphabetically in a readable table.
Why this method rather than Win32_Product
A common approach to this task is to query the Win32_Product WMI class. Avoid it. Querying that class causes Windows Installer to enumerate every installed MSI package and run a consistency check against each one, which can verify and repair those installations as a side effect. On a device with many applications this is slow, it writes Windows Installer reconfiguration entries into the Application event log, and it has been associated with slow startup and logon.
It is also incomplete and inconsistent for this purpose:
- It only returns applications installed by Windows Installer, so anything installed by another method is missing, even though Add or Remove Programs displays it.
- The version it reports can differ from the version shown in Add or Remove Programs and in the Automox console, because it reads the MSI product version rather than the registry display version.
If you are comparing a device against the Automox console, the registry method above is the one that will reconcile.
How Automox reads installed software
Automox builds its software inventory by reading the uninstall keys in the registry and taking the display name and version from them, checking both the 64-bit and 32-bit locations, and checking user space for certain applications. Because the script above reads the same locations, its output should line up with what the console shows after the next scan.
If an application still appears in Automox after being uninstalled, that usually means remnants of the package remain in one of these keys on the device. Running the script is a quick way to confirm whether the entry is genuinely gone.
Running this as a Worklet
Worklets execute in a 32-bit context as the System account. On 64-bit Windows, a 32-bit process reading HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall is transparently redirected to the WOW6432Node path. The script would then read the 32-bit location twice and return no 64-bit applications at all, which looks like a device with far less software installed than it has.
To read the 64-bit registry from a Worklet, invoke the 64-bit PowerShell explicitly:
C:\Windows\Sysnative\WindowsPowerShell\v1.0\powershell.exe -File "YOUR_SCRIPT.ps1"
For background on this behavior see Requested Registry Access is Not Allowed, and for validating a script under the same conditions Automox uses see How to Test a PowerShell Script Locally for Worklets.
Applications that do not appear
Some entries exist in these keys but are deliberately hidden from Add or Remove Programs, typically components installed as part of a larger product. These carry a SystemComponent value of 1. The script above returns them, so its output can be slightly longer than the Add or Remove Programs list. To match that list more closely, exclude them:
Where-Object { $_.DisplayName -and $_.SystemComponent -ne 1 } |This distinction matters when a product appears under an unexpected name or architecture, since the visible entry and the actual installed component are not always the same record.