What you are asking for used to be done with Get-CimInstance
, but that comes at a cost and as you pointed out is no longer accurate. It used to be a WMI command. Now CIM. It is not a fast command, more on that later.
Get-CimInstance win32_product
The cost is Get-CimInstance
can return incomplete data. It also runs a consistency check on all applications and performs automatic and silent repairs. Yes, when you run this command, it automatically runs a consistency check on all applications and performs automatic and silent repairs. That is why this simple command is so slow to report back.
Microsoft's documentation on this: Link Here
So we do not use that anymore, now what? What you are looking for is gathering the information from both 32 and 64 bit installers that looks like this and must be done, I do it first, always.
$installedApplications = @()
$installedApplications+= Get-ItemProperty "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" # 32 Bit
$installedApplications+= Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*" # 64 Bit
One caveat to the above is that the above method will return a ton more elements than the Win32_Product
command does. It will have things such as Service Packs, Office Updates, Language Packs, etc. You will most likely need to filter out things you aren’t interested in, though you shouldn't have an issue using PowerShell to filter results.
To complete the answer to your question, how do you quantify data from installers regardless of their install location? Specifically finding the userprofile\AppData
install information. The good news is these applications have their installation information documented in the registry as well, under HKEY_CURRENT_USER
instead of HKEY_LOCAL_MACHINE
. What this means is every user's install location information is sitting in the registry hive under their profile, for instance c:\users\inet\NTUSER.DAT
.
What else needs to be said about this;
- If a user is logged in this can be accessed by any other admin user on the system by using the
HKEY_USERS\$ACCOUNT_SID
key.
- If a user is not logged in, the hive can be manually mounted using
REG LOAD
- If a user's registry hive is already loaded it cannot be loaded a second time and will give you the obligatory "this file is being used by another process."
So how do we get what you are asking for, which is to replicate the "full install data" for the device, and not just what's in the 32 and 64bit directories?
This is what I use, if one so chooses you can pipe the info to CSV/HTML/etc...
$installedApplications = @()
$installedApplications+= Get-ItemProperty "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*"
$installedApplications+= Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*"
$32BitPath = "SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*"
$64BitPath = "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*"
$tigerStripes= Get-CimInstance Win32_UserProfile | Select LocalPath, SID, Loaded, Special | Where {$_.SID -like "S-1-5-21-*"}
$unavailableProfiles = $tigerStripes| Where {$_.Loaded -eq $true}
$availableProfiles = $tigerStripes| Where {$_.Loaded -eq $false}
#Mounted
$unavailableProfiles | % {
$installedApplications += Get-ItemProperty -Path "Registry::\HKEY_USERS\$($_.SID)\$32BitPath"
$installedApplications += Get-ItemProperty -Path "Registry::\HKEY_USERS\$($_.SID)\$64BitPath"
}
#Unmounted
$availableProfiles | % {
#Mount Hive
$Hive = "$($_.LocalPath)\NTUSER.DAT"
if (Test-Path $Hive) {
REG LOAD HKU\temp $Hive
$installedApplications += Get-ItemProperty -Path "Registry::\HKEY_USERS\temp\$32BitPath"
$installedApplications += Get-ItemProperty -Path "Registry::\HKEY_USERS\temp\$64BitPath"
#This lets the hive be unmounted, using a manual Get-Content
[GC]::Collect()
[GC]::WaitForPendingFinalizers()
REG UNLOAD HKU\temp
} else {
Write-Warning "Unable to access registry at $Hive"
}
}
$installedApplications