Private/Collectors.InstalledSoftware.ps1
|
function Get-ICInstalledSoftwareEvidence { [CmdletBinding()] param( [Parameter(Mandatory)] [object]$Context ) $warnings = New-Object System.Collections.ArrayList $files = New-Object System.Collections.ArrayList # Win32_Product is intentionally not queried: enumerating it triggers MSI # consistency checks that can reconfigure installed products on the host. $roots = New-Object System.Collections.ArrayList [void]$roots.Add([pscustomobject]@{ Path = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall'; Scope = 'Machine' }) [void]$roots.Add([pscustomobject]@{ Path = 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall'; Scope = 'Machine32' }) try { foreach ($hive in Get-ChildItem -LiteralPath 'Registry::HKEY_USERS' -ErrorAction Stop) { $sidName = Split-Path -Leaf $hive.Name if ($sidName -match '_Classes$') { continue } [void]$roots.Add([pscustomobject]@{ Path = "Registry::HKEY_USERS\$sidName\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" Scope = "User:$sidName" }) } } catch { Add-ICCollectorWarning -List $warnings -Message "Loaded user registry hives: $($_.Exception.Message)" } $entries = New-Object System.Collections.ArrayList $machineEntries = 0 $userEntries = 0 foreach ($root in $roots) { if (-not (Test-Path -LiteralPath $root.Path)) { continue } try { foreach ($key in Get-ChildItem -LiteralPath $root.Path -ErrorAction Stop) { try { $properties = Get-ItemProperty -LiteralPath $key.PSPath -ErrorAction Stop $estimatedSize = Get-ICPropertyValue -InputObject $properties -Name 'EstimatedSize' [void]$entries.Add([pscustomobject][ordered]@{ Scope = [string]$root.Scope KeyName = Split-Path -Leaf $key.Name DisplayName = [string](Get-ICPropertyValue -InputObject $properties -Name 'DisplayName') DisplayVersion = [string](Get-ICPropertyValue -InputObject $properties -Name 'DisplayVersion') Publisher = [string](Get-ICPropertyValue -InputObject $properties -Name 'Publisher') InstallDate = [string](Get-ICPropertyValue -InputObject $properties -Name 'InstallDate') InstallLocation = [string](Get-ICPropertyValue -InputObject $properties -Name 'InstallLocation') InstallSource = [string](Get-ICPropertyValue -InputObject $properties -Name 'InstallSource') UninstallString = [string](Get-ICPropertyValue -InputObject $properties -Name 'UninstallString') QuietUninstallString = [string](Get-ICPropertyValue -InputObject $properties -Name 'QuietUninstallString') EstimatedSizeKB = if ($null -ne $estimatedSize) { [int64]$estimatedSize } else { $null } SystemComponent = [bool]([int](Get-ICPropertyValue -InputObject $properties -Name 'SystemComponent' -Default 0) -eq 1) WindowsInstaller = [bool]([int](Get-ICPropertyValue -InputObject $properties -Name 'WindowsInstaller' -Default 0) -eq 1) RegistryKey = [string]$key.Name }) if ($root.Scope -like 'User:*') { $userEntries++ } else { $machineEntries++ } } catch { Add-ICCollectorWarning -List $warnings -Message "Uninstall entry '$($key.Name)': $($_.Exception.Message)" } } } catch { Add-ICCollectorWarning -List $warnings -Message "Uninstall inventory '$($root.Path)': $($_.Exception.Message)" } } $sorted = @($entries | Sort-Object Scope, DisplayName, KeyName) Add-ICOutputFiles -List $files -Path (Export-ICCollectorData -Context $Context -Collector InstalledSoftware -RelativePath 'evidence/software/installed-software.json' -Data $sorted -Csv) return New-ICCollectorResultData -OutputFiles @($files) -Warnings @($warnings) -Metrics ([ordered]@{ InstalledSoftwareEntries = $sorted.Count MachineScopeEntries = $machineEntries UserScopeEntries = $userEntries }) } |