Workload identity federation

Microsoft Azure · Expert · 15 min

Federation eliminates the last long-lived secrets: CI/CD service principal passwords and pod credentials.

Managed identities only work for code running on Azure resources. Workload identity federation covers everything else that can present an OIDC token: GitHub Actions, GitLab, Kubernetes service accounts (including AKS), Google Cloud and AWS workloads.

You add a federated identity credential to a user-assigned managed identity or an app registration. It names a trusted issuer (e.g. https://token.actions.githubusercontent.com), an exact subject (e.g. repo:contoso/orders:environment:prod) and the audience api://AzureADTokenExchange. At runtime the workload gets a short-lived token from its own issuer and exchanges it at Entra ID (client credentials flow with a client_assertion) for an Entra access token. Nothing secret is stored anywhere.

Subjects must match exactly (flexible claim matching exists in preview for some issuers), so design them deliberately: binding to a GitHub environment lets you add required reviewers before production credentials are usable. An identity has a limit of 20 federated credentials.

On AKS, enable the OIDC issuer and workload identity, annotate a Kubernetes service account with the identity's client ID, label pods with azure.workload.identity/use: "true", and create a federated credential whose subject is system:serviceaccount:<namespace>:<name>. The webhook injects environment variables and a projected token file that DefaultAzureCredential picks up automatically.

Example

# One-time setup (bash):
# az identity federated-credential create -g rg-id --identity-name id-orders-deployer \
#   --name gh-prod --issuer https://token.actions.githubusercontent.com \
#   --subject repo:contoso/orders:environment:prod --audiences api://AzureADTokenExchange

# .github/workflows/deploy.yml
name: deploy
on: { push: { branches: [main] } }
permissions:
  id-token: write      # allow requesting the GitHub OIDC token
  contents: read
jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: prod    # subject becomes repo:contoso/orders:environment:prod
    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 deployment group create -g rg-orders-prod -f main.bicep

Remember

  • Federation trusts an external OIDC issuer + exact subject; no secret is stored
  • Works for CI/CD, Kubernetes pods and other clouds
  • Scope subjects to environments or branches deliberately
  • AKS: OIDC issuer + annotated service account + pod label

Watch out for

  • Forgetting permissions: id-token: write in GitHub Actions, so no OIDC token can be requested.
  • Using a broad subject pattern that lets any branch or fork obtain production credentials.

Check yourself

  1. What does Entra ID verify before issuing a token through a federated credential?
  2. Which subject should an AKS federated credential use for service account 'api' in namespace 'orders'?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation