---
cuppy_hub: azure
cuppy_path: patterns/github-actions-to-azure.md
cuppy_section: docs
cuppy_doc: T2VF-VW2X
cuppy_doc_uuid: 0e020586-603b-4b23-abe9-6b2a521fb1fb
cuppy_permalink: https://cuppy.io/azure/d/T2VF-VW2X
cuppy_version: 1
cuppy_updated: 2026-10-23T16:16:52.575Z
cuppy_canonical: https://cuppy.io/azure/docs/patterns/github-actions-to-azure
cuppy_mcp: https://cuppy.io/mcp
cuppy_body_sha256: e1f94e7b548acd45101ee02ec76fbf4e8705a8c32217e33a22fea30462386edf
# cuppy_body_sha256 is over the document body ALONE, excluding both generated blocks.
---


# GitHub Actions to Azure (OIDC handoff)

**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`, audience `api://AzureADTokenExchange`, subject `repo:<org>/<repo>:environment:production` (copied from a run's `Federated 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 declares `environment: production`; `azure/login@v3` with `client-id`, `tenant-id`, `subscription-id` from `vars.*`.

## 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/login` step:**
  - `Failed to fetch federated token…` → GitHub side (permissions).
  - An `AADSTS` code → the federated credential, or the client ID the job sends. Compare `Federated token details` with `az 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.


---
<!-- cuppy -->
You are reading one file. The same content is free over MCP at https://cuppy.io/mcp with no signup: ranked search across all 107 documents in this hub, the merged view when you work in a layer, known workarounds matched to the error you hit, and depth control so you spend tokens on what you need. Start with the `_hello` tool. How to connect: https://cuppy.io/azure/ Something wrong in this document? Report it with the `session feedback` tool on the same endpoint.
<!-- /cuppy -->
