How to create a Software Inventory Report using powershell (step by step)
How to create a Software Inventory Report using powershell (step by step)
There are different ways to obtain the list of the installed software on a computer, usually are wmi query using the Win32_Product class, but therea ar an alternative: read directly the registry
Get the software installed on the local computer
Open a powershell as administrator
Paste the following code: Get-CimInstance -Class Win32_Product
The first thing you realize, the command give you a lot of information and is very slow
These are the fields returned br the command for an installed software
Name : PowerShell 7-x64
Version : 7.1.3.0
InstallState : 5
Caption : PowerShell 7-x64
Description : PowerShell 7-x64
IdentifyingNumber : {A6307460-5CB8-47E2-91FE-A35552EA2C39}
SKUNumber :
Vendor : Microsoft Corporation
AssignmentType : 1
HelpLink : https://github.com/PowerShell/PowerShell
HelpTelephone :
InstallDate : 20210319
InstallDate2 :
InstallLocation :
InstallSource : C:\Users\gas\Downloads\
Language : 1033
LocalPackage : C:\WINDOWS\Installer\11b2728f.msi
PackageCache : C:\WINDOWS\Installer\11b2728f.msi
PackageCode : {6488FC90-3CAB-4246-A212-1AD7BDE685F7}
PackageName : PowerShell-7.1.3-win-x64.msi
ProductID :
RegCompany :
RegOwner :
Transforms :
URLInfoAbout :
URLUpdateInfo :
WordCount : 0
PSComputerName :
CimClass : root/cimv2:Win32_Product
CimInstanceProperties : {Caption, Description, IdentifyingNumber, Name…}
CimSystemProperties : Microsoft.Management.Infrastructure.CimSystemProperties
We don’t want all information, but only some fields…
Now we select only the info wanted
Name,Version,Caption,IdentifyingNumber,Vendor,description
Paste the following code: Get-CimInstance -Class Win32_Product|select-object Name,Version,Caption,IdentifyingNumber,Vendor,description,installState
And this is the output
Some note about the field InstallState, should be useful
-6 |
Bad Configuration |
-2 |
Invalid Argument |
-1 |
Unknown Package |
1 |
Advertised |
2 |
Absent |
5 |
Installed |
Now we try to find the software installed on a remote computer, returning only the fields wanted
Get-CimInstance -Class Win32_Product -ComputerName RemotePcName|select-object Name,Version,Caption,IdentifyingNumber,Vendor,description,installState,PScomputerName
Note: if you get some error, probably you don’t have the permission on the remote computer or the firewall block the request, you can do some troubleshooting using WBEMTest.exe or read this link /en-us/windows/win32/wmisdk/connecting-to-wmi-remotely-starting-with-vista
Now we try to query more computers
‘server1’,’server2’,’pc01’| % {Get-CimInstance -Class Win32_Product -ComputerName $_}|select-object Name,Version,Caption,IdentifyingNumber,Vendor,description,installState,PScomputerName
If you are on a server and have Active directory cmdlets, this command will return all computer that have MyApp installed on a specific OU
$OUpath = 'OU=test,DC=contoso,DC=com'
Get-ADComputer -Filter * -SearchBase $OUpath |
ForEach-Object {
Get-CimInstance -Class Win32_Product -computername $_.Name |
Where-Object {$_.Name -like "*MyApp*"} }
CSV report creation
# Make Report folder
Mkdir c:\report # Local PC Software Inventory Report creation
Get-CimInstance -Class Win32_Product |
select-object Name , Version , Caption , IdentifyingNumber , Vendor , description , installState , PScomputerName |
Export-Csv -UseCulture -NoTypeInformation -LiteralPath c:\Report\LocalInventorySwReport.csv
$OUpath = 'OU=test,DC=contoso,DC=com'
Get-ADComputer -Filter * -SearchBase $OUpath |
ForEach-Object {
Get-CimInstance -Class Win32_Product -computername $_.Name |
Where-Object {$_.Name -like "*MyApp*"}|
select-object Name,Version,Caption,IdentifyingNumber,Vendor,description,installState,PScomputerName |
Export-Csv -UseCulture -NoTypeInformation -LiteralPath c:\Report\OUInventorySwReport.csv
}
Measures the time it takes to run our script
In this example measures the time it takes to run the Get-CimInstance -Class Win32_Product command that gets the software installed on the local computer
Paste the following code in the powershell windows : Measure-Command {Get-CimInstance -Class Win32_Product}
The output:
Days : 0
Hours : 0
Minutes : 0
Seconds : 27
Milliseconds : 959
Ticks : 279596631
TotalDays : 0,000323607211805556
TotalHours : 0,00776657308333333
TotalMinutes : 0,465994385
TotalSeconds : 27,9596631
TotalMilliseconds : 27959,6631
We want to be faster
The Win32_product class is not query optimized. Queries such as “select * from Win32_Product where (name like ‘Sniffer%’)” require WMI to use the MSI provider to enumerate all of the installed products and then parse the full list sequentially to handle the “where” clause…
https://devblogs.microsoft.com/scripting/use-powershell-to-quickly-find-installed-software/
The solution is read the registry
function Get-installedApps {
param( $computers=$env:computername )
$array = @()
foreach($pc in $computers){
$computername=$pc
#Define the variable to hold the location of Currently Installed Programs
$unistalKeys=@("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall","SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall")
$unistalKeys| ForEach-Object{
$UninstallKey=$_
#Create an instance of the Registry Object and open the HKLM base key
$reg=[microsoft.win32.registrykey]::OpenRemoteBaseKey(‘LocalMachine’,$computername)
#Drill down into the Uninstall key using the OpenSubKey Method
$regkey=$reg.OpenSubKey($UninstallKey)
#Retrieve an array of string that contain all the subkey names
$subkeys=$regkey.GetSubKeyNames()
#Open each Subkey and use GetValue Method to return the required values for each
foreach($key in $subkeys){
$thisKey=$UninstallKey+”\\���+$key
$thisSubKey=$reg.OpenSubKey($thisKey)
$obj = New-Object PSObject
$obj | Add-Member -MemberType NoteProperty -Name “ComputerName” -Value ($computername).ToUpper()
$obj | Add-Member -MemberType NoteProperty -Name “DisplayName” -Value $($thisSubKey.GetValue(“DisplayName”))
$obj | Add-Member -MemberType NoteProperty -Name “DisplayVersion” -Value $($thisSubKey.GetValue(“DisplayVersion”))
$obj | Add-Member -MemberType NoteProperty -Name “InstallLocation” -Value $($thisSubKey.GetValue(“InstallLocation”))
$obj | Add-Member -MemberType NoteProperty -Name “Publisher” -Value $($thisSubKey.GetValue(“Publisher”))
$array += $obj
}
}
}
$array |sort-object -Property displayname| Select-Object * -unique
}
Get-installedApps
# GET DURATION
measure-comman {Get-installedApps}
Ticks : 4154470
Days : 0
Hours : 0
Milliseconds : 415
Minutes : 0
Seconds : 0
TotalDays : 4,80841435185185E-06
TotalHours : 0,000115401944444444
TotalMilliseconds : 415,447
TotalMinutes : 0,00692411666666667
TotalSeconds : 0,415447
Fast! From 27 seconds to 0.4,
Fast Software Inventory Report creation
Using the procedure above Get-installedApps report generation is easy
New-Item -Path "c:\ -Name "report" -ItemType "directory"
$report='c:\Report\OUInventorySwReport.csv'
Get-installedApps| Export-Csv -LiteralPath $report -UseCulture -NoTypeInformation
# Search computer with the software *eye* installed
Get-installedApps -computers 'server1',’server2’|Where-Object {$_.DisplayName -like "*eye*"}
References
https://devblogs.microsoft.com/scripting/use-powershell-to-quickly-find-installed-software/