PowerShell: list installed software (without Win32_Product)
Read the two registry uninstall hives, 64-bit and 32-bit, shown below. Do not use Get-CimInstance Win32_Product: enumerating that class makes Windows Installer verify and often repair every MSI package on the machine, which is slow and can trigger repair storms.
$paths = 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*',
'HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
Get-ItemProperty $paths |
Where-Object DisplayName |
Select-Object DisplayName, DisplayVersion, Publisher, InstallDate |
Sort-Object DisplayName
Notes before you run it
- Per-user installs (many browsers, Teams) live under each user's
HKCUhive and will not appear when run as SYSTEM or another admin; addHKCU:paths if you need them for the logged-on user. - Store/UWP apps are a separate world:
Get-AppxPackage -AllUsers. InstallDateis a bare yyyyMMdd string when present, and often absent; treat it as a hint.
Why Win32_Product is the wrong tool
Querying the Win32_Product WMI class forces Windows Installer to enumerate every registered MSI package and run a consistency check. That check can trigger a repair, which reinstalls files, resets permissions and, in some cases, restarts services on a machine nobody asked you to change. The machine writes MsiInstaller event 1035 ("Reconfiguration success or error status") and event 1004 for each package as it goes, so a machine full of those events is often a sign that something is querying the class on a schedule.
The operation is also slow, taking minutes where the registry read takes under a second. The registry uninstall keys are what Add/Remove Programs itself reads, so they are the authoritative list, not a workaround.
Including per-user installs
Applications that install per user, which includes several browsers and Teams, write to HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*. Running as the logged-on user, add that path to the list and it just works. Running as SYSTEM, HKCU is SYSTEM's own hive and will be nearly empty. To reach a real user's installs from SYSTEM, read their loaded hive under HKEY_USERS by SID:
Get-ItemProperty "Registry::HKEY_USERS\<SID>\Software\Microsoft\Windows\CurrentVersion\Uninstall\*"
A user's hive is only loaded under HKEY_USERS while they are signed in, so an inventory run overnight will miss them.
Finding one application across the fleet
Filter on the name rather than eyeballing the list:
... | Where-Object DisplayName -like '*Chrome*' | Select-Object DisplayName, DisplayVersion
Run it remotely with Invoke-Command -ComputerName <names> -ScriptBlock { ... }, which tags every row with PSComputerName so the output says which machine had which version. Export for a report with Export-Csv -NoTypeInformation. This is the practical way to answer "who still has the vulnerable version" without a dedicated inventory product.
Getting the uninstall command
Each uninstall key holds UninstallString, and many also hold QuietUninstallString. QuietUninstallString is the one to use in automation, because UninstallString often opens an interactive window that will sit there forever on a machine with nobody at the keyboard. For MSI packages the key name is the product GUID, and the silent removal is msiexec /x {GUID} /qn /norestart.
Add UninstallString, QuietUninstallString to the Select-Object list when the point of the inventory is removal rather than reporting.
Frequently asked questions
Why should I not use Win32_Product to list installed software?
Querying that class makes Windows Installer verify every installed MSI package, which can trigger repairs that reinstall files and restart services on a production machine. It also takes minutes rather than seconds. Reading the registry uninstall keys returns the same list without touching anything.
Where does Windows store the list of installed programs?
In the registry, under HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall for 64-bit applications, the WOW6432Node path for 32-bit ones, and the matching HKCU path for per-user installs. Add/Remove Programs reads the same keys.
Why is an application missing from the results?
It is most likely a per-user install sitting in HKCU rather than HKLM, a Store app that only Get-AppxPackage sees, or a 32-bit application on a 64-bit machine when only the main hive was read.
Does this list Windows updates?
No. Updates are servicing components, not installed programs, and do not appear in the uninstall keys. Use Get-HotFix for the installed update list.
Run it on every machine, not one at a time.
Save any of these as a command in Moorfox and run it on a machine in one click, output and exit code captured, with no PSRemoting or WinRM setup. The agent runs it locally and reports back, even over the internet.