Description
Allows you get some information about installed software on a Local or Remote Computer or a List of computers.
This is best viewed using Export-CSV or Out-GridView.
Source Code
#\/******/\********\/********/\********\/********/\********\/********/\******\/******\/******\/
#/\()()()\/()()()()/\()()()()\/()()()()/\()()()()\/()()()()/\()()()()\/()()()/\()()()/\()()()/\
#\/()()()/\()()()()\/()()()()/\()()()()\/()()()()/\()()()()\/()()()()/\()()()\/()()()\/()()()\/
#/\******\/********/\********\/********/\********\/********/\********\/******/\******/\******/\
#\-\
#-\-Author: Chris Rakowitz
#\-\Purpose: Returns a list of programs installed on a Target Machine.
#/-/Date: August 28, 2015
#-/-Updated: September 14, 2016 (2.00)
#/-/ [+] Re-wrote the script to use the Registry instead of WMI Queries.
#\-\ [+] Script is substantially quicker.
#-\- [+] Sorts output by Name making it easier to locate specific software.
#\-\ [+] Now checks if PC is online before attempting to retrieve data.
#/-/ [+] Updated Help information.
#-/- August 4, 2017 (2.01)
#/-/ [+] Script now detects if a machine is x86 or x64.
#\-\ September 6, 2017 (2.02)
#-\- [+] Corrected issue with getting installed software from 32-bit machines.
#\-\ October 11, 2017 (2.03)
#/-/ [+] Added Progress Bars.
#-/-Version: 2.03
#/-/
#\/******/\********\/********/\********\/********/\********\/********/\******\/******\/******\/
#/\()()()\/()()()()/\()()()()\/()()()()/\()()()()\/()()()()/\()()()()\/()()()/\()()()/\()()()/\
#\/()()()/\()()()()\/()()()()/\()()()()\/()()()()/\()()()()\/()()()()/\()()()\/()()()\/()()()\/
#/\******\/********/\********\/********/\********\/********/\********\/******/\******/\******/\
<#
.SYNOPSIS
Extract information for all software installed in a computers registry.
.DESCRIPTION
This script gets software information by querying the registry Keys:
64-Bit
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall
32-Bit
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall
The output is stored as a sorted array of objects using the Sort-object cmdlet
so they can be exported easily to .csv files, the Format-List, Format-Table, Out-GridView cmdlets, etc.
Running the script without any input will cause it to be run on the local machine. Text files,
comma-separated lists and single computers or IP Addresses can be provided to target other machines.
The Remote Registry service is required to be enabled in order to extract information from a remote computer.
This script will enable this service, extract the information, and then stop and disable the service.
Provides the following information:
Computer Name, Registry Key for the Software, Software Name, Software Publisher, Install Date,
Software Version, Architecture(x86/x64), Install Source and the Uninstall String to remove the software.
.PARAMETER ComputerName
Targets a single computer, a comma-separated list of computers or a text file with
a list of computer. Each computer must be on a separate line in the text file.
Also accepts IP Addresses.
.EXAMPLE
Get-InstalledSoftware
Get information about installed software on the local machine.
.EXAMPLE
Get-InstalledSoftware Test-1234
Get information about installed software for a remote machine.
.EXAMPLE
Get-InstalledSoftware Test-1234, Test-2345, Test-3456, Test-4567
Get software information for each of the computers supplied in the
comma-separated list.
.EXAMPLE
Get-InstalledSoftware "C:\My Directory\mylistoffiles.txt"
Get information about installed software from a list of machines.
.EXAMPLE
Get-InstalledSoftware "C:\Another Directory\list3.txt" | Export-CSV "C:\SoftwareList.csv"
Get software information from a list of computers and export it
to a csv file for later viewing.
.EXAMPLE
Get-InstalledSoftware | Sort-Object -Property ProductName | Out-GridView
Get a full list of software and sort it by Product Name before displaying it with
Out-GridView.
.NOTES
This is required to be run as a local administrator or as a Domain account
that has Administrator rights on the target computer or computers.
#>
param
(
[Parameter(ValueFromPipelineByPropertyName=$true,ValueFromPipeline=$true,`
Mandatory=$False,Position=0)] [string[]]$ComputerName = $env:computername
)
# Tell Powershell to ignore any errors that may fill up the screen.
$ErrorActionPreference = 'silentlycontinue'
# Allows the Script to accept a text file list of computers or IP Addresses as input.
# Each item must be on its own line.
If($ComputerName -like "*.txt")
{
$CompList = Get-Content ([regex]::matches($ComputerName,'[^\"]+') | %{$_.value})
}
Else
{
$CompList = $ComputerName
}
[int]$CompCount = $CompList.Count
[int]$CCount = 0
Foreach($Computer in $CompList)
{
$CCount++
Write-Progress -Activity "Installed Software Scan" -Status "Checking Computer [$CCount / $CompCount]: $Computer" `
-PercentComplete (($CCount / $CompCount) * 100) -Id 1
# Check to make sure if computer is online. Skip PC if not online.
If(!(Test-Connection -ComputerName $Computer -TimeToLive 18 -Count 1))
{
Write-Host "$Computer - Offline" -Foreground red -Background black
Write-Host ""
Write-Progress -Activity "Installed Software Scan" -Status "Complete" -Completed -Id 1
continue
}
Else
{
# Get the Computers Name. This is helpful if a single IP address or list of
# IP Addresses is provided.
$CompName = (Get-WMIObject -Class Win32_ComputerSystem -ComputerName $Computer).Name
# Get the Computers Archtecture (x86 or x64).
$Architecture = (Get-WMIObject -Class Win32_ComputerSystem -ComputerName $Computer).SystemType
# Enable the Remote Registry service on the target computer. This allows keys to be read.
# Checks if computer is a Local Computer.
If($Computer -NotLike "*$env:computername*")
{
Set-Service -ComputerName $Computer -StartUpType Manual -Status Running `
-Name RemoteRegistry -DisplayName "Remote Registry"
}
# Array to hold the software information objects.
$ProgramsList = @()
# This is the HKLM Registry Hive on a target computer.
$HKLMBaseKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBasekey('LocalMachine', "$Computer")
# Get the Uninstall keys for the 64-bit software installed on the target computer.
# This key will get the x86 software from 32-bit machines.
# $False specifies that the key cannot be modified and is Read-Only.
$UninstallKeyx64 = $HKLMBaseKey.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall", $False)
$UninstallListx64 = $UninstallKeyx64.GetSubKeyNames()
# Get the Uninstall keys for the 32-bit software installed on 64-bit computers.
# $False specifies that the key cannot be modified and is Read-Only.
$UninstallKeyx86 = $HKLMBaseKey.OpenSubKey("SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall", $False)
$UninstallListx86 = $UninstallKeyx86.GetSubKeyNames()
# For 64-bit machines get all the software from the 32-bit and 64-bit portions of the registry.
If($Architecture -Like "*x64*")
{
[int]$x64Count = $UninstallListx64.Count
[int]$x86Count = $UninstallListx86.Count
[int]$Total = $UninstallListx64.Count + $UninstallListx86.Count
[int]$PCount = 0
# Extract desired information from the 64-bit Uninstall registry keys.
Foreach($Key in $UninstallListx64)
{
$Value = $HKLMBaseKey.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\$Key", $False)
# Skip keys that have no values in them.
If($Value.GetValue("DisplayName") -Like "")
{
$PCount++
continue
}
# Skip keys that are just Windows Updates.
ElseIf($Value.GetValue("DisplayName") -Like "*(KB*)*")
{
$PCount++
continue
}
Else
{
$PCount++
$Name = $Value.GetValue("DisplayName")
Write-Progress -Activity "Software" -Status "Found [$PCount / $Total]: $Name" `
-PercentComplete (($PCount / $Total) * 100) -Id 2
$ObjProgram = New-Object PSObject
$ObjProgram | Add-Member -MemberType NoteProperty -Name ComputerName -Value $CompName
$ObjProgram | Add-Member -MemberType NoteProperty -Name RegistryKey -Value $Key
$ObjProgram | Add-Member -MemberType NoteProperty -Name DisplayName -Value $Value.GetValue("DisplayName")
$ObjProgram | Add-Member -MemberType NoteProperty -Name Publisher -Value $Value.GetValue("Publisher")
$ObjProgram | Add-Member -MemberType NoteProperty -Name InstallDate -Value $Value.GetValue("InstallDate")
$ObjProgram | Add-Member -MemberType NoteProperty -Name Version -Value $Value.GetValue("DisplayVersion")
$ObjProgram | Add-Member -MemberType NoteProperty -Name Architecture -Value "x64"
$ObjProgram | Add-Member -MemberType NoteProperty -Name InstallSource -Value $Value.GetValue("InstallSource")
$ObjProgram | Add-Member -MemberType NoteProperty -Name UninstallString -Value $Value.GetValue("UninstallString")
# Add the Program Info to the array.
# This allows for easier use of Out-GridView.
$ProgramsList += $ObjProgram
}
}
# Extract desired information from the 32-bit Uninstall registry keys.
Foreach($Key in $UninstallListx86)
{
$Value = $HKLMBaseKey.OpenSubKey("SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\$Key", $False)
# Skip keys that have no values in them.
If($Value.GetValue("DisplayName") -Like "")
{
$PCount++
continue
}
# Skip keys that are just Windows Updates.
ElseIf($Value.GetValue("DisplayName") -Like "*(KB*)*")
{
$PCount++
continue
}
Else
{
$PCount++
$Name = $Value.GetValue("DisplayName")
Write-Progress -Activity "Software" -Status "Found [$PCount / $Total]: $Name" `
-PercentComplete (($PCount / $Total) * 100) -Id 2
$ObjProgram = New-Object PSObject
$ObjProgram | Add-Member -MemberType NoteProperty -Name ComputerName -Value $CompName
$ObjProgram | Add-Member -MemberType NoteProperty -Name RegistryKey -Value $Key
$ObjProgram | Add-Member -MemberType NoteProperty -Name DisplayName -Value $Value.GetValue("DisplayName")
$ObjProgram | Add-Member -MemberType NoteProperty -Name Publisher -Value $Value.GetValue("Publisher")
$ObjProgram | Add-Member -MemberType NoteProperty -Name InstallDate -Value $Value.GetValue("InstallDate")
$ObjProgram | Add-Member -MemberType NoteProperty -Name Version -Value $Value.GetValue("DisplayVersion")
$ObjProgram | Add-Member -MemberType NoteProperty -Name Architecture -Value "x86"
$ObjProgram | Add-Member -MemberType NoteProperty -Name InstallSource -Value $Value.GetValue("InstallSource")
$ObjProgram | Add-Member -MemberType NoteProperty -Name UninstallString -Value $Value.GetValue("UninstallString")
# Add the Program Info to the array.
# This allows for easier use of Out-GridView.
$ProgramsList += $ObjProgram
}
}
Write-Progress -Activity "Software" -Status "Complete" -Completed -Id 2
}
# For 32-bit machines get the software that is stored in the registry.
ElseIf($Architecture -Like "*x86*")
{
[int]$x86Count = $UninstallListx64.Count
[int]$PCount = 0
# Extract desired information from the 32-bit Uninstall registry keys.
Foreach($Key in $UninstallListx64)
{
$Value = $HKLMBaseKey.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\$Key", $False)
# Skip keys that have no values in them.
If($Value.GetValue("DisplayName") -Like "")
{
$PCount++
continue
}
# Skip keys that are just Windows Updates.
ElseIf($Value.GetValue("DisplayName") -Like "*(KB*)*")
{
$PCount++
continue
}
Else
{
$PCount++
$Name = $Value.GetValue("DisplayName")
Write-Progress -Activity "Software" -Status "Found [$PCount / $x86Count]: $Name" `
-PercentComplete (($PCount / $Total) * 100) -Id 2
$ObjProgram = New-Object PSObject
$ObjProgram | Add-Member -MemberType NoteProperty -Name ComputerName -Value $CompName
$ObjProgram | Add-Member -MemberType NoteProperty -Name RegistryKey -Value $Key
$ObjProgram | Add-Member -MemberType NoteProperty -Name DisplayName -Value $Value.GetValue("DisplayName")
$ObjProgram | Add-Member -MemberType NoteProperty -Name Publisher -Value $Value.GetValue("Publisher")
$ObjProgram | Add-Member -MemberType NoteProperty -Name InstallDate -Value $Value.GetValue("InstallDate")
$ObjProgram | Add-Member -MemberType NoteProperty -Name Version -Value $Value.GetValue("DisplayVersion")
$ObjProgram | Add-Member -MemberType NoteProperty -Name Architecture -Value "x86"
$ObjProgram | Add-Member -MemberType NoteProperty -Name InstallSource -Value $Value.GetValue("InstallSource")
$ObjProgram | Add-Member -MemberType NoteProperty -Name UninstallString -Value $Value.GetValue("UninstallString")
# Add the Program Info to the array.
# This allows for easier use of Out-GridView.
$ProgramsList += $ObjProgram
}
}
Write-Progress -Activity "Software" -Status "Complete" -Completed -Id 2
}
# Display the Information on screen sorted by "DisplayName".
$ProgramsList | Sort-Object -Property "DisplayName"
# Stop and Disable the Remote Registry service.
# Checks if computer is a Local Computer.
If($Computer -NotLike "*$env:computername*")
{
(Get-Service -ComputerName $Computer -Name RemoteRegistry).stop()
Set-Service -ComputerName $Computer -StartUpType Disabled -Name RemoteRegistry
}
Write-Progress -Activity "Installed Software Scan" -Status "Complete" -Completed -Id 1
}
}
1 Spice up
martin9700
(Martin9700)
2
Just so you know, Win32_Product is evil: Win32_Product Is Evil. | Greg's Systems Management Blog (and so is this 10 word minimum)
anthalus
(anthalus)
3
Win32 has a other really bad thing… it only displays you the Software that is installed as an MSI. So you couldn´t find other Software. If you take a look in a few other Scripts für Softwareinventory (like https://community.spiceworks.com/scripts/show/2170-get-a-list-of-installed-software-from-a-remote-computer-fast-as-lightning?page=3 ) you can see that this is realized with a Registryscan. It is very fast.
You are right, I have done some research and re-vamped this script to get more accurate information. It produces some duplicates, but it works better at least.