# PowerShell: List Installed Software (Registry Method)

> List installed software on Windows with PowerShell using the registry: fastest one-liner, remote computers, CSV export and why to avoid Win32_Product.

- Page URL: https://itinsighthub.com/guides/powershell-list-installed-software/
- Markdown variant of this page: append `?format=md` to any URL on this site or send `Accept: text/markdown`.
- Full site index for AI assistants: https://itinsighthub.com/llms.txt
- Full site export: https://itinsighthub.com/llms-full.txt

# List Installed Software with PowerShell

Query the Windows registry for a complete, accurate inventory of installed programs — locally or across a network — without triggering Windows Installer side effects.

Published August 22, 2026

## The fastest way

Every Windows installer that follows the Microsoft convention writes an uninstall entry under `HKLM`. Reading those keys with [Get-ItemProperty](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.management/get-itemproperty) is instant, read-only and needs no elevation:

PowerShell

```
Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*, HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* | Select-Object DisplayName, DisplayVersion, Publisher, InstallDate | Sort-Object DisplayName | Format-Table -AutoSize
```

That returns a table of display name, version, publisher and install date for machine-wide installs. If you see blank rows, those are system components that ship no `DisplayName` — pipe through [Where-Object](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/where-object) to drop them.

## Why not Win32_Product?

Avoid Get-WmiObject Win32_Product (or its Get-CimInstance equivalent) for inventory. Enumerating this class makes Windows Installer validate every MSI-managed package on the machine, which can trigger silent reconfigurations and self-repairs of applications — the classic cause of "apps started fixing themselves after someone ran a software audit." It is also dramatically slower than a registry read. The WMI class is fine for querying one specific product by exact identity; it is the wrong tool for listing everything.

## Remote computers

The same registry query runs remotely over WinRM with [Invoke-Command](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/invoke-command), so nothing has to be installed on the target:

PowerShell

```
Invoke-Command -ComputerName PC01 -ScriptBlock {
    Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*,
                     HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* |
        Where-Object DisplayName |
        Select-Object DisplayName, DisplayVersion, Publisher, InstallDate |
        Sort-Object DisplayName
} -Credential (Get-Credential)
```

You need the WinRM service enabled on the target (it is on by default for domain-joined servers), permission as a local administrator there, and typically an elevated session. Drop the `-Credential` parameter when your signed-in account already has rights.

## Export to CSV

Swap `Format-Table` for [Export-Csv](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/export-csv) to hand the inventory to Excel or a ticketing system. Adding `UninstallString` captures each package's uninstall command line at the same time:

PowerShell

```
$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, UninstallString |
    Sort-Object DisplayName |
    Export-Csv -Path .\installed-software.csv -NoTypeInformation
```

`-NoTypeInformation` keeps the file clean in Windows PowerShell 5.1; PowerShell 7 omits that header row by default either way.

## Filtering by name

Filter early with `Where-Object` instead of eyeballing a long table:

PowerShell

```
Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*,
                 HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* |
    Where-Object { $_.DisplayName -like '*Visual Studio*' } |
    Select-Object DisplayName, DisplayVersion, UninstallString
```

Use `-eq 'Exact Name'` for exact matches or `-match 'pattern'` for regex. Filtering before `Select-Object` keeps the pipeline cheap on machines with hundreds of entries.

## The winget alternative

PowerShell

```
winget list
```

On Windows 10/11 with App Installer installed, [winget](https://learn.microsoft.com/en-us/windows/package-manager/winget/) `list` is a friendly alternative: it resolves human-readable names and shows packages winget can upgrade. Scope differs though — it reports what is visible to *your user account*, including per-user installs, while the HKLM keys above enumerate *machine-wide* installs regardless of which user queries them or which package manager put them there. For audits and compliance snapshots, prefer the registry method; use `winget list` interactively.

## 64-bit vs 32-bit: why both paths?

On 64-bit Windows there are two parallel registry views. Native 64-bit installers write under `HKLM:\SOFTWARE\...\Uninstall`, while 32-bit installers get silently redirected into `HKLM:\SOFTWARE\WOW6432Node\...`. Querying only one tree misses half your inventory, so the examples above always include both paths.

One more trap: if you launch the 32-bit edition of PowerShell on a 64-bit OS, the filesystem and registry redirectors kick in, so HKLM:\Software\Microsoft\... quietly resolves to the WOW6432Node view and you will see duplicates or miss entries. Use the default 64-bit shell (C:\Program Files\PowerShell\7\pwsh.exe or %SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe) and query both trees explicitly.

## FAQ

**Do I need administrator rights to list installed software?**

Not locally. The HKLM uninstall keys are readable by regular users. Administrator rights are required only for the remote scenario via Invoke-Command, where you must be authorized on the target machine.

**Why does the output include entries with no DisplayName?**

Many uninstall keys belong to MSI components, updates and runtime libraries that do not expose a display name. Filter them out with Where-Object DisplayName right after Get-ItemProperty.

**How do I get the uninstall command line for each program?**

Add UninstallString (and optionally QuietUninstallString) to the Select-Object list. The value contains the exact command Windows runs when a user clicks Uninstall.

**Do these commands work in both Windows PowerShell 5.1 and PowerShell 7?**

Yes. Every command on this page uses built-in cmdlets and the registry provider, which behave identically in 5.1 and 7 on Windows. Export-Csv's -NoTypeInformation flag is simply redundant in 7, where it is already the default.

## More IT tools

Explore the networking references and calculators:

- [IPv4 subnet calculator](https://itinsighthub.com/subnet-calculator/) — network, broadcast and host range for any CIDR block.
- [Subnet cheat sheet](https://itinsighthub.com/subnet-cheat-sheet/) — printable mask table from /0 to /32.
- [Wildcard mask calculator](https://itinsighthub.com/wildcard-mask-calculator/) — CIDR &hArr; inverse masks for ACLs.
- [About ITInsightHub](https://itinsighthub.com/about/) — what we build and why.

---

© 2026 ITInsightHub · [About](https://itinsighthub.com/about/) · [Contact](https://itinsighthub.com/contact/) · [Privacy](https://itinsighthub.com/privacy/)
