Skip to main content

GitHub OIDC

GitHub OIDC is the arrangement where a GitHub Actions job proves who it is to Microsoft Entra ID with a signed token instead of a client secret. On the Azure platform each repository has one user-assigned managed identity that trusts only that repository's exact job subjects, and the pipelines hold no Azure credential at all.

What it does​

GitHub mints an OpenID Connect (OIDC) token for each job that asks for one, with the repository and the ref, pull request or environment in its sub claim. A federated identity credential on a managed identity trusts tokens from the issuer https://token.actions.githubusercontent.com with the audience api://AzureADTokenExchange and one exact subject. The azure/login action presents the GitHub token, Entra exchanges it for an access token of the identity, and the job calls Azure as that identity for the rest of its run. Nothing long-lived is stored in GitHub; the identity's client ID and the tenant ID are identifiers, not secrets.

How BuiltForProd uses it​

The github-oidc unit (environments/core/auto/global) creates one identity per repository in acme-core-auto, named id-acme-<repository key>-deployer, with one federated credential per subject. Subjects match exactly, repo:your-github-org/<repository>:<subject>, with no wildcards:

IdentitySubjectsAzure access
id-acme-azure-platform-baseline-deployerref:refs/heads/main, pull_request, environment:prodOwner at mg-acme: it applies every unit of the landing zone, the access matrix included
id-acme-azure-blueprint-webapp-infra-deployer, id-acme-azure-blueprint-etl-infra-deployerref:refs/heads/main, pull_request, environment:dev, environment:staging, environment:prodacme-blueprint-deployer and a constrained Role Based Access Control Administrator on the four stage subscriptions
id-acme-azure-blueprint-webapp-code-deployer, id-acme-azure-blueprint-etl-code-deployerref:refs/heads/main, environment:dev, environment:staging, environment:prodNo subscription-wide role; only the grants the owning units make

The code repositories' identities receive narrow grants from the units that own the resources: AcrPush on the registry, Secrets Officer on the contract vault for the values they record, for the web app, Storage Blob Data Contributor on the front-end storage and CDN Profile Contributor on Front Door for cache purges; for the ETL code, Contributor on the trigger job's resource group. The blueprint infrastructure identities can create role assignments only for an allow-listed set of roles, enforced by a condition on their Role Based Access Control Administrator assignment; Azure RBAC shows the custom roles.

There is no role chain. The workflow signs in once, as the identity that already holds the access it needs, in the subscription it targets.

State access follows the same split, per repository: the Baseline identity has Storage Blob Data Contributor on the whole tfstate container in acme-core-root; each blueprint infrastructure identity may read and write only its own key prefix, apps/app-blueprint/ or apps/etl-blueprint/, through a custom role with an attribute-based access control (ABAC) condition; the code repositories, which never run OpenTofu, have no state access.

Environments and tags. A job that declares a GitHub Environment presents environment:<name> instead of its ref, which is how the per-stage plans and applies, and the prod apply behind its reviewers, are matched. A release runs on a tag ref, and tag subjects cannot be listed without wildcards, so every job of the code repositories' release workflow that signs in to Azure declares an Environment.

The secrets repository uses a separate identity, id-acme-secrets-syncer, trusted only for environment:sandbox, dev, staging and prod, because every job of its two workflows runs in a stage Environment; SOPS describes it.

Terms you will see​

TermMeaning
sub claimThe token field naming the repository and the ref, pull request or environment.
Federated identity credentialThe trust on a managed identity for one issuer, subject and audience.
Deployer identityThe per-repository managed identity a workflow signs in as.
GitHub EnvironmentThe named deployment target whose reviewers and branch policy gate a job.
Audienceapi://AzureADTokenExchange, the value Entra expects in the GitHub token.

Where to read more​