GitHub Actions to Azure (OIDC handoff)
The handoff from a GitHub Actions job to Azure Resource Manager through workload identity federation — what each side must hold (id-token permission, client and tenant IDs, a federated credential matching issuer, subject and audience, a role at the deployment scope), the known-good configuration, the known-bad ones with their exact symptoms (including 2026 immutable subjects and pull-request jobs), and how to tell which side is wrong.
markdownpatternv1T2VF-VW2Xstage: buildtrust: officialservice: entraservice: rbacservice: managed-identityinterface: ciinterface: cliGitHub Actions Azure handoffOIDC GitHub to Entrafederated credential known-goodAADSTS700213 GitHub environmentpull_request subjectimmutable subject repository renamedid-token write missingContributor cannot assign roles pipelineazure/login v3deployment identity least privilegewhich side is wrong GitHub Azure
GitHub side: the job may request an OIDC token (id-token: write) and sends a token whose sub describes the job. Entra side: a federated credential on the identity matches that token exactly, and the identity holds a role at the scope the job deploys to. A failure is always one of those four facts.
Known-good
- Identity: a user-assigned managed identity
gh-deploy, one per repository and environment group. - Federated credential: issuer
https://token.actions.githubusercontent.com, audienceapi://AzureADTokenExchange, subjectrepo:<org>/<repo>:environment:production(copied from a run'sFederated token details). - Roles: Contributor on the target resource group. Plus, only if the job deploys role assignments, Role Based Access Control Administrator on the same group with a condition listing the role IDs it may assign.
- Workflow:
permissions: id-token: write, contents: read; the deploying job declaresenvironment: production;azure/login@v3withclient-id,tenant-id,subscription-idfromvars.*.
Known-bad
| Configuration | Symptom |
|---|---|
No id-token: write (or set only on another job) | Failed to fetch federated token from GitHub. Please make sure to give write permissions to id-token in the workflow. |
Credential subject is …:ref:refs/heads/main, but the job declares environment: production | AADSTS700213 … presented assertion subject 'repo:<org>/<repo>:environment:production' |
Only a branch credential exists, and the workflow also runs on pull_request | PR runs fail with AADSTS700213 for subject repo:<org>/<repo>:pull-request; pushes work |
| Repository created or renamed after July 15, 2026; credential uses the name-based subject | AADSTS700213 quoting repo:<org>@<id>/<repo>@<id>:… |
| Credential made in the portal with owner/repo IDs filled in; repository never opted in to immutable subjects | AADSTS700213 quoting the name-based subject |
My-Org in GitHub, my-org in the credential | AADSTS7002138 … matches with case-insensitive comparison, but not with case-sensitive comparison. |
| First run seconds after creating the credential | AADSTS70021; passes on a re-run minutes later |
client-id is the app's object ID, or another app's client ID | AADSTS70021 or AADSTS700016 |
Identity has Contributor; the Bicep file contains roleAssignments | Login succeeds; deployment fails with AuthorizationFailed on Microsoft.Authorization/roleAssignments/write |
| Role on the resource group; the job also creates a new resource group | AuthorizationFailed on Microsoft.Resources/subscriptions/resourceGroups/write |
No subscription-id and no allow-no-subscriptions | Ensure 'subscription-id' is supplied or 'allow-no-subscriptions' is 'true'. |
Which side is wrong
- The failure happens in the
azure/loginstep: Failed to fetch federated token…→ GitHub side (permissions).- An
AADSTScode → the federated credential, or the client ID the job sends. CompareFederated token detailswithaz identity federated-credential list(reference/entra/federated-credential-errors.md). - Login succeeds, then a later step fails:
AuthorizationFailed→ the role or its scope (reference/rbac/errors.md).RequestDisallowedByPolicy→ policy, not the pipeline (reference/policy/effects-and-errors.md).- Works on
main, fails on PRs or tags → each trigger presents its own subject; add a credential per subject, or route deployments through an environment.