Using Azure EntraID App Registrations is almost essential for any type of application security or person-independant access to APIs and other autorization flows. Always follow the best practices when setting up this type of access. I’m not covering the full use of these type of identities here, but wanted to provide an easy solution for a recurring problem with App Registration secrets : they (should) expire!
The problem
App registration secrets (and certificates) are created with a specific expiration date. E.g. you make them valid for 3, 6, … months and rotate them before they expire by creating a new secret and replacing the values in the application configuration. The old secret can then be safely removed.
However, by default, there is no functionality available for the owner to get notified of secrets that are about to expire. This seems like an essential functionality, as the alternative is to put a recurring reminder in your calendar to update the secret, or create a framework that takes care of it automatically every week. Not all applications allow this reconfiguration automatically, so this can be quite tedious.
As a few quick google searches quickly learn, this seems like a common problem, without a common solution Automate sending email notifications for client secret expiration of App Registration under Microsoft Entra ID – Microsoft Q&A
The PowerShell script suggested in the Q&A was unfortunately left as a ‘take-home’ exercise. I’ve provided below that functioning script, which you can fine tune for your purposes. I’ll also cover what is required to get this setup.
Another option is functionality only available to Entra ID “Security Administrators”, so usually not for mortals in an organisation. In addition, it also check all the app registrations and doesn’t really know who to notify : Recommendation to renew expiring application credentials – Microsoft Entra ID | Microsoft Learn
The Solution
I often found solutions relying on Logic Apps to perform this notification, but it seems like overkill (and very tedious to setup) for what could be a 20 line scripted solution. Eg. Using Logic Apps and Microsoft Sentinel to alert on expiring Azure AD Secrets – Microsoft Sentinel 101
My approach schedules a PowerShell script on a daily basis, and list the app registrations that have secrets which are about to expire. The script calls Graph API calls and sends an email with a summary. To make this you’ll need:
- text editor : to write the powershell script
- an Azure Entra ID App Registration (yes, case of chicken and egg, but luckily it will check itself as well) with a secret
- an Azure Key Vault : to store the secret in, so you don’t have to hard-code and update the script every few months
- an SMTP server in order to send emails
The App Registration
The App Registration used to call the Graph APIs will need:
- note down the Client ID
- note down the Tenant ID
- a secret which you store in Keyvault
- API permission : Graph, Application permission, Applications.Read.All
- This needs to be granted by your organisation admin before you can use it
The Key Vault
When running the script from a specific server, make sure to give the Managed Identify of the virtual machine access to the Keyvault. Alternatively, you can just give the identity that runs the script access to the keyvault secrets.
In below script, I’ll use -Identity to authenticate with the managed identity.
The Script
The script will connect to Keyvault to get the secret of the app registration and combine that in a PSCredential to be used in the connection to Graph.
It will then query all app registrations starting with e.g. MY-TEAM- in the Display Name. Good naming convention of your app registrations helps to dynamically filter them out. Alternatively, you can update the script to look for all app registrations of a specific owner, but that is sometimes not desired either as anyone can make them.
It will the check all the secrets (if any) of the app registration and store those that are about to expire in the next 30 days.
At the end, it will email the list of all these secrets. Make sure to update you email and smtp server.
This is just a draft script, which lacks error handling, but it provides a workable example of the essential steps, without having to revert to Logic Apps.
Import-Module Microsoft.Graph.Applications
# update following variables with your values
$KeyVaultName = "axis-kv" # Keyvault that holds the
$SecretName = "appreg--GRAPH-API--secret" # reference in keyvault to the secret you generated for this app registration to connect to Graph
$AppRegClientId = "36bf94-e6b6-4019-0f7d7e-43d0f7d7eee9" #the app registration to connect to Graph
$TenantId = "e549f63e-b8f6-fe549-9f6-2db7fe54c74"
$AppRegFilter = "MY-TEAM-" #used to limit the app registrations to those belonging to yourself (based on naming convention)
$DaysBeforeExpiration = 30 #amount of days to start warning before the expiration date
$SmtpServer = "smtp1.axis.0"
# Connect to Azure resources
Connect-AzAccount -Identity # Managed identity, the virtual machine
# Get secret from KeyVault
$secret = Get-AzKeyVaultSecret -VaultName $KeyVaultName -Name $SecretName -AsPlainText
$secureSecret = ConvertTo-SecureString $secret -AsPlainText -Force
$appCred = New-Object System.Management.Automation.PSCredential ($AppRegClientId, $secureSecret)
# Connect to Graph
Connect-MgGraph -TenantId $TenantId -ClientSecretCredential $appCred
# Empty array to store expired secrets
$secrets = @()
# Get all app registrations starting with MY-TEAM-
Get-MgApplication -Filter "startswith(displayName, '$AppRegFilter')" -ConsistencyLevel eventual | ForEach-Object {
# for each listed application, get the details required to find end date of secrets
$appReg = Get-MgApplicationByAppId -AppId $_.AppId -Property "id,appId,displayName,passwordCredentials"
$secretList = $appReg.PasswordCredentials
# if there are secrets created for this app registration, go through them
if ($secretList -ne $null) {
#check for each secret if the end date is less then 30 days from now (expire in 30 days)
foreach ($s in $secretList) {
if ($s.EndDateTime -lt (Get-Date).AddDays($DaysBeforeExpiration)) {
#add this secret to the list of expired secrets
$secrets += "$($appReg.DisplayName) has a secret ($($s.DisplayName)) that is about to expire on the $($s.EndDateTime)"
Write-Host $secrets[$secrets.count - 1]
}
}
}
}
#send a summary email of the expired secrets
if ($secrets.Count -gt 0) {
$bodyText = $secrets | Out-String
$sendMailMessageSplat = @{
From = 'DE SMET Mattias <mattias@axis.0>'
To = 'DE SMET Mattias <mattias@axis.0>'
Subject = 'Secrets about to expire'
Body = $bodyText
SmtpServer = $SmtpServer
}
Send-MailMessage @sendMailMessageSplat
}