Use PowerShell to Find 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…

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.

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:

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:

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.

Thanks, for sharing but I found it missing a lot of apps. command I used Get-ItemProperty HKLM:SoftwareMicrosoftWindowsCurrentVersionUninstall* | Select-Object DisplayName, DisplayVersion, Publisher, InstallDate | ft –AutoSize

Thank you so much for sharing this post. I’d been researching and attempting several ways to gather the information, but this one bubbled to the top. Especially when adding the Wow6432Node — provided me with exactly what I needed in preparation of a huge transition. Thank you again.

win32_product has the additional drawback of not reporting on x86 apps for x64 systems.

The script repository has a couple functions for download that people can download in case anyone wants to add this to one of their modules or profile. I keep get-applicationinfo in my profile because I end up using it so much.

@Jason – Win32_Product reports both x*^ and x64 products on all systems I have tested on. It only reports products installed by Windows installer and does not report O365 components. They are not really installed locally but are web delivered and use a different tool to report.

Older 'Setup' installs are not reported and XCOPY self installers are not reported "Store Apps" are not reported.

@Jason – just to be sure I ran it on both x86 and x64 PowerShell and both report the exact same thing.

You are thinking of the registry install keys. You have to query both the 32 and 64 bit registry on a 64 bit system to get all installed programs and that still will not get XCOPY installs. Java is frequently a copy install local to the app that uses it. There is no way to know about that automatically. You must know in advance that the program sues Java and then look for the Java file. It can be anywhere. It is usually in a subdir of the program called JRE.

Interesting.. but could we go on with this and uninstall a software? Thats actually why I started to look into Win32_Product class. I could search all systems in the domain for a software and start a deinstallation. Really nice if you need to remove a specific software from all systems.

I have a 64-bit system and this script does not output all installed applications. It only outputs a portion of all installed applications. Isn’t there a script which output ALL installed applications?

This is a nice little command. I was looking for something simple to check that a particular piece of software was installed on a system before having a script start the quiet install for it and this does the trick by piping Where-Object { $_.DisplayName
-eq "Name of Software" } after the get-itemproperty command.

Hi Scripting Guy,
I’d like to get a list of installed softwares like .net framework, SQL Server,etc(selective applications) with its version for a list of servers(input server list) to an excel file. Could you please help?

Hi guys,
thanks for the script it was very helpful. however i have a little probleme. i adapted the script to check if a s pecifique software is installed ( to not reinstall it again). my script looks like :

but i face a probleme when the Display name is " Microsoft Visual C++ 2005 Redistributable" , is is not displayed by the instructio " $x.DisplayName" , and the test "if ( $x.DisplayName )" fails .
any one can Help Please ?

Is there a particular version of PowerShell I need to be running in? I’m getting an error indicating ‘winrm quickconfig’ needs to be run on the remote system. After running the command, I get a result but the data is not formatted correctly as shown in
your screenshot, i.e. I see a long list rather than a table with headers. How can I effectively run the winrm command on all my remote systems (starting the winrm service which is default to manual, does not suffice), and how can I export the list to a formatted
CSV?

When I run this on a machine that I know is up and running:
Invoke-Command -cn XXXX -ScriptBlock {Get-ItemProperty HKLM:SoftwareMicrosoftWindowsCurrentVersionUninstall* | select DisplayName, Publisher, InstallDate }

I get this error message:
[XXXX] Connecting to remote server v0vps1 failed with the following error message : WinRM cannot process the request. The following error occurred while using Kerberos authentication: Cannot find the computer XXXX. Verify that the computer exists on the network
and that the name provided is spelled correctly. For more information, see the about_Remote_Troubleshooting Help topic.
+ CategoryInfo : OpenError: (XXXX:String) [], PSRemotingTransportException
+ FullyQualifiedErrorId : NetworkPathNotFound,PSSessionStateBroken

This doesn’t work for me. I tried it on Windows Vista, Windows 7, and even on Windows 10. I get no errors and no output. I can’t even get the listing of my local PC. I can do this in VBS and that works fine. Is this another PS annoyance?

This is confusing to someone new with pweershell/scripting. I didn’t see anywhere in the story where it said. This is how you search for specific software installations. All it says is "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."

So…..how does this help me? This says nothing about it searching for software. Why is this so difficult? I thought this would be easily explained, I thought wrong.

To use the function do something like : Check-InstalledSoft -computer (Get-content MyListOfServers.txt) -SoftToCheck (get-content MyListOfSoft.txt)

If you don’t specify computer it’ll run on the host you run it on.
If you don’t specify SoftToCheck it’ll retrieve all the softwares.

But as stated previously it’s not 100% reliable as I get incomplete outputs compared to the actual softs installed… I haven’t dug yet but probably something to do with 64bits or the character limits in the registry keys.
If you know please share 🙂