List Installed Software aka Get-InstalledSoftware

I needed a script to check for installed software on local and remote machines that did not rely on the Win32_Product WMI class as this will only return software that the windows installer knows about.

One way to do this is via the visual basic script List All Installed Software (Microsoft) on script center, but as I prefer Powershell I had to rewrite my own version.

Also since posting this on technet I have had some feedback that it was not working on everyones machine so please feel free to add questions or feedback here and we can iron out any bugs there maybe.

Also if anyone has any design ideas, as I am not particularly happy about the way the 64 bit version works.

Usage Examples:
Get-InstalledSoftware
Get-InstalledSoftware | Select Name,InstallDate | Format-Table
Get-InstalledSoftware | Sort-Object @{Expression={$_.ComputerName};Ascending=$True},@{Expression={$_.Name};Ascending=$True} | Format-Table
Get-InstalledSoftware "ComputerA","ComputerB" | Export-CSV -NoTypeInformation "C:\InstalledSoftware.csv";

32 Bit

#http://jongurgul.com/blog/installedsoftware/
Function Get-InstalledSoftware{
	Param([String[]]$Computers)
	If (!$Computers) {$Computers = $ENV:ComputerName}
	$Base = New-Object PSObject;
	$Base | Add-Member Noteproperty ComputerName -Value $Null;
	$Base | Add-Member Noteproperty Name -Value $Null;
	$Base | Add-Member Noteproperty Publisher -Value $Null;
	$Base | Add-Member Noteproperty InstallDate -Value $Null;
	$Base | Add-Member Noteproperty EstimatedSize -Value $Null;
	$Base | Add-Member Noteproperty Version -Value $Null;
	$Results =  New-Object System.Collections.Generic.List[System.Object];

	ForEach ($ComputerName in $Computers){
		$Registry = $Null;
		Try{$Registry = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine,$ComputerName);}
		Catch{Write-Host -ForegroundColor Red "$($_.Exception.Message)";}

		If ($Registry){
			$UninstallKeys = $Null;
			$SubKey = $Null;
			$UninstallKeys = $Registry.OpenSubKey("Software\Microsoft\Windows\CurrentVersion\Uninstall",$False);
			$UninstallKeys.GetSubKeyNames()|%{
				$SubKey = $UninstallKeys.OpenSubKey($_,$False);
				$DisplayName = $SubKey.GetValue("DisplayName");
				If ($DisplayName.Length -gt 0){
					$Entry = $Base | Select-Object *
					$Entry.ComputerName = $ComputerName;
					$Entry.Name = $DisplayName.Trim();
					$Entry.Publisher = $SubKey.GetValue("Publisher");
					[ref]$ParsedInstallDate = Get-Date
					If ([DateTime]::TryParseExact($SubKey.GetValue("InstallDate"),"yyyyMMdd",$Null,[System.Globalization.DateTimeStyles]::None,$ParsedInstallDate)){
					$Entry.InstallDate = $ParsedInstallDate.Value
					}
					$Entry.EstimatedSize = [Math]::Round($SubKey.GetValue("EstimatedSize")/1KB,1);
					$Entry.Version = $SubKey.GetValue("DisplayVersion");
					[Void]$Results.Add($Entry);
				}
			}
		}
	}
	$Results
}

64 Bit

#http://jongurgul.com/blog/installedsoftware/
Function Get-InstalledSoftware{
	Param([String[]]$Computers)
	If (!$Computers) {$Computers = $ENV:ComputerName}
	$Base = New-Object PSObject;
	$Base | Add-Member Noteproperty ComputerName -Value $Null;
	$Base | Add-Member Noteproperty Name -Value $Null;
	$Base | Add-Member Noteproperty Publisher -Value $Null;
	$Base | Add-Member Noteproperty InstallDate -Value $Null;
	$Base | Add-Member Noteproperty EstimatedSize -Value $Null;
	$Base | Add-Member Noteproperty Version -Value $Null;
	$Base | Add-Member Noteproperty Wow6432Node -Value $Null;
	$Results =  New-Object System.Collections.Generic.List[System.Object];

	ForEach ($ComputerName in $Computers){
		$Registry = $Null;
		Try{$Registry = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine,$ComputerName);}
		Catch{Write-Host -ForegroundColor Red "$($_.Exception.Message)";}

		If ($Registry){
			$UninstallKeys = $Null;
			$SubKey = $Null;
			$UninstallKeys = $Registry.OpenSubKey("Software\Microsoft\Windows\CurrentVersion\Uninstall",$False);
			$UninstallKeys.GetSubKeyNames()|%{
				$SubKey = $UninstallKeys.OpenSubKey($_,$False);
				$DisplayName = $SubKey.GetValue("DisplayName");
				If ($DisplayName.Length -gt 0){
					$Entry = $Base | Select-Object *
					$Entry.ComputerName = $ComputerName;
					$Entry.Name = $DisplayName.Trim();
					$Entry.Publisher = $SubKey.GetValue("Publisher");
					[ref]$ParsedInstallDate = Get-Date
					If ([DateTime]::TryParseExact($SubKey.GetValue("InstallDate"),"yyyyMMdd",$Null,[System.Globalization.DateTimeStyles]::None,$ParsedInstallDate)){
					$Entry.InstallDate = $ParsedInstallDate.Value
					}
					$Entry.EstimatedSize = [Math]::Round($SubKey.GetValue("EstimatedSize")/1KB,1);
					$Entry.Version = $SubKey.GetValue("DisplayVersion");
					[Void]$Results.Add($Entry);
				}
			}

				If ([IntPtr]::Size -eq 8){
                $UninstallKeysWow6432Node = $Null;
                $SubKeyWow6432Node = $Null;
                $UninstallKeysWow6432Node = $Registry.OpenSubKey("Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall",$False);
                    If ($UninstallKeysWow6432Node) {
                        $UninstallKeysWow6432Node.GetSubKeyNames()|%{
                        $SubKeyWow6432Node = $UninstallKeysWow6432Node.OpenSubKey($_,$False);
                        $DisplayName = $SubKeyWow6432Node.GetValue("DisplayName");
                        If ($DisplayName.Length -gt 0){
                        	$Entry = $Base | Select-Object *
                            $Entry.ComputerName = $ComputerName;
                            $Entry.Name = $DisplayName.Trim();
                            $Entry.Publisher = $SubKeyWow6432Node.GetValue("Publisher");
                            [ref]$ParsedInstallDate = Get-Date
                            If ([DateTime]::TryParseExact($SubKeyWow6432Node.GetValue("InstallDate"),"yyyyMMdd",$Null,[System.Globalization.DateTimeStyles]::None,$ParsedInstallDate)){
                            $Entry.InstallDate = $ParsedInstallDate.Value
                            }
                            $Entry.EstimatedSize = [Math]::Round($SubKeyWow6432Node.GetValue("EstimatedSize")/1KB,1);
                            $Entry.Version = $SubKeyWow6432Node.GetValue("DisplayVersion");
                            $Entry.Wow6432Node = $True;
                            [Void]$Results.Add($Entry);
                        	}
                        }
                	}
                }
		}
	}
	$Results
}

http://gallery.technet.microsoft.com/scriptcenter/519e1d3a-6318-4e3d-b507-692e962c6666


Comments

18 responses to “List Installed Software aka Get-InstalledSoftware”

  1. In a file copy the function only. Name this file MyScript.ps1 and the in a powershell run . .\MyScript.ps1 which will load the function. Finally run function with variable. You are currently executing the content of the file and no variable is passed.if you want to call it the way you are currently doing you will need to specify the param at the top of the file and then call the function with that.

    1. I’m sorry, I do appreciate you trying to help, but I’m confused(to be clear, I’m using the 64-bit version of your script).
      I am following your 4th Usage Example to call on a remote computer, so I’m not sure why it won’t work for me as stated. I’m also unsure what parts of the script makes only the function – if you can specify the range of lines, that would be of great help.
      I tried specifying the name of the remote computer for the $Computer variable within the script, and saw it worked for me, so I hope we’re able to figure it out.

      Thank you.

  2. Copy everything from function to the final bracket into a file. At the top of this file add Param([String[]]$Computers)

    At the end of the file add

    Get-InstalledSoftware $Computers

  3. Jon… it worked:

    Param([String[]]$Computers)
    Function Get-InstalledSoftware{
    If (!$Computers) {$Computers = $ENV:ComputerName}

    }Get-InstalledSoftware $Computers

    Command line:
    Get-InstalledSoftware “Computer1″,”Computer2″,”Computer3”,etc

    Now – figure out how to stretch the length of the cells, when formatting the output to table…

    Thanks again, Jon

    1. Either use format-table or similar command to specify width. Or send straight to a file instead.
      Get-InstalledSoftware $Computers| Export-Csv -Path “c:\results.csv” -NoTypeInformation

Leave a Reply

Your email address will not be published. Required fields are marked *