You have probably lived this one, or you will. An integration that has worked for two years starts throwing AADSTS7000222, "the provided client secret keys are expired," at 3am. Nothing was deployed. Nothing changed. A client secret on an app registration simply reached its end date, and Entra ID, which knew that date all along, told absolutely no one.
Here is the part that still surprises people: Microsoft Entra ID has no native expiry alerting for app registration credentials. The portal shows each secret's expiry date on the registration blade, but nothing watches those dates. No email to the owner, no alert rule, no Action Group hook. The feedback idea asking for owner alerts has been open for over six years with 223+ votes, and it is still unaddressed.
So the first signal most teams get is the outage itself, followed by the fun part: figuring out under incident pressure which of your dozens of app registrations it was, which credential expired, and who even owns the thing.
Why this is annoying to solve by hand
The data is all there, just scattered. Every application object in Microsoft Graph carries its own passwordCredentials (client secrets) and keyCredentials (certificates), each with an endDateTime. Answering "what expires in the next 30 days, tenant-wide?" means walking every registration, not checking one dashboard.
The build: a daily Graph scan with escalating thresholds
The fix is small and pleasant to run:
- A daily timer job, an Azure Function on a consumption plan works nicely, running under a system-assigned managed identity. That last part matters: the watcher itself has no credential that can expire. No irony-based outages.
- Grant the identity the
Application.Read.AllGraph application role. That is enough to list registrations and read credential metadata. It cannot read secret values, which is exactly what you want. - Call
GET /v1.0/applications, and for every app readendDateTimeon each entry in bothpasswordCredentialsandkeyCredentials. Two gotchas here: follow the paging or a large tenant silently truncates, and do not skip certificates. They expire too, and cert-based auth failures are even more confusing to debug at 3am. - Compute days to expiry and compare against a ladder of thresholds, for example 30, 14, 7, and 1 days. A single warning that fires once and goes quiet is easy to lose in a busy channel. The escalation is what actually gets a secret rotated.
- Post hits to a Teams webhook with the app name, credential type, and days remaining, so the alert lands where the owning team already works.
That is genuinely the whole thing: managed identity, one read-only Graph permission, one API call with paging, a threshold ladder, a webhook.
If you would rather not maintain it
Disclosure: I work for Katabarwa Labs, and we ship this exact pattern packaged as Secret Sentinel: App Registration Expiry Alerts. It deploys from the Azure Marketplace as a managed application into your own tenant, scans every app registration daily, and alerts your Teams channel at 30/14/7/1 days (you pick the thresholds at deployment). It runs on a managed identity with only Application.Read.All, reads expiry dates and never secret values, and there is no vendor backend, so credential metadata never leaves your tenant.
Listing: https://marketplace.microsoft.com/en-us/product/azure-application/katabarwalabs.secret-sentinel
There is a longer write-up of the DIY version on our blog: Get alerted before Entra app registration secrets and certificates expire.
Takeaway
Every client secret and certificate in your tenant has a known expiry date sitting in Graph right now, and by default nothing will warn you before one of them takes an integration down. Whether you write the twenty-line scan yourself or grab a packaged one, put something between those dates and your on-call rotation. Future 3am-you will be grateful.
Top comments (0)