Browse all topics
Microsoft Entra (Identity)

Rotating an app registration secret without downtime

By Emil Björk · Microsoft ecosystem consultant, Gothenburg

The overlap procedure for replacing a client secret or certificate on an Entra app registration with zero outage — find every consumer, add the new credential alongside the old, switch, verify, remove — and how to stop doing it by hand.

Client secrets expire, and the expiry email from Entra goes to whoever created the app registration, who left two years ago. The result is an integration that stops at 09:00 on a Tuesday with an invalid_client error. Rotation itself is a five-minute job; the reason it becomes an outage is that nobody knows where the old secret is being used. This runbook is the overlap method, which never has a moment where no valid credential exists, followed by the ways to stop doing this manually at all.

The distinction between app registrations and enterprise apps, and the service principal hygiene around them, is assumed.

Step 1: find every consumer of the current secret

An app registration can have several secrets and certificates at once, and the consumers of each are not recorded anywhere in Entra. What is recorded is which service principal signed in, when, from where: Entra admin center → Monitoring → Sign-in logsService principal sign-ins tab, filtered on the app. The IP addresses and the volume tell you how many places hold the secret. If the last sign-in is months ago, you may be rotating a credential nothing uses — check the Remove unused credentials recommendation before doing any work.

Also search where secrets live in your world: Azure Key Vault secrets referencing the app, pipeline variables, appsettings in deployed services, Power Automate custom connectors, Logic Apps HTTP actions, on-prem scheduled tasks with a PowerShell script, and the integration vendor's portal. Write the list down; it is the deliverable that makes the next rotation ten minutes.

If you genuinely cannot find a consumer but the sign-in log shows it is live, the IP address and the user agent in the log entry are your clues. A rotation is still possible — you just have to accept that the unknown consumer will break at the remove step, and be watching.

Step 2: add the new credential alongside the old

Entra admin center → App registrations → the app → Certificates & secrets → New client secret. Set a description that says what it is for and who owns it, and pick the shortest expiry the consumers can tolerate — the portal caps at 24 months, and 6 or 12 months is what a rotation process should be built around.

Or, in PowerShell:

$cred = Add-MgApplicationPassword -ApplicationId "app-object-id" -PasswordCredential @{ displayName = "rotation 2026-09"; endDateTime = (Get-Date).AddMonths(12) }
$cred.SecretText

Copy the value immediately; it is shown once. Put it in Key Vault or your secret store before doing anything else. The old secret remains valid — the app now has two, and both work.

If you are rotating a certificate, upload the new public key the same way; the app can hold multiple. This is also the moment to move from a secret to a certificate if the consumer supports it, since certificates are not sent over the wire and are harder to leak.

Step 3: switch consumers, one at a time

Update each consumer from the list in step 1 to the new value. For Key Vault-backed consumers this is a new secret version and, depending on how the consumer reads it, a restart. For pipeline variables it is an edit. For a vendor, it is their portal.

After each switch, confirm in the service principal sign-in log that the consumer is now authenticating — the log shows which credential key ID was used under Authentication details for the sign-in, so you can watch the old key ID's usage drop to zero. That column is the entire verification step, and it is why the overlap method works: you never have to guess.

Step 4: wait, then remove the old credential

Once the old key ID shows no sign-ins for a period that covers every consumer's schedule — a weekly report needs a week, a monthly one needs a month — delete the old secret from the app registration. If anything breaks at this point, you have found the consumer you did not know about; re-add a secret (it will be a new one, the deleted value cannot be recovered), update the consumer, and add it to the list.

Removal is the step people skip, and skipping it is how apps accumulate six live secrets, any of which may be in a repository somewhere. Delete it.

The case where downtime is unavoidable

A consumer that cannot hold two credentials and has no restart-free reload — some on-prem middleware, some vendor products — will drop requests between "old secret removed from the consumer" and "new secret loaded". That gap is seconds to minutes and it is the consumer's limitation, not Entra's. Schedule it; do not pretend the overlap method removes it.

An app whose secret has already expired is a different situation: there is no overlap available, and step 2 followed immediately by step 3 is the fastest path. The invalid_client errors stop the moment the consumer picks up the new value.

Stop rotating by hand

Three changes remove most of this runbook from your future:

Workload identity federation. For consumers that run in GitHub Actions, Azure DevOps, Kubernetes, or another Azure tenant, the app registration can trust the platform's own token instead of holding a secret. No secret, no expiry, no rotation. This is the right answer for CI/CD.

Managed identities. For consumers running in Azure — Functions, App Service, VMs, Logic Apps — a managed identity replaces the app registration entirely. Grant the managed identity the Graph permissions and delete the secret-based registration.

Monitoring the ones that remain. Query Graph for applications with passwordCredentials or keyCredentials ending within 60 days, and alert on it in a channel a team reads, not an individual's inbox:

Get-MgApplication -All -Property DisplayName,PasswordCredentials,KeyCredentials |
  ForEach-Object { $app = $_; $app.PasswordCredentials + $app.KeyCredentials |
    Where-Object EndDateTime -lt (Get-Date).AddDays(60) |
    Select-Object @{n="App";e={$app.DisplayName}}, DisplayName, EndDateTime, KeyId }

Pair that with an app owner on every registration — a group, not a person — and the expiry email lands with someone who still works there. The Entra ID recommendations view surfaces expiring and unused credentials tenant-wide and is worth a monthly look.

What needs support

Nothing. The only external party is a vendor whose product holds the secret, and their change process is the thing to plan around.

Further reading

Spot something wrong or want a topic covered? Send it through the contact form.