AADSTS7000222: The provided client secret keys for app '***' are expired.
Your YAML is the same as last week. Your Azure Resource Manager service connection signs in to
Azure with an app registration and a client secret, and that secret has passed its expiry date.
Depending on the task, the same problem can also show up as
AADSTS7000215: Invalid client secret is provided or as a plain
Invalid client id or client secret.
The right fix depends on how the connection was created, which takes less than a minute to check.
First, check which kind of service connection you have
Open Project settings → Service connections and select the connection your
pipeline names in azureSubscription:. If its secret has expired, the details page says
so; Azure DevOps added that indicator in 2024. Then check how it authenticates:
| Connection type | How you can tell | What to do |
|---|---|---|
| App registration (automatic) with a secret Older connections show this as Service principal (automatic) |
Azure DevOps created the app registration for you. The page offers Rotate secret and Convert | Rotate now, or convert so it never expires again (Fix 1 or Fix 2) |
| Manual, with a secret Service principal (manual), or App registration (manual) with a secret |
You (or someone before you) pasted a client ID and a secret into the form | Create a new secret in Entra ID and paste it in (Fix 3) |
| Workload identity federation | The credential type says Workload identity federation | There's no secret to expire. A failure here is a different problem, usually an issuer or subject mismatch (AADSTS70021, AADSTS700213) |
| Managed identity | The identity type says Managed identity | No secret either. Check the identity's role assignments instead |
Fix 1: rotate the secret (automatic connections)
- In Project settings → Service connections, open the connection.
- Select Rotate secret.
- Re-run the failed pipeline.
Microsoft's docs say the rotated secret is good for three more months. If Rotate secret isn't on the page, select Edit, then Save: saving an automatic connection renews its secret too. Either way, the person doing it needs enough rights on the subscription and in Entra ID, because Azure DevOps writes a new secret to the app registration on your behalf.
You can also rotate before a secret expires. Three months is short, though: rotate today and do nothing else, and the same pipeline fails again in three months.
Fix 2: convert the connection so it never expires (recommended)
Converting switches the connection from a stored secret to workload identity federation. Azure DevOps then gets a short-lived token for every run, and there is no secret to rotate.
- In Project settings → Service connections, open the connection.
- Select Convert. If the secret has already expired, you get a slightly different convert prompt; it does the same thing.
- Select Convert again to confirm. It can take a few minutes.
- Re-run the pipeline.
Pipelines refer to the connection by name, so your YAML doesn't change. If something breaks, you can revert to the secret within seven days with Revert conversion to the original scheme. After seven days, going back means creating a new secret by hand.
The confirmation dialog spells out what happens to the old secret: Azure DevOps deletes its own copy after seven days, but leaves the secret on the app registration in Microsoft Entra ID. Once you're sure you won't revert, delete it there under Certificates & secrets, so an unused secret doesn't sit on the app until it expires.
The conversion tool only works when both of these are true:
- Azure DevOps created the connection. A manual connection can't be converted with the tool, because Azure DevOps has no permission to change credentials it didn't create.
- Only one project uses it. Cross-project (shared) connections can't be converted.
Before converting, check these:
- Scripts that read the secret. An
AzureCLI@2orAzurePowerShelltask withaddSpnToEnvironment: trueexposes the secret to your script asservicePrincipalKey. After conversion, the script gets anidTokeninstead. If you passservicePrincipalKeytoaz login, Terraform or anything else, update that script first. - Marketplace tasks. Any third-party task that uses the connection must support workload identity federation. Check the documentation for the task version you run.
- Whether the connection is enabled. Azure Pipelines can disable a connection that hasn't been used for 100 days, and a disabled connection fails before the secret even comes into it.
Fix 3: replace the secret on a manual connection
Azure DevOps can't rotate a secret it didn't create, so this one is on you.
- In the service connection's edit dialog, copy the Service Principal Id (the app's client ID).
- In the Microsoft Entra admin center, go to App registrations and search by that client ID, not by name. Automatically created apps follow a naming pattern of organization, project and subscription ID, and several can end up with the same name.
- Open Certificates & secrets, select New client secret, and copy the Value right away. Copy the Value, not the Secret ID; pasting the ID is how you trade this error for AADSTS7000215.
- Back in Azure DevOps, paste the Value into Service Principal Key and select Verify and save.
- Re-run the pipeline. When everything that uses this app registration passes, delete the old secret.
While you're in there, consider setting the manual connection up with workload identity federation instead, following Microsoft's steps for setting up workload identity manually.
Still failing after you fixed it?
The same secret often lives in more places than the service connection. Check these:
- Variable groups and pipeline variables. Terraform pipelines often pass
ARM_CLIENT_SECRETas a secret variable, separate from the service connection. Update it there too. - Other connections on the same app. Several service connections, in this project or others, can point at one app registration. Each one that stores the secret needs the new value.
- Classic release pipelines. They can use the same connection and are easy to forget when you test.
- The wrong app. If you rotated a different app registration that shares the name, nothing changes. Match the client ID.
- A brand-new secret. A secret can take a short while to work everywhere after you create it. If the first retry fails with
7000215, wait a minute and retry before changing anything else.
To see which pipelines use the connection, open its Usage history tab. That's also the list to re-run once you're done.
For workloads outside Azure DevOps that use the same app (Azure CLI scripts, GitHub Actions, App Service settings, Key Vault), how to fix AADSTS7000222 has a table of where the old secret usually lives.
Stop it from happening again
The expired-secret message on the service connection page helps with diagnosis, but you only see it after the pipeline has failed and you've gone looking. Microsoft doesn't document any warning before the secret expires.
Start by converting everything you can. To list the connections in a project that still use a secret:
az devops service-endpoint list \
--organization https://dev.azure.com/<org> --project "<project>" \
--query "[?type=='azurerm' && authorization.scheme=='ServicePrincipal'].{name:name, created:data.creationMode}" \
-o table
Rows marked Automatic can usually be converted in the UI as above, and Microsoft's docs
include a script that converts them in bulk. Rows marked Manual need the manual workload
identity setup.
Then watch the secrets you have to keep. Some connections will keep a secret for a while: manual ones, shared ones, ones behind a task that doesn't support federation yet, and the same app registrations used outside Azure DevOps. Each of those secrets lives on an app registration in Entra ID, and it will expire again.
That's what Token Watch does. It reads every app registration in your tenant with one read-only Microsoft Graph permission, and alerts you before a secret or certificate expires. On the Team plan, the Azure DevOps integration creates a work item for each expiring credential in the project you choose, and closes it automatically once the secret is rotated, so the rotation gets planned into a sprint like any other task. The Free plan sends the same warning by email, and the integrations page lists the other channels.
FAQ
servicePrincipalKey through addSpnToEnvironment, or a Marketplace task that doesn't support federation. You can revert within seven days.
Written by Maksym Vostruhin
Founder of Token Watch. Built it after chasing down one too many expired App Registration secrets by hand across a growing set of Entra tenants.
Token Watch