What breaks
When a workload submits an expired client secret, its token request fails with AADSTS7000222. It cannot get new tokens with that secret. Microsoft's error reference says: "The provided client secret keys are expired. Create new keys for your app, or consider using certificate credentials."
Catch the next secret before AADSTS7000222
AppAlert emails your admins before any app client secret expires. Read-only, free during Early Access. Connect read-only
- Read-only permissions
- Microsoft Graph application permissions
Application.Read.All,Directory.Read.All,AgentIdentity.Read.All,AgentIdentityBlueprint.Read.All,AgentIdentityBlueprintPrincipal.Read.All, plusUser.Readfor sign-in. No write permissions: AppAlert cannot create, rotate, or delete credentials. - Never received
- Secret values, certificate private keys, Microsoft account passwords, and mailbox contents. AppAlert stores credential metadata such as type, expiry date, and owner email addresses.
- Where data lives
- App functions and database in Frankfurt, Germany (Vercel fra1, Neon on AWS eu-central-1). Alert emails are sent through Resend, which stores them in the United States. Data location
- Revoke any time
- A Microsoft Entra administrator can revoke AppAlert's enterprise application consent. That stops future directory reads; deleting stored data is a separate request. Retention and revocation
How you notice
Check the error in the failed token request. AADSTS7000222 means the supplied secret keys have expired. After updating the consumer, check for AADSTS7000215. It means the secret value is not valid. Check the value in the consumer's configuration before assuming the replacement has expired.
How do I find all secrets that expire soon?
For a single app, Azure CLI lists credential metadata. It cannot retrieve the secret value. Review the expiry dates in this output:
az ad app credential list --id '<app-id>' --output table
For all app registrations, adapt Microsoft's PowerShell export script. Run this snippet in an authenticated Microsoft Graph PowerShell session. It checks only passwordCredentials, using the credential's endDateTime.
$WarningDays = 30
$Cutoff = [DateTimeOffset]::UtcNow.AddDays($WarningDays)
Get-MgApplication -All -Property @('appId', 'displayName', 'passwordCredentials') -ErrorAction Stop |
ForEach-Object {
$App = $_
foreach ($Credential in $App.PasswordCredentials) {
if ($null -eq $Credential.EndDateTime) { continue }
$End = [DateTimeOffset][DateTime]::SpecifyKind($Credential.EndDateTime, 'Utc')
if ($End -le $Cutoff) {
[pscustomobject]@{
App = $App.DisplayName
AppId = $App.AppId
KeyId = $Credential.KeyId
EndDateTime = $End.UtcDateTime.ToString('o')
}
}
}
}
Set $WarningDays to your renewal lead time. This example includes expired secrets and skips entries without an expiry date. Review those entries separately.
Use Monitor credential expiry to plan recurring checks. See App secret expiration notifications for notification options. To have client secret expiry tracked for you, see Monitored by AppAlert.
Why it happens
Secret creation sets an expiration. In the portal, the lifetime is limited to 24 months or less. Microsoft recommends less than 12 months. See Add credentials.
The defaults differ by tool. Graph addPassword defaults to two years after the start time. Azure CLI --years defaults to one year.
Creating a replacement does not update the workload's configuration. Treat consumer configuration as separate work. Include every deployment that uses the secret in your renewal plan.
Renewal path
Create a replacement, update every consumer, verify the new key ID, then delete the old secret. If the old secret has already expired, prioritize restoring authentication. For a planned rotation, keep the existing credential while you update and verify each consumer.
The commands follow Microsoft's documentation and were not run against a real environment.
Choose one creation tool below. Replace the placeholders before running a sample. Capture the new value in the approved secret store. Keep secret values out of terminal output, transcripts, HTTP response logs, and deployment logs.
Azure CLI
Use az ad app credential reset with --append to add the replacement. Without --append, the reset overwrites existing credentials. Capture the password in a variable rather than printing the response:
new_secret=$(az ad app credential reset \
--id '<app-id>' \
--append \
--display-name 'rotation' \
--query password \
--output tsv)
# Write "$new_secret" to the approved secret store without logging it,
# then clear the variable.
unset new_secret
Microsoft Graph PowerShell
Use Add-MgApplicationPassword with the application object ID. Its documented scope is Application.ReadWrite.All. This sample sets a six-month expiry and captures the response:
Connect-MgGraph -Scopes 'Application.ReadWrite.All'
$NewCredential = Add-MgApplicationPassword `
-ApplicationId '<application-object-id>' `
-PasswordCredential @{
displayName = 'rotation'
endDateTime = (Get-Date).AddMonths(6)
} -ErrorAction Stop
# Write $NewCredential.SecretText to the approved secret store without logging it,
# then clear the variable. Do not output $NewCredential.
Remove-Variable NewCredential
Graph API
Call addPassword with the application object ID. The least privileged delegated permission is Application.ReadWrite.All. For application access, it is Application.ReadWrite.OwnedBy.
Replace <expiry-utc> with your chosen expiry in ISO 8601 format. Send this request through an authenticated client that does not log the response body:
POST https://graph.microsoft.com/v1.0/applications/<application-object-id>/addPassword
Content-Type: application/json
{
"passwordCredential": {
"displayName": "rotation",
"endDateTime": "<expiry-utc>"
}
}
Graph returns secretText only in the creation response. You cannot retrieve it later. Write it to the approved secret store before discarding the response.
Update and verify every consumer
If you use the portal, open Certificates & secrets > Client secrets > New client secret. Store the Value before leaving the page. Microsoft never displays it again. See the portal credential steps.
Update each consumer to read the replacement from the secret store. Deploy the configuration and test a fresh authentication attempt. Follow Identify the active credential to verify the new key ID in service principal sign-in logs. Keep that evidence for every consumer before deleting the old credential.
Delete the old secret by key ID
After verification, choose the deletion command for your tool. Use the old key ID, not the replacement's key ID.
With Azure CLI:
az ad app credential delete --id '<app-id>' --key-id '<old-key-id>'
With Remove-MgApplicationPassword:
Remove-MgApplicationPassword `
-ApplicationId '<application-object-id>' `
-BodyParameter @{ keyId = '<old-key-id>' } `
-ErrorAction Stop
With Graph removePassword, a successful request returns 204 No Content:
POST https://graph.microsoft.com/v1.0/applications/<application-object-id>/removePassword
Content-Type: application/json
{
"keyId": "<old-key-id>"
}
For production, Microsoft recommends certificates or federated credentials instead of secrets. Plan that migration separately from incident recovery.
Who should own it
Assign the application owner to coordinate with the team that can deploy the replacement. Record an alternate who can cover absences.
Lead time and business-day math
Suggested planning buffer: 30 calendar days. Subtract weekends, holidays, approval time, and deployment freezes when scheduling the change.
Related failure modes
A client authentication certificate also needs a tested rollover procedure. Use Owner has left and Ownerless credential when renewal has no reachable owner. Use Identify the active credential to gather evidence before removal.
An Azure DevOps service connection that signs in with a client secret has its own error messages and renewal steps.
Monitored by AppAlert
Monitored
AppAlert monitors client secret expiry and sends email alerts to configured admin recipients before a secret expires, with optional routing to application owners.
Expiration tracking does not replace credential rotation or deployment testing.
Use the Operations queue to review the expiry and application context. Configure who receives alerts in Alert settings and recipients; for a missing alert, follow Missing notifications.