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.' } } |