Use Powershell to Get Installed Software

Need to get installed software on a Windows workstation? No problem.

Use Powershell to get all software installed on an end-user Windows workstation

So, you’re working in an enterprise environment? Maybe you’re an Organization Unit Administrator or a Workstation Administrator. Either way, you’ve probably wished there was an easy way to remotely get the software installed on one of the workstations in your footprint. Well, fret no more! Today I’m going to share three commands you can use to get a lit of software installed on any Windows workstation. Of course, the following commands will rely on you having a remote connection to the workstation(s) in question.

You can quickly get on the target computer by running the following command:

Enter-PSSession <ComputerName>

Where is the workstation you’re interested in. You will of course need to provide credentials or have access via your Active Directory domain group permissions profile.

First Way

Get-WMIObject -Class Win32_Product

Second Way

Get-WMIObject -Class Win32Reg_AddRemovePrograms

Third Way (Comprehensive)

Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*, HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* | Where-Object {-not [string]::IsNullOrWhiteSpace($_.DisplayName) } | Select *

Since Powershell is an object-oriented shell, you can really benefit by tweaking which data points you need using a pipe to the Select Commandlet (as shown in the last example). This make it really easy to gather information quickly, without leaving your seat, and organizing it into some kind of logical representation you can use for reporting or general oversight. Let’s switch gears and assume you have a local file with computers listed in it, and you want to get a comprehensive list of all installed software by machine. No problem! Here’s one way to tackle this:

# Your list of devices
$devices        = Import-Csv "AD_Devices.csv"
# Create an empty array to hold all objects created in the script and setup analytics
$collection     = @()
$deviceCount    = $devices.Count
$startTime      = Get-Date
$devicesUp      = 0
$devicesDown    = 0
$position       = 1
Write-Host "Starting software enumeration of $deviceCount devices..."
foreach ($dev in $devices) {

    $computerName   = $dev.Name # <-- The actuall computer name  - should be column name in csv
    $bucketName     = $dev.BucketName # <-- Bucketname in this case is the OU - should be column name too
    $fqdn           = "$computerName.DS.DOMAIN.YOU" # <-- Whatever your domain is...

    Write-Host "Connecting to $computerName ... " -NoNewline
    if (Test-Connection -Count 2 $fqdn -Quiet) {
        Write-Host "Connected! " -ForegroundColor Green -NoNewline
        Write-Host "Enumerating software ... " -NoNewline
        $software = Get-WMIObject -ErrorAction SilentlyContinue -Class Win32Reg_AddRemovePrograms -ComputerName $computerName
        if ($software) {
            $softwareCount = $software.Count
            foreach ($item in $software) {
                $obj = New-Object PSObject
                $obj | Add-Member NoteProperty ComputerName($computerName)
                $obj | Add-Member NoteProperty BucketName($bucketName)
                $displayName = If ($item.DisplayName -eq $null) {"Unknown"} Else {$item.DisplayName}
                $obj | Add-Member NoteProperty SoftwareDisplayName($displayName)
                $publisher = If ($item.Publisher -eq $null) {"Unknown"} Else {$item.Publisher}
                $obj | Add-Member NoteProperty SoftwarePublisher($publisher)
                $obj | Add-Member NoteProperty InstallDate($item.InstallDate)
                $obj | Add-Member NoteProperty ProductID($item.ProdID)
                $obj | Add-Member NoteProperty Version($item.Version)
                $collection += $obj
            Write-Host $softwareCount" programs found! " -ForegroundColor Green -NoNewline
        } else {
            Write-Host "No Software! " -ForegroundColor Red -NoNewline
        $place      = $deviceCount - $position
        $position  += 1
        $devicesUp += 1
        Write-Host "$place devices left" -ForegroundColor Green
     } else {
        $place = $deviceCount - $position
        $position += 1
        Write-Host "Unable to connect! $place devices left" -ForegroundColor Red
        $devicesDown += 1


$endTime = Get-Date
$duration = 'Operation took: {0:mm} min {0:ss} sec' -f ($endTime-$startTime)
Write-Host $duration
Write-Host "Devices up: $devicesUp - Devices down: $devicesDown"
Write-Host 'Exporting CSV data...'
$collection | Export-Csv -NoTypeInformation Discovered_Software.csv
Write-Host 'Complete!' -ForegroundColor Green

Of course, this is only one way to do it, and it uses some pre-suppositions i.e. what your columns are named in your CSV file. As you get better at powershell, you’ll be able to define your own PSObjects to hold the exact data you want. In the end, it just makes your life a hell of a lot easier when you can run a script against a known set and then get the data you need. As an aside, Powershell is not known for being “fast” - LOL. So, if you run this against 1K computers or more, then you should expect a good three-hour run! Hope these solutions help you out!

Think I might be a good fit for your project?

Let's get the conversation started!