Skip to content

Instantly share code, notes, and snippets.

@Sid110307
Created August 12, 2025 08:15
Show Gist options
  • Select an option

  • Save Sid110307/7c17053be108620ca83b7ef734781139 to your computer and use it in GitHub Desktop.

Select an option

Save Sid110307/7c17053be108620ca83b7ef734781139 to your computer and use it in GitHub Desktop.
List all installed apps, programs, features, etc.
[CmdletBinding()]
param()
function Test-Cmd { param([string]$Name) [bool](Get-Command $Name -ErrorAction SilentlyContinue) }
function Test-Try { param([scriptblock]$Block) try { & $Block } catch { } }
$results = New-Object System.Collections.Generic.List[object]
$seenKeys = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)
$dupeCount = 0
function New-DupeKey {
param([string]$Source, [string]$Name, [string]$Version, [string]$Identifier)
if (-not [string]::IsNullOrWhiteSpace($Identifier)) { return "id::$Identifier" }
if (-not [string]::IsNullOrWhiteSpace($Version)) { return "nv::$Name|$Version" }
if (-not [string]::IsNullOrWhiteSpace($Name)) { return "n::$Name" }
return "src::$Source::(blank)"
}
function Add-Item {
param(
[string]$Source, [string]$Name, [string]$Version, [string]$Publisher,
[string]$InstallDate, [string]$Location, [string]$Identifier, [hashtable]$Extra
)
$key = New-DupeKey -Source $Source -Name $Name -Version $Version -Identifier $Identifier
if (-not $seenKeys.Add($key)) {
$script:dupeCount++
return
}
$obj = [PSCustomObject]@{
Source = $Source
Name = $Name
Version = $Version
Publisher = $Publisher
InstallDate = $InstallDate
Location = $Location
Identifier = $Identifier
Extra = if ($Extra) { $Extra } else { $null }
}
[void]$results.Add($obj)
}
Write-Output "=== Inventory started $(Get-Date) ==="
Write-Output "PS Version: $($PSVersionTable.PSVersion)"
# -------- 1) Win32 apps via Registry --------
function Get-UninstallKeyItems {
param([string[]]$Roots)
foreach ($root in $Roots) {
if (Test-Path $root) {
Get-ChildItem $root -ErrorAction SilentlyContinue | ForEach-Object {
$p = Get-ItemProperty $_.PsPath -ErrorAction SilentlyContinue
if ($p.DisplayName -and ($p.SystemComponent -ne 1) -and ($p.ReleaseType -ne 'Update') -and ($p.ParentKeyName -ne 'OperatingSystem')) {
$arguments = @{
Source = "Win32-Registry"
Name = $p.DisplayName
Version = $p.DisplayVersion
Publisher = $p.Publisher
InstallDate = $p.InstallDate
Location = $p.InstallLocation
Identifier = $p.PSChildName
Extra = @{
UninstallString = $p.UninstallString
InstallSource = $p.InstallSource
URLInfoAbout = $p.URLInfoAbout
}
}
Add-Item @arguments
}
}
}
}
}
Write-Output "[1/6] Registry..."
Get-UninstallKeyItems -Roots @(
'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall',
'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall',
'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall'
)
# -------- 2) Appx / Microsoft Store apps --------
Write-Output "[2/6] Appx..."
Test-Try { Get-AppxPackage -AllUsers -ErrorAction SilentlyContinue } | ForEach-Object {
Add-Item -Source "Appx" -Name $_.Name -Version $_.Version -Publisher $_.Publisher `
-InstallDate $null -Location $_.InstallLocation -Identifier $_.PackageFullName `
-Extra @{ Architecture = $_.Architecture; IsFramework = $_.IsFramework }
}
# -------- 3) Windows Optional Features --------
Write-Output "[3/6] Windows Optional Features..."
Test-Try { Get-WindowsOptionalFeature -Online } |
Where-Object State -eq 'Enabled' |
ForEach-Object {
Add-Item -Source "WindowsFeature" -Name $_.FeatureName -Version $null -Publisher $null `
-InstallDate $null -Location $null -Identifier $_.FeatureName -Extra @{ State = $_.State }
}
# -------- 4) Windows Capabilities --------
Write-Output "[4/6] Windows Capabilities..."
Test-Try { Get-WindowsCapability -Online } |
Where-Object State -eq 'Installed' |
ForEach-Object {
Add-Item -Source "WindowsCapability" -Name $_.Name -Version $_.Version -Publisher $null `
-InstallDate $null -Location $null -Identifier $_.Name -Extra @{ State = $_.State }
}
# -------- 5) WinGet packages --------
Write-Output "[5/6] WinGet..."
if (Test-Cmd "winget") {
$wg = Test-Try { winget list --accept-source-agreements --output json 2>$null | ConvertFrom-Json }
if ($wg) {
foreach ($p in $wg) {
Add-Item -Source "WinGet" -Name $p.Name -Version $p.Version -Publisher $p.Source `
-InstallDate $null -Location $null -Identifier $p.Id `
-Extra @{ Available = $p.Available; Scope = $p.Scope }
}
}
else {
winget list --accept-source-agreements 2>$null |
Select-Object -Skip 2 | ForEach-Object {
$line = ($_ -split '\s{2,}', 4)
if ($line.Length -ge 2) {
Add-Item -Source "WinGet" -Name $line[0] -Version $line[1] -Publisher $null `
-InstallDate $null -Location $null -Identifier $line[2] -Extra @{}
}
}
}
}
else { Write-Output " WinGet not found." }
# -------- 6) Chocolatey packages --------
Write-Output "[6/6] Chocolatey..."
if (Test-Cmd "choco") {
Test-Try { choco list -lo --limit-output --no-color --source="'Chocolatey'" --format=json 2>$null | ConvertFrom-Json } |
ForEach-Object {
foreach ($p in $_.packages) {
Add-Item -Source "Chocolatey" -Name $p.title -Version $p.version -Publisher "Chocolatey" `
-InstallDate $null -Location $null -Identifier $p.id -Extra @{}
}
}
}
else { Write-Output " Chocolatey not found." }
Write-Output "[OUTPUT] Writing Excel workbook..."
if (-not (Get-Module -ListAvailable -Name ImportExcel)) {
try { Import-Module ImportExcel -ErrorAction Stop }
catch {
Write-Warning "✖ The 'ImportExcel' module is required. Install with: Install-Module ImportExcel -Scope CurrentUser"
return
}
}
$stamp = Get-Date -Format "yyyyMMdd_HHmmss"
$baseXlsx = Join-Path (Get-Location) "inventory_$stamp.xlsx"
function ConvertTo-Row {
param($x)
[PSCustomObject]@{
Source = $x.Source
Name = $x.Name
Version = $x.Version
Publisher = $x.Publisher
InstallDate = $x.InstallDate
Location = $x.Location
Identifier = $x.Identifier
ExtraJson = if ($x.Extra) { $x.Extra | ConvertTo-Json -Depth 6 -Compress } else { $null }
}
}
$allRows = $results | ForEach-Object { ConvertTo-Row $_ }
$xl = $allRows | Export-Excel -Path $baseXlsx -WorksheetName 'All' -TableName 'All' `
-AutoSize -AutoFilter -FreezeTopRow -BoldTopRow -ClearSheet -PassThru
$wsAll = $xl.Workbook.Worksheets["All"]
Set-ExcelRange -Worksheet $wsAll -Range $wsAll.Dimension.Address -HorizontalAlignment Left
$allRows | Group-Object Source | ForEach-Object {
$sheetName = ($_.Name -replace '[^\w\.-]', '_')
if ($sheetName.Length -gt 31) { $sheetName = $sheetName.Substring(0, 31) }
$tableName = 'T_' + ($sheetName -replace '[^\w]', '_')
if ($tableName.Length -gt 31) { $tableName = $tableName.Substring(0, 31) }
$_.Group | Export-Excel -ExcelPackage $xl -WorksheetName $sheetName -TableName $tableName `
-AutoSize -AutoFilter -FreezeTopRow -BoldTopRow -ClearSheet -PassThru | Out-Null
$ws = $xl.Workbook.Worksheets[$sheetName]
Set-ExcelRange -Worksheet $ws -Range $ws.Dimension.Address -HorizontalAlignment Left
}
Close-ExcelPackage -ExcelPackage $xl
$results | Group-Object Source | Sort-Object Name | Format-Table Name, Count -AutoSize
Write-Output "Done. Workbook created at: $baseXlsx"
Write-Output "=== Inventory completed $(Get-Date) ==="
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment