Skip to content

Guardrail: OIDC Federation for GitHub Actions → Azure

Status: OIDC required by default Scope: All GitHub Actions workflows that authenticate to Azure Issue: #56


Rule

GitHub Actions workflows authenticating to Azure MUST use OpenID Connect (OIDC) federated credentials via azure/login@v2 by default.

Stored service-principal credentials are an exception that must be explicitly documented and kept in GitHub Secrets. The portal deployment workflow supports the temporary AZURE_CREDENTIALS fallback while selecting OIDC whenever that secret is absent. New workflows must not add stored-credential authentication without an approved operational requirement.


How OIDC Works with GitHub Actions + Azure

  1. GitHub mints a short-lived OIDC token for each workflow run, signed by GitHub's OIDC provider (token.actions.githubusercontent.com or enterprise-scoped issuer such as token.actions.githubusercontent.com/<enterprise-slug>).
  2. Azure Entra ID validates the token against a federated credential configured on an app registration (or managed identity). It checks the issuer, subject, and audience claims.
  3. Azure issues a short-lived access token scoped to the permissions granted to the app registration — no long-lived secret is ever stored or transmitted.

The entire flow is secretless. GitHub never sees an Azure credential, and Azure never stores a GitHub credential.


Bootstrap Pattern

1. Define the Entra identity in IaC

Create the app registration, service principal, federated credentials, and least-privilege role assignments through the repository's IaC. Do not create or change Azure identities ad hoc through the CLI or portal.

2. Add federated credentials

Define one federated credential per branch or environment that needs access. Set the issuer in the IaC definition to https://token.actions.githubusercontent.com, or to the enterprise-scoped issuer when an enterprise custom issuer policy is enabled:

For the main branch:
{
  "name": "main-branch",
  "issuer": "https://token.actions.githubusercontent.com",
  "subject": "repo:ivegamsft/basecoat:ref:refs/heads/main",
  "audiences": ["api://AzureADTokenExchange"]
}

For pull requests:
{
  "name": "pull-requests",
  "issuer": "https://token.actions.githubusercontent.com",
  "subject": "repo:ivegamsft/basecoat:pull_request",
  "audiences": ["api://AzureADTokenExchange"]
}

For a specific environment:
{
  "name": "production-env",
  "issuer": "https://token.actions.githubusercontent.com",
  "subject": "repo:ivegamsft/basecoat:environment:production",
  "audiences": ["api://AzureADTokenExchange"]
}

3. Store identifiers as repository variables

Only non-secret identifiers are stored — no passwords or keys:

Repository variable Value
AZURE_CLIENT_ID App registration Application (client) ID
AZURE_TENANT_ID Entra ID (Azure AD) tenant ID
AZURE_SUBSCRIPTION_ID Target Azure subscription ID

Example Workflow Step

permissions:
  id-token: write   # Required for OIDC token request
  contents: read

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: azure/login@v2
        with:
          client-id: ${{ vars.AZURE_CLIENT_ID }}
          tenant-id: ${{ vars.AZURE_TENANT_ID }}
          subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}

      - run: az account show

Critical: The permissions.id-token: write block is required. Without it, GitHub will not issue the OIDC token and the login step will fail.


Why OIDC Is Preferred

Risk Description
Rotation burden Client secrets expire and must be manually rotated. Missed rotations cause outages.
Secret sprawl Secrets copied across repos, environments, and developer machines multiply the attack surface.
Exfiltration exposure A compromised workflow can exfiltrate a stored secret. OIDC tokens are audience-bound and expire in minutes.
Lateral movement A leaked client secret can be used from any network. OIDC tokens are bound to a specific repo, branch, and workflow run.
Audit gap Secret usage is hard to attribute. OIDC claims provide exact provenance (repo, branch, commit, actor).

References