Public/Get-IOExpiringSecrets.ps1
|
function Get-IOExpiringSecrets { <# .SYNOPSIS Finds app registrations with client secrets or certificates expiring within a specified number of days. .EXAMPLE Get-IOExpiringSecrets -DaysUntilExpiry 30 .EXAMPLE Get-IOExpiringSecrets -DaysUntilExpiry 90 -IncludeExpired -ToCsv "expiring-secrets.csv" #> [CmdletBinding()] param( [ValidateRange(1, 3650)] [int]$DaysUntilExpiry = 30, [switch]$IncludeExpired, [string]$ToCsv ) $cmdName = $MyInvocation.MyCommand.Name Write-IOLog "Scanning app registrations for secrets/certs expiring within $DaysUntilExpiry days..." -Level Info -Component $cmdName $now = [datetime]::UtcNow $cutoff = $now.AddDays($DaysUntilExpiry) $results = [System.Collections.Generic.List[PSCustomObject]]::new() # Fetch all app registrations with credential fields $apps = Invoke-IOGraphRequest -Uri 'v1.0/applications?$select=id,displayName,appId,passwordCredentials,keyCredentials' $total = ($apps | Measure-Object).Count Write-IOLog "Evaluating $total app registration(s)..." -Level Verbose -Component $cmdName if ($total -eq 0) { Export-IOResult -Data @() -ToCsv $ToCsv -CommandName $cmdName return } $counter = 0 try { foreach ($app in $apps) { $counter++ if ($counter % 50 -eq 0) { Write-Progress -Activity 'Scanning secrets & certificates' -Status "$counter / $total apps" -PercentComplete (($counter / $total) * 100) } # ── Password credentials (client secrets) ───────────────────────── if ($app.passwordCredentials) { foreach ($cred in $app.passwordCredentials) { if (-not $cred.endDateTime) { continue } $endDate = [datetime]::Parse($cred.endDateTime, [System.Globalization.CultureInfo]::InvariantCulture, [System.Globalization.DateTimeStyles]::AssumeUniversal) $isExpired = $endDate -lt $now $isExpiring = $endDate -le $cutoff if ($isExpiring -or ($IncludeExpired -and $isExpired)) { $results.Add([PSCustomObject]@{ ApplicationName = $app.displayName ApplicationId = $app.appId ObjectId = $app.id CredentialType = 'ClientSecret' CredentialName = $cred.displayName KeyId = $cred.keyId ExpiryDate = $endDate.ToString('yyyy-MM-dd') DaysRemaining = [math]::Floor(($endDate - $now).TotalDays) Status = if ($isExpired) { 'EXPIRED' } else { 'EXPIRING' } }) } } } # ── Key credentials (certificates) ──────────────────────────────── if ($app.keyCredentials) { foreach ($cred in $app.keyCredentials) { if (-not $cred.endDateTime) { continue } $endDate = [datetime]::Parse($cred.endDateTime, [System.Globalization.CultureInfo]::InvariantCulture, [System.Globalization.DateTimeStyles]::AssumeUniversal) $isExpired = $endDate -lt $now $isExpiring = $endDate -le $cutoff if ($isExpiring -or ($IncludeExpired -and $isExpired)) { $results.Add([PSCustomObject]@{ ApplicationName = $app.displayName ApplicationId = $app.appId ObjectId = $app.id CredentialType = 'Certificate' CredentialName = $cred.displayName KeyId = $cred.keyId ExpiryDate = $endDate.ToString('yyyy-MM-dd') DaysRemaining = [math]::Floor(($endDate - $now).TotalDays) Status = if ($isExpired) { 'EXPIRED' } else { 'EXPIRING' } }) } } } } } finally { Write-Progress -Activity 'Scanning secrets & certificates' -Completed } $sorted = $results | Sort-Object DaysRemaining Export-IOResult -Data $sorted -ToCsv $ToCsv -CommandName $cmdName } |