Public/Get-CertificateStoreReport.ps1

function Get-CertificateStoreReport {
    <#
    .SYNOPSIS
        Produces a comprehensive inventory of certificates in a store.
 
    .DESCRIPTION
        Enumerates every certificate in the specified store on local or remote
        computers and returns detailed metadata including key length, signature
        algorithm, private key presence, and an expiry finding.
 
        By default expired certificates are excluded from output; use
        -IncludeExpired to include them.
 
    .PARAMETER ComputerName
        One or more computer names to scan. Accepts pipeline input.
        Defaults to localhost.
 
    .PARAMETER StoreName
        Certificate store to inventory. Valid values: My, Root, CA,
        TrustedPeople. Defaults to My.
 
    .PARAMETER StoreLocation
        Certificate store location. Valid values: LocalMachine, CurrentUser.
        Defaults to LocalMachine.
 
    .PARAMETER IncludeExpired
        When specified, includes certificates whose NotAfter date has passed.
 
    .EXAMPLE
        Get-CertificateStoreReport
 
        Inventories all non-expired certificates in LocalMachine\My on the
        local machine.
 
    .EXAMPLE
        Get-CertificateStoreReport -StoreName Root -IncludeExpired
 
        Inventories the Trusted Root CA store including expired certificates.
 
    .EXAMPLE
        Get-CertificateStoreReport -ComputerName DC01 -StoreName CA
 
        Inventories the Intermediate CA store on DC01.
 
    .OUTPUTS
        PSCustomObject with properties: Subject, Issuer, Thumbprint, NotBefore,
        NotAfter, DaysRemaining, KeyLength, SignatureAlgorithm, HasPrivateKey,
        Store, Finding.
    #>

    [CmdletBinding()]
    param(
        [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [string[]]$ComputerName = @('localhost'),

        [Parameter()]
        [ValidateSet('My', 'Root', 'CA', 'TrustedPeople')]
        [string]$StoreName = 'My',

        [Parameter()]
        [ValidateSet('LocalMachine', 'CurrentUser')]
        [string]$StoreLocation = 'LocalMachine',

        [Parameter()]
        [switch]$IncludeExpired
    )

    begin {
        Write-Verbose "Get-CertificateStoreReport - $StoreLocation\$StoreName (IncludeExpired=$IncludeExpired)"

        $scriptBlock = {
            param($StoreName, $StoreLocation, $IncludeExpired)

            $storePath = "Cert:\$StoreLocation\$StoreName"
            if (-not (Test-Path $storePath)) {
                Write-Warning "Store $storePath does not exist on $env:COMPUTERNAME."
                return
            }

            $now = Get-Date

            Get-ChildItem -Path $storePath -ErrorAction SilentlyContinue | ForEach-Object {
                $cert = $_
                $daysRemaining = ($cert.NotAfter - $now).Days

                # Skip expired unless requested
                if ($cert.NotAfter -lt $now -and -not $IncludeExpired) { return }

                $finding = if ($cert.NotAfter -lt $now)      { 'EXPIRED'  }
                           elseif ($daysRemaining -le 7)     { 'CRITICAL' }
                           elseif ($daysRemaining -le 30)    { 'WARNING'  }
                           else                               { 'OK'       }

                # Extract key length from the public key
                $keyLength = 0
                try {
                    if ($cert.PublicKey.Key) {
                        $keyLength = $cert.PublicKey.Key.KeySize
                    }
                    elseif ($cert.PublicKey.EncodedKeyValue) {
                        $keyLength = $cert.PublicKey.EncodedKeyValue.RawData.Length * 8
                    }
                }
                catch { }

                # Signature algorithm friendly name
                $sigAlg = $cert.SignatureAlgorithm.FriendlyName

                [PSCustomObject]@{
                    Subject            = $cert.Subject
                    Issuer             = $cert.Issuer
                    Thumbprint         = $cert.Thumbprint
                    NotBefore          = $cert.NotBefore
                    NotAfter           = $cert.NotAfter
                    DaysRemaining      = $daysRemaining
                    KeyLength          = $keyLength
                    SignatureAlgorithm = $sigAlg
                    HasPrivateKey      = $cert.HasPrivateKey
                    Store              = "$StoreLocation\$StoreName"
                    Finding            = $finding
                }
            }
        }
    }

    process {
        foreach ($computer in $ComputerName) {
            Write-Verbose "Inventorying $StoreLocation\$StoreName on $computer"

            try {
                $isLocal = $computer -eq 'localhost' -or
                           $computer -eq $env:COMPUTERNAME -or
                           $computer -eq '.'

                if ($isLocal) {
                    $results = & $scriptBlock -StoreName $StoreName `
                                              -StoreLocation $StoreLocation `
                                              -IncludeExpired $IncludeExpired.IsPresent
                }
                else {
                    $results = Invoke-Command -ComputerName $computer `
                                              -ScriptBlock $scriptBlock `
                                              -ArgumentList $StoreName, $StoreLocation, $IncludeExpired.IsPresent `
                                              -ErrorAction Stop
                }

                if ($results) {
                    $results | ForEach-Object { $_ }
                }
                else {
                    Write-Verbose "No certificates returned from $computer."
                }
            }
            catch {
                Write-Warning "Failed to inventory ${computer}: $_"
            }
        }
    }

    end {
        Write-Verbose 'Get-CertificateStoreReport complete.'
    }
}