Use PowerShell to Find Installed Software

Summary: Guest blogger, Marc Carter, reprises his popular blog post about locating installed software.

Microsoft Scripting Guy, Ed Wilson, is here. Marc Carter is joining us again today with another guest blog post…

Looking back a couple years ago to my previous post, Use PowerShell to Quickly Find Installed Software, I find it interesting to reflect on common issues shared amongst the IT pro community. In our underlying goal to control our environment, whether that environment consists of a desktop computer, a development server, or production data center, we must first discover and understand before we can effectively attempt to control. Such is the case for sys admins when determining what software is currently configuring a server.

But first, let’s have a quick refresher on what initially prompted this discussion…

Win32_Product: The Good, the Bad, and the Ugly

 [Good] The Win32_Product WMI class represents products as they are installed by Windows Installer.

If you choose to query Win32_Product class by using Get-WmiObject, you’ll find yourself [Bad] waiting for your query (or application) to return [Ugly] a consistency check of packages that are installed as it attempts to verify and repair installs. (For more information, see Event log message indicates that the Windows Installer reconfigured all installed applications).

Problem #1: Um, is there a problem, officer?

Querying the Win32_Product class to determine installed software is more than likely not your “best” option. Unfortunately, not everyone knows this.

Solution: (Understanding) Do your part and help spread the word.

Problem #2: Identify better alternatives

I really like some of the refinements and suggestions within comments that were mentioned by others on my previous post. One of the things I take a lot of pride in is my association with the men and women of US Army and their core values (The Army Values).

I see that similar mindset and participation reflected in the esprit de corps (or cohesion) of the Windows PowerShell community. As others have pointed out, there are a lot better and easier ways to gather information without invoking the Win32_Product class. One of my favorite alternatives involved suggestions from Knut Johansen and Mike Crowley: use the PS Registry Provider.

The Windows PowerShell Registry provider lets you get, add, change, clear, and delete registry keys, entries, and values in Windows PowerShell. The Registry provider lets you access a hierarchical namespace that consists of registry keys and subkeys. Registry entries and values are not components of that hierarchy. Instead, they are properties of each of the keys. The Registry provider supports all the cmdlets that contain the “item” noun—that is, the Item cmdlets (except Invoke-Item) such as Get-Item, Copy-Item, and Rename-Item. Use the Item cmdlets when you work with registry keys and subkeys.
For more information, see Registry Provider.

In the following example, I use the Get-ItemProperty cmdlet to return values from the Uninstall Registry Key within the HKEY LOCAL MACHINE (HKLM) Registry Provider, selecting specific properties and then formatting output.

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

The Get-ItemProperty cmdlet is a great tool because it’s designed to work with data that is exposed by any provider. To get a better idea of the various providers that are available in your session, simply execute the Get-PSProvider cmdlet.  

And of course, depending on my needs, I could have also used alternative output methods like Out-GridView or Export-Csv. Either way, we’ve now reduced the process to a one-liner that can be used in 64-bit and 32-bit environments:

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

Problem #3: Can we make it even more useful?

Absolutely! We are talking Windows PowerShell after all…

One way that comes to mind (and again, visible within the comments from the previous post), is addressing the issue of how to query multiple remote devices. My solution (or a number of reasons) is to rely on using the Invoke-Command cmdlet. In the following example, I query both of my SharePoint Web Front End (WFE) servers by using Invoke-Command to execute the same Get-ItemProperty on the remote system’s HKLM PS Registry Provider:

Invoke-Command -cn wfe0, wfe1 -ScriptBlock {Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* | select DisplayName, Publisher, InstallDate }

The output now includes the PSComputerName column, which will help when I want to sort results down the road. And there we have it…an easy method to report installed software!

I look forward to reading comments from the Windows PowerShell community on other refinements and ways to improve this task. In many ways, I relate our efforts to that of a symphony or band. Each of us plays a different note in that we all hear and see things differently. Put us all together on the same sheet of music, and we have the potential for some awesome melodies.


Thank you, Marc, for another awesome blog.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy