---
title: "List Installed Software with PowerShell: A Free Tool"
description: "Build a free tool to quickly and easily list installed software on multiple Windows computers using PowerShell."
canonical: "https://adamtheautomator.com/powershell-list-installed-software/"
---

# List Installed Software with PowerShell: A Free Tool

> Build a free tool to quickly and easily list installed software on multiple Windows computers using PowerShell.

Source: https://adamtheautomator.com/powershell-list-installed-software/

---

ATA Learning

Tap to hide

[

ATA Learning

](/)

*   [Home](/)
*   [Tutorials](/tutorials/)
*   [Instructors](/author/)
*   [Advertising](/advertising/)
*   [Recommended Resources](/resources/)
*   [About Adam](/about-adam/)

Search for:  

*   [](https://twitter.com/adbertram)
*   [](https://github.com/Adam-the-Automator)
*   [](https://www.linkedin.com/company/adam-the-automator-llc)
*   [](/feed/)

![List Installed Software with PowerShell: A Free Tool](https://adamtheautomator.com/wp-content/uploads/2019/07/software-417880_1280.jpg)

# List Installed Software with PowerShell: A Free Tool

[![](https://secure.gravatar.com/avatar/d0b9d42e21e5622713f8b693aa5c0f9244d5f7dd200ed29b8398f52dee5de337?s=192&d=mm&r=g)Adam Bertram](https://adamtheautomator.com/author/adam-bertram/)21 July 20193 min. read

Categories: [IT Ops](/category/it-ops/)

Tags:[PowerShell](/tag/powershell/)

Table of Contents

*   [Installed Software and the Registry](#where-installed-software-lives)
*   [List Installed Software with PowerShell](#using-the-get-installedsoftware-function)
*   [Summary](#summary)

If you’re a system administrator, one of your jobs is to install, upgrade and remove software from lots of systems. What if I told you you don’t have to connect to each machine and check for installed software manually anymore? You can actually list installed software with PowerShell!

In this blog post, I’m going to show you how to list installed software with PowerShell on your local machine and lots of computers at once.

![Listing installed software with PowerShell script](https://adamtheautomator.com/wp-content/uploads/2022/04/image-119-1024x480.png)

Listing installed software with PowerShell script

> _Note that some articles may tell you to do something like `Get-WmiObject -Class win32_product`. Don’t do that. Learn why [here](https://sdmsoftware.com/wmi/why-win32_product-is-bad-news/)._

## Installed Software and the Registry

For reference, installed software exists in three locations:

*   the 32-bit system uninstall registry key
*   the 64-bit system uninstall registry key
*   each user profile’s uninstall registry key.

Each software entry is typically defined by the software’s globally unique identifier (GUID). The inside of the GUID key contains all the information about that particular piece of software. To get a complete list, PowerShell must enumerate each of these keys, read each registry value and parse through the results.

Related: [How to use PowerShell to Get a Registry Value (PS Drives and .NET)](https://adamtheautomator.com/powershell-get-registry-value/)

Since the code to correctly parse these values is way more than a single article can hold, I’ve prebuilt a function called `Get-InstalledSoftware` to list installed software with PowerShell that wraps all of that code up for you as you can see below that lists installed programs on a computer.

Related:[Get-ChildItem: Listing Files, Registry and Certificates](https://adamtheautomator.com/get-childitem/)

```powershell
function Get-InstalledSoftware {
    <#
	.SYNOPSIS
		Retrieves a list of all software installed on a Windows computer.
	.EXAMPLE
		PS> Get-InstalledSoftware
		
		This example retrieves all software installed on the local computer.
	.PARAMETER ComputerName
		If querying a remote computer, use the computer name here.
	
	.PARAMETER Name
		The software title you'd like to limit the query to.
	
	.PARAMETER Guid
		The software GUID you'e like to limit the query to
	#>
    [CmdletBinding()]
    param (
		
        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string]$ComputerName = $env:COMPUTERNAME,
		
        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string]$Name,
		
        [Parameter()]
        [guid]$Guid
    )
    process {
        try {
            $scriptBlock = {
                $args[0].GetEnumerator() | ForEach-Object { New-Variable -Name $_.Key -Value $_.Value }
				
                $UninstallKeys = @(
                    "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall",
                    "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
                )
                New-PSDrive -Name HKU -PSProvider Registry -Root Registry::HKEY_USERS | Out-Null
                $UninstallKeys += Get-ChildItem HKU: | where { $_.Name -match 'S-\d-\d+-(\d+-){1,14}\d+$' } | foreach {
                    "HKU:\$($_.PSChildName)\Software\Microsoft\Windows\CurrentVersion\Uninstall"
                }
                if (-not $UninstallKeys) {
                    Write-Warning -Message 'No software registry keys found'
                } else {
                    foreach ($UninstallKey in $UninstallKeys) {
                        $friendlyNames = @{
                            'DisplayName'    = 'Name'
                            'DisplayVersion' = 'Version'
                        }
                        Write-Verbose -Message "Checking uninstall key [$($UninstallKey)]"
                        if ($Name) {
                            $WhereBlock = { $_.GetValue('DisplayName') -like "$Name*" }
                        } elseif ($GUID) {
                            $WhereBlock = { $_.PsChildName -eq $Guid.Guid }
                        } else {
                            $WhereBlock = { $_.GetValue('DisplayName') }
                        }
                        $SwKeys = Get-ChildItem -Path $UninstallKey -ErrorAction SilentlyContinue | Where-Object $WhereBlock
                        if (-not $SwKeys) {
                            Write-Verbose -Message "No software keys in uninstall key $UninstallKey"
                        } else {
                            foreach ($SwKey in $SwKeys) {
                                $output = @{ }
                                foreach ($ValName in $SwKey.GetValueNames()) {
                                    if ($ValName -ne 'Version') {
                                        $output.InstallLocation = ''
                                        if ($ValName -eq 'InstallLocation' -and 
                                            ($SwKey.GetValue($ValName)) -and 
                                            (@('C:', 'C:\Windows', 'C:\Windows\System32', 'C:\Windows\SysWOW64') -notcontains $SwKey.GetValue($ValName).TrimEnd('\'))) {
                                            $output.InstallLocation = $SwKey.GetValue($ValName).TrimEnd('\')
                                        }
                                        [string]$ValData = $SwKey.GetValue($ValName)
                                        if ($friendlyNames[$ValName]) {
                                            $output[$friendlyNames[$ValName]] = $ValData.Trim() ## Some registry values have trailing spaces.
                                        } else {
                                            $output[$ValName] = $ValData.Trim() ## Some registry values trailing spaces
                                        }
                                    }
                                }
                                $output.GUID = ''
                                if ($SwKey.PSChildName -match '\b[A-F0-9]{8}(?:-[A-F0-9]{4}){3}-[A-F0-9]{12}\b') {
                                    $output.GUID = $SwKey.PSChildName
                                }
                                New-Object -TypeName PSObject -Prop $output
                            }
                        }
                    }
                }
            }
			
            if ($ComputerName -eq $env:COMPUTERNAME) {
                & $scriptBlock $PSBoundParameters
            } else {
                Invoke-Command -ComputerName $ComputerName -ScriptBlock $scriptBlock -ArgumentList $PSBoundParameters
            }
        } catch {
            Write-Error -Message "Error: $($_.Exception.Message) - Line Number: $($_.InvocationInfo.ScriptLineNumber)"
        }
    }
}
```

Once you copy and paste this function into your PowerShell console or add it to your script, you can call it by using a particular computer name with the `ComputerName` parameter.

## List Installed Software with PowerShell

```powershell
PS> Get-InstalledSoftware -ComputerName XXXXX
```

When you do this, you will get an object back for each piece of software that’s installed. You are able to get a wealth of information about this whatever software is installed.

If you know the software title ahead of time you can also use the `Name` parameter to limit only to the software that matches that value.

For example, perhaps you’d only like to check if Microsoft _Visual C++ 2005 Redistributable (x64)_ is installed. You’d simply use this as the `Name` parameter value as shown below.

```powershell
PS> Get-InstalledSoftware -ComputerName MYCOMPUTER -Name 'Microsoft VisualC++ 2005 Redistributable (x64)'
```

## Summary

Using PowerShell to get installed software, you can build a completely free tool that you and your team can use to easily find installed software on many Windows computers at once!

Share this article

[Share on X](https://twitter.com/intent/tweet?url=https%3A%2F%2Fadamtheautomator.com%2Fpowershell-list-installed-software%2F&text=List%20Installed%20Software%20with%20PowerShell%3A%20A%20Free%20Tool)[Share on Facebook](https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fadamtheautomator.com%2Fpowershell-list-installed-software%2F)[Share on LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fadamtheautomator.com%2Fpowershell-list-installed-software%2F)

## Related Posts

![](https://adamtheautomator.com/wp-content/uploads/2026/06/26995-troubleshoot-dns-issues-powershell-codex.webp)

### [Troubleshoot DNS Issues with PowerShell](/troubleshoot-dns-issues-powershell/)

Troubleshoot DNS issues with PowerShell by testing name resolution, DNS client settings, cache entries, and network connectivity in a repeatable workflow.

![](https://adamtheautomator.com/wp-content/uploads/2025/10/image_2025-10-24_095322075.png)

### [Migrating from PowerShell 6 to 7.5: Breaking Changes/New Features](/migrating-powershell-6-to-7-5/)

Migrate from PowerShell Core 6 to 7.5: breaking changes, features, and testing tips.

![](https://adamtheautomator.com/wp-content/uploads/2025/06/featured-image-6.png)

### [How to Add Timeouts to Pester Tests with PowerShell Runspaces](/pester-test-timeout-runspaces/)

Prevent Pester tests from hanging indefinitely using PowerShell runspaces. Learn to handle variable scoping, module loading, TestDrive access, and stream capture challenges with timeout protection.

## Categories

*   [IT Ops](/category/it-ops/)
*   [Cloud](/category/cloud/)
*   [DevOps](/category/devops/)
*   [Home Ops](/category/home-ops/)
*   [Information Security](/category/infosec/)
*   [Software Development](/category/software-development/)

## Site

*   [Home](/)
*   [Tutorials](/tutorials/)
*   [Instructors](/author/)
*   [Advertising](/advertising/)
*   [Recommended Resources](/resources/)
*   [About Adam](/about-adam/)

Copyright 2026© ATA Learning | [Privacy Policy](/privacy/)
