Public/Get-InstalledSoftware.ps1

Function Get-InstalledSoftware {
    <#
    .SYNOPSIS
        Retrieves a list of installed software from the Windows registry.
    .DESCRIPTION
        This function queries the Windows registry to retrieve information about installed software.
        It can filter results based on display name, include system components, and include updates.
    .PARAMETER DisplayName
        Specifies a wildcard pattern to filter the display names of installed software.
        Default is "*", which matches all software.
    .PARAMETER IncludeSystemComponents
        If specified, includes system components in the results.
    .PARAMETER IncludeUpdates
        If specified, includes updates in the results.
    .INPUTS
        None. This function does not accept pipeline input.
    .OUTPUTS
        System.Collections.Generic.List[PSCustomObject]
        Returns a list of custom objects representing installed software, with properties such as:
        DisplayName, DisplayVersion, Publisher, InstallDate, InstallLocation, UninstallString, QuietUninstallString,
        EstimatedSizeMB, RegistryPath, Architecture, Source, and PSTypeName.
    .EXAMPLE
        PS> Get-InstalledSoftware -DisplayName "Microsoft*" -IncludeSystemComponents
        Retrieves all installed software with display names starting with "Microsoft", including system components.
    .EXAMPLE
        PS> Get-InstalledSoftware -DisplayName "Visual Studio*" -IncludeUpdates
        Retrieves all installed software with display names starting with "Visual Studio", including updates.
    .NOTES
        This function is designed to work on Windows systems and requires appropriate permissions to read the registry.
        It may not return results for software installed by non-standard methods or in non-standard locations.
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $false, Position = 0, ValueFromPipelineByPropertyName = $true)]
        [string]$DisplayName = "*",
        [Parameter(Mandatory = $false)]
        [switch]$IncludeSystemComponents,
        [Parameter(Mandatory = $false)]
        [switch]$IncludeUpdates
    )

    Begin {
        Write-Verbose "[BEGIN]: Get-InstalledSoftware"
        $UninstallPaths = @(
            "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall",
            "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall",
            "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"
        )
    }

    Process {
        Write-Verbose "[PROCESS]: Get-InstalledSoftware"
        $Results = foreach ($Path in $UninstallPaths) {
            try {
                $Items = Get-ItemProperty "$Path\*" -ErrorAction SilentlyContinue

                foreach ($Item in $Items) {
                    if ($Item.DisplayName -and $Item.DisplayName -like $DisplayName) {
                        # Filter out system components and updates if not requested
                        $SystemComponent = $Item.SystemComponent
                        $ParentKeyName = $Item.ParentKeyName
                        $IsUpdate = $Item.DisplayName -match 'Update|Hotfix|KB\d+'

                        if ((-not $SystemComponent -or $IncludeSystemComponents) -and
                            (-not $ParentKeyName -or $IncludeSystemComponents) -and
                            (-not $IsUpdate -or $IncludeUpdates)) {

                            [PSCustomObject]@{
                                DisplayName          = $Item.DisplayName
                                DisplayVersion       = $Item.DisplayVersion
                                Publisher            = $Item.Publisher
                                InstallDate          = if ($Item.InstallDate) {
                                    try { [DateTime]::ParseExact($Item.InstallDate, 'yyyyMMdd', $null).ToString('dd.MM.yyyy') }
                                    catch { $Item.InstallDate }
                                } else { $null }
                                InstallLocation      = $Item.InstallLocation
                                UninstallString      = $Item.UninstallString
                                QuietUninstallString = $Item.QuietUninstallString
                                EstimatedSizeMB      = if ($Item.EstimatedSize) {
                                    [math]::Round($Item.EstimatedSize / 1024, 2)
                                } else { $null }
                                RegistryPath         = $Item.PSPath
                                Architecture         = if ($Path -like '*WOW6432Node*') { 'x86' } elseif ($Path -like '*HKCU*') { 'User' } else { 'x64' }
                                Source               = 'Registry'
                                PSTypeName           = 'PSSystemDiagnostics.InstalledSoftware'
                            }
                        }
                    }
                }
            } catch {
                Write-Warning "Failed to query $Path`: $_"
            }
        }
    }

    End {
        Write-Verbose "[END]: Get-InstalledSoftware"
        if ($Results) {
            $Results | Sort-Object DisplayName
        } else {
            Write-Warning "No installed software found matching criteria."
        }
        Write-Verbose "Total items found: $($Results.Count)"
        if ($Results.Count -eq 0) {
            Write-Warning "No installed software found matching criteria."
        }
        Write-Verbose "Get-InstalledSoftware completed."
        return $Results
    }
}