Public/Get-ExpiringCertificates.ps1

function Get-ExpiringCertificates {
    <#
    .SYNOPSIS
        Scans certificate stores for expiring or expired certificates.
 
    .DESCRIPTION
        Queries the specified certificate store on local or remote computers and
        returns certificates that are expired, expiring within a critical window,
        expiring within a warning window, or healthy. Each certificate is tagged
        with a Finding (EXPIRED, CRITICAL, WARNING, OK) based on its remaining
        lifetime relative to -DaysUntilExpiration and a seven-day critical threshold.
 
        For remote computers the function uses Invoke-Command over WinRM. For
        localhost it reads the cert:\ PSDrive directly, avoiding unnecessary
        remoting overhead.
 
    .PARAMETER ComputerName
        One or more computer names to scan. Accepts pipeline input.
        Defaults to localhost.
 
    .PARAMETER StoreName
        Certificate store name to scan. Defaults to "My" (Personal).
 
    .PARAMETER StoreLocation
        Certificate store location. Defaults to LocalMachine.
 
    .PARAMETER DaysUntilExpiration
        Warning threshold in days. Certificates expiring within this window are
        flagged WARNING; those within 7 days are flagged CRITICAL; those already
        past NotAfter are flagged EXPIRED. Defaults to 30.
 
    .EXAMPLE
        Get-ExpiringCertificates
 
        Scans the local machine Personal store for certificates expiring within
        30 days.
 
    .EXAMPLE
        Get-ExpiringCertificates -ComputerName Server01,Server02 -DaysUntilExpiration 60
 
        Scans two remote servers with a 60-day warning window.
 
    .EXAMPLE
        'Web01','Web02' | Get-ExpiringCertificates -StoreName Root
 
        Pipeline input to scan the Trusted Root CA store on two servers.
 
    .OUTPUTS
        PSCustomObject with properties: Subject, Thumbprint, Issuer, NotBefore,
        NotAfter, DaysRemaining, Store, ComputerName, Finding.
    #>

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

        [Parameter()]
        [string]$StoreName = 'My',

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

        [Parameter()]
        [int]$DaysUntilExpiration = 30
    )

    begin {
        Write-Verbose "Get-ExpiringCertificates - threshold $DaysUntilExpiration days, store $StoreLocation\$StoreName"

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

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

            $now = Get-Date
            $warningDate  = $now.AddDays($DaysUntilExpiration)

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

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

                [PSCustomObject]@{
                    Subject       = $_.Subject
                    Thumbprint    = $_.Thumbprint
                    Issuer        = $_.Issuer
                    NotBefore     = $_.NotBefore
                    NotAfter      = $_.NotAfter
                    DaysRemaining = $daysRemaining
                    Store         = "$StoreLocation\$StoreName"
                    ComputerName  = $env:COMPUTERNAME
                    Finding       = $finding
                }
            }
        }
    }

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

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

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

                if ($results) {
                    $results | ForEach-Object { $_ }
                }
                else {
                    Write-Verbose "No certificates found in $StoreLocation\$StoreName on $computer."
                }
            }
            catch {
                Write-Warning "Failed to scan ${computer}: $_"
            }
        }
    }

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