Public/Get-InstalledApplications.ps1
|
function Get-InstalledApplications { <# .SYNOPSIS Retrieve installed applications from the Windows registry. .DESCRIPTION Queries the Windows registry uninstall keys to find installed applications. Supports multiple scopes: global (HKLM), current user, all users, or combinations. Querying all users requires administrative privileges to mount offline user hives. .PARAMETER Scope The scope of applications to query. Defaults to 'GlobalAndAllUsers'. Valid values: Global, GlobalAndCurrentUser, GlobalAndAllUsers, CurrentUser, AllUsers. .EXAMPLE Get-InstalledApplications Returns all installed applications (global + all users). Requires elevation. .EXAMPLE Get-InstalledApplications -Scope Global Returns only machine-wide installed applications. .EXAMPLE Get-InstalledApplications -Scope CurrentUser Returns applications installed for the current user only. #> [CmdletBinding(SupportsShouldProcess)] param( [ValidateSet('Global', 'GlobalAndCurrentUser', 'GlobalAndAllUsers', 'CurrentUser', 'AllUsers')] [string]$Scope = 'GlobalAndAllUsers' ) # Check if running with Administrative privileges if required if ($Scope -in @('GlobalAndAllUsers', 'AllUsers')) { if (-not (Test-IsElevated)) { throw 'Finding all user applications requires an elevated (Administrator) PowerShell session.' } } # Empty array to store applications $Apps = [System.Collections.Generic.List[PSObject]]::new() $32BitPath = "SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" $64BitPath = "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*" # Retrieve globally installed applications if ($Scope -in @('Global', 'GlobalAndAllUsers', 'GlobalAndCurrentUser')) { Write-Verbose "Processing global hive" Get-ItemProperty "HKLM:\$32BitPath" | ForEach-Object { $Apps.Add($_) } Get-ItemProperty "HKLM:\$64BitPath" | ForEach-Object { $Apps.Add($_) } } if ($Scope -in @('CurrentUser', 'GlobalAndCurrentUser')) { Write-Verbose "Processing current user hive" Get-ItemProperty "Registry::\HKEY_CURRENT_USER\$32BitPath" | ForEach-Object { $Apps.Add($_) } Get-ItemProperty "Registry::\HKEY_CURRENT_USER\$64BitPath" | ForEach-Object { $Apps.Add($_) } } if ($Scope -in @('AllUsers', 'GlobalAndAllUsers')) { Write-Verbose "Collecting hive data for all users" $AllProfiles = Get-CimInstance Win32_UserProfile | Select-Object LocalPath, SID, Loaded, Special | Where-Object {$_.SID -like "S-1-5-21-*"} $MountedProfiles = $AllProfiles | Where-Object {$_.Loaded -eq $true} $UnmountedProfiles = $AllProfiles | Where-Object {$_.Loaded -eq $false} Write-Verbose "Processing mounted hives" $MountedProfiles | ForEach-Object { Get-ItemProperty -Path "Registry::\HKEY_USERS\$($_.SID)\$32BitPath" | ForEach-Object { $Apps.Add($_) } Get-ItemProperty -Path "Registry::\HKEY_USERS\$($_.SID)\$64BitPath" | ForEach-Object { $Apps.Add($_) } } Write-Verbose "Processing unmounted hives" $UnmountedProfiles | ForEach-Object { $Hive = "$($_.LocalPath)\NTUSER.DAT" Write-Verbose " -> Mounting hive at $Hive" if (Test-Path $Hive) { REG LOAD HKU\temp $Hive Get-ItemProperty -Path "Registry::\HKEY_USERS\temp\$32BitPath" | ForEach-Object { $Apps.Add($_) } Get-ItemProperty -Path "Registry::\HKEY_USERS\temp\$64BitPath" | ForEach-Object { $Apps.Add($_) } # Run manual GC to allow hive to be unmounted [GC]::Collect() [GC]::WaitForPendingFinalizers() REG UNLOAD HKU\temp } else { Write-Warning "Unable to access registry hive at $Hive" } } } Write-Output $Apps } |