Expiring app secrets: the outage nobody sees coming

01 Aug 2026

Expiring app secrets: the outage nobody sees coming

Somewhere in your tenant there is an app registration with a client secret that expires in the next 90 days. When it does, something stops working: the backup job, the ticketing integration, the SSO for a line-of-business app, the script that provisions new hires. Nobody gets an email. The app doesn't degrade gracefully — it returns AADSTS7000215: Invalid client secret provided to a service account, once, and then keeps returning it until a human notices that the nightly export hasn't landed for four days. This is the most predictable outage in Microsoft 365, and almost nobody has it on a calendar.

24m
max secret lifetime in the portal
0
notifications Entra sends you
2
credential types that expire
90d
the window worth reporting on

Two credential types, two places to look

An app registration in Entra ID can hold two kinds of credential, and the Graph API exposes them as two separate collections on /applications:

  • passwordCredentials — client secrets. A string, created in the portal with a maximum lifetime of 24 months (the API allows longer, which is how tenants end up with secrets nobody remembers creating). Each entry has a displayName, startDateTime and endDateTime.
  • keyCredentials — certificates. Same shape, longer lifetimes, and usually attached to the integrations that matter most, which makes them the ones that break loudest.

Then there is the second place: /servicePrincipals. Every app registration has a service principal in your tenant, but not every service principal has an app registration you own — third-party SaaS apps and Microsoft first-party apps appear there too, some with their own credentials. Auditing only /applications gives you the apps your team created. Auditing only the portal blade gives you whichever one you happen to be looking at.

WHAT EXPIRES, AND WHAT BREAKS App registration created once, owned by whom? passwordCredentials client secret · 6, 12, 24 months keyCredentials certificate · years, then silence AADSTS7000215 logged by a service account read by nobody, for four days
The pattern
Nothing in Entra ID warns you that a credential is about to expire. The detection mechanism your tenant currently uses is "a user reports that something is broken", and the mean time to attribute that report to an expired secret is measured in days, not minutes.

The four failure modes

Situation What happens Cost of finding out late
Secret expires on a live integration Auth fails immediately, every call. Hours of outage plus a backlog to replay.
Certificate expires on SAML SSO Every user of that app is locked out at once. A full-department incident, in business hours.
The app has three secrets, one used Nothing — until you rotate the wrong one. Two live credentials nobody can account for.
Secret expired months ago Nothing broke, so the app is dead. An over-permissioned app registration, still consented.
Owner left the company The rotation runbook left with them. Nobody knows where the secret is configured.

The fourth row deserves a second look, because it isn't an availability problem — it's the same category as orphaned Azure resources: something that was provisioned, stopped being used, and was never removed. An app registration with an expired secret and Mail.ReadWrite application permission is dormant, not harmless. Someone can add a new secret to it in thirty seconds and inherit consent that was granted in 2022.

Getting ahead of it in four steps

1
Inventory every credential, not every app.

One row per credential, with app name, app id, credential type, display name and endDateTime. An app with four secrets is four rows. Sorted by expiry date, this list is the whole report — and it immediately shows the apps carrying credentials they don't need.

2
Split it into three buckets: expired, 30 days, 90 days.

Expired means "investigate whether this app is dead" — delete or document, don't rotate by reflex. Thirty days is an incident waiting for a date and belongs in this week's queue. Ninety days is planning: enough time to find the owner, find where the secret is configured, and schedule the change.

3
Rotate with overlap, never with a cutover.

Add the new credential first, deploy it to the consumer, verify a successful call, then delete the old one. App registrations accept multiple valid credentials precisely so rotation doesn't need a maintenance window. Deleting the old secret in the same change is how a routine rotation becomes an outage.

4
Stop creating secrets where you don't need them.

Workload identity federation and managed identities remove the credential entirely: no string to store, no expiry to track, no rotation runbook. Every app you migrate is a row that leaves this report permanently. What remains — third-party SaaS that only speaks client secret — is a much shorter list to babysit.

Why the portal won't tell you

The Entra portal shows credentials per app registration, one blade at a time. There is no tenant-wide "expiring in the next 90 days" view, no alert, and no export that spans applications and service principals together. Doing it by hand means a Get-MgApplication -All loop that flattens PasswordCredentials and KeyCredentials, a second pass over service principals, and a date filter — a script that works fine the first time and then quietly stops being run. Across multiple tenants, multiply by the number of tenants and add the delegated-access dance for each one.

GraphPaaS collects expiring app secrets as part of the governance domain it already syncs per tenant, alongside admin roles and Conditional Access policies, so "what expires in the next 90 days, across every tenant" is a filter rather than a scripting afternoon. It sits next to the same identity hygiene questions as MFA registration gaps — because an app credential is an identity too, and it's the one nobody offboards.

Key takeaway
Every expiring credential in your tenant has a known expiry date sitting in the Graph API right now. An outage you could have read the date of in advance isn't an incident — it's a reporting gap.
GraphPaaS

See every client secret and certificate in every tenant you manage, sorted by the day it stops working.

See what expires next →

Newsletter

Get the next article by email

Practical M365 & Azure cost pieces like this one. No spam, unsubscribe anytime.