Monitor AzureAD App registration expiration with PowerShell (GraphAPI)

There are several methods for monitoring Azure AD App registration expiration (like PowerAutomate or Azure Logic Apps) but these methods require extra licences or an Azure subscription. The PowerShell way is free and it only requires a new registration in AzureAD.

TL;DR

  • Create a new app registration with Microsoft Graph Application.Read.All application permission
  • Add a client secret to the app, copy the secret as it will be used in the script
  • Use the script below, fill the variables $tenantID, $appID, $appSecret, $daysBeforeExpiration
  • The output is a PSCustomObject, it is up to you to process it (the example below converts it to a bordered HTML table which is then sent in email – make sure to correct the parameters when using it)

The script:

$tenantID = ''
$appID = ''
$appSecret = ''
$daysBeforeExpiration = 30

#Ensure TLS1.2 is used
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

#Get access token
$scope = 'https://graph.microsoft.com/.default'
$oAuthUri = "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token"
$body = [Ordered] @{
    scope = "$scope"
    client_id = "$appId"
    client_secret = "$appSecret"
    grant_type = 'client_credentials'
}
$response = Invoke-RestMethod -Method Post -Uri $oAuthUri -Body $body -ErrorAction Stop
$aadToken = $response.access_token

#Query app registrations, store it in $applications variable
 $url = 'https://graph.microsoft.com/v1.0/applications'

    $headers = @{ 
    'Content-Type' = 'application/json'
    'Accept' = 'application/json'
    'Authorization' = "Bearer $aadToken" 
    }

$applications = $null
while ($url -ne $null){
 $json_response =  (Invoke-WebRequest -Method Get -Uri $url -Headers $headers -ErrorAction Stop | ConvertFrom-Json) #use -UseBasicParsing if scheduling with 'NT Authority\Network Service'
 $applications += $json_response.value
 $url = $json_response.'@odata.nextLink'
 }

 #for each app select the latest credentials of each credential type
 $apps_Cred_latest = $null
 $apps_Cred_latest = foreach ($app in $applications){
    [pscustomobject]@{
        Name = $app.displayname
        LatestKey = $app.KeyCredentials.enddatetime | sort -Descending | select -First 1 
        LatestPass = $app.PasswordCredentials.enddatetime | sort -Descending | select -First 1 


    }
    $app = $null
}

#select apps that are expiring within the range defined in $daysBeforeExpiration
$expiringApps = $apps_Cred_latest | ? {($_.LatestKey,$_.LatestPass -ne $null)} | ? {[datetime]($_.LatestKey, $_.latestPass | sort -Descending | select -First 1) -le (Get-date).AddDays($daysBeforeExpiration) } 


if ($expiringApps){
$Header = @"
<style>
TABLE {border-width: 1px; border-style: solid; border-color: black; border-collapse: collapse;}
TH {border-width: 1px; padding: 3px; border-style: solid; border-color: black;}
TD {border-width: 1px; padding: 3px; border-style: solid; border-color: black;}
</style>
"@

[string]$html_expiringApps = $expiringApps | convertto-html -Head $Header

Send-MailMessage -From "daniel@f12.hu" -To "reports@f12.hu" -SmtpServer smtp.f12.hu -Port 25 -Subject "Azure AD credentials expiring in $daysBeforeExpiration days" -Body $html_expiringApps -BodyAsHtml -Encoding UTF8
}

Leave a Reply