# InventorySoftware.ps1
# Generates a software inventory report for Windows 10/11 (local machine) # 1) Collect desktop/Store uninstall entries
$registryPaths = @( "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*", "HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*", "HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*") $softwareFromRegistry = foreach ($path in $registryPaths) { if (Test-Path $path) { Get-ItemProperty -Path $path -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName -and $_.DisplayName -ne "" -and ($_.SystemComponent -ne 1) } | Select-Object DisplayName, DisplayVersion, Publisher, InstallDate }
} # 2) Collect Windows Store (Appx) apps for All Users
try { $storeApps = Get-AppxPackage -AllUsers | ForEach-Object { [pscustomobject]@{ DisplayName = $_.Name DisplayVersion = $_.Version Publisher = $_.Publisher InstallDate = "" # Appx doesn't always expose a clean InstallDate here } }
} catch { $storeApps = @
} # 3) Normalize and combine results
$combined = @ foreach ($row in $softwareFromRegistry) { $combined += [pscustomobject]@{ DisplayName = $row.DisplayName DisplayVersion = $row.DisplayVersion Publisher = $row.Publisher InstallDate = if ([string]::IsNullOrWhiteSpace($row.InstallDate) { "" } else { $row.InstallDate } }
} # Append Appx results, avoiding duplicates by DisplayName
foreach ($app in $storeApps) { $exists = $combined | Where-Object { $_.DisplayName -eq $app.DisplayName } if (-not $exists) { $combined += [pscustomobject]@{ DisplayName = $app.DisplayName DisplayVersion = $app.DisplayVersion Publisher = $app.Publisher InstallDate = $app.InstallDate } }
} # 4) Optional: Clean up InstallDate into a friendlier format if possible
$formatted = $combined | ForEach-Object { $inst = $_.InstallDate if ($inst -and $inst -match '^\d{8}$') { try { $_ | Add-Member -NotePropertyName InstallDateFriendly -NotePropertyValue:([datetime]::ParseExact($inst, 'yyyyMMdd', $null).ToShortDateString -Force } catch { $_ | Add-Member -NotePropertyName InstallDateFriendly -NotePropertyValue $inst -Force } } elseif ($inst) { $_ | Add-Member -NotePropertyName InstallDateFriendly -NotePropertyValue $inst -Force } else { $_ | Add-Member -NotePropertyName InstallDateFriendly -NotePropertyValue "" -Force } $_
} # 5) Export reports
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
$docPath = "$env:USERPROFILE\Documents\SoftwareInventory_$timestamp" # Create directory if needed
New-Item -Path $docPath -ItemType Directory -Force | Out-Null $csvPath = "$docPath\SoftwareInventory.csv"
$htmlPath = "$docPath\SoftwareInventory.html" $formatted | Sort-Object Publisher, DisplayName | Export-Csv -Path $csvPath -NoTypeInformation
$formatted | Sort-Object Publisher, DisplayName | Select-Object DisplayName, DisplayVersion, Publisher, InstallDateFriendly | ConvertTo-Html -Title "Software Inventory Report" -PreContent "<h2>Software Inventory Report</h2><p>Generated: $(Get-Date)</p>" | Out-File -FilePath $htmlPath -Encoding UTF8 Write-Host "Inventory reports generated:"
Write-Host "CSV: $csvPath"
Write-Host "HTML: $htmlPath"