TL;DR
| |
And in the workflow:
| |
Whatever roles the pipeline needs (Artifact Registry, Cloud Run, GKE…) go to github-actions@… like to any service account.
Why
The classic alternative is exporting a service account JSON key and storing it as a GitHub secret. That key never expires, anyone with access to the repo or the logs can copy it, and rotating it is manual work nobody does. With Workload Identity Federation GitHub issues a short-lived OIDC token for each job, Google verifies it against GitHub’s issuer, checks your attribute condition, and hands out temporary service account credentials. There is no secret to store or rotate, and you can restrict who gets in down to the repo or branch.
The pieces
| Piece | What it is |
|---|---|
Pool (github) | Container for external identities; one per provider is enough |
OIDC provider (github-oidc) | Trusts https://token.actions.githubusercontent.com and maps token claims (repository, repository_owner, actor, ref) to attributes |
| Attribute condition | CEL filter over those claims. Without it, any GitHub repo could try to enter the pool; the official README says to always set one |
workloadIdentityUser binding | Which pool identities may impersonate the service account: attribute.repository/my-org/my-repo, or attribute.repository_owner/my-org for the whole organization |
auth@v3 | Exchanges GitHub’s token for Google credentials and leaves them configured for gcloud, the SDKs, and Terraform |
To restrict to a branch, map the claim and use it in both the condition and the binding:
| |
With Terraform
The official module creates pool, provider, and bindings in one block:
| |
The output is exactly the value that goes into the workflow’s workload_identity_provider.
Without a service account
auth@v3 also supports direct federation: the principalSet gets IAM roles without going through a service account (workload_identity_provider and project_id, no service_account). Fewer pieces, but not every service accepts federated principals; Artifact Registry, GCS, and most APIs do, and it is the path Google recommends for new setups.
Traps
id-token: writein the job’spermissions. Without it,authfails with “Unable to get ACTIONS_ID_TOKEN_REQUEST_URL”.- Project number, not ID, in the provider path and the
principalSet:projects/123456789012/…. Permission 'iam.serviceAccounts.getAccessToken' denied: theroles/iam.workloadIdentityUserbinding is missing orattribute.repositorydoes not match (case and exact repo name).- The condition must reference mapped claims: if you use
assertion.refin the condition without mapping it to an attribute, the provider gets created but never matches. - Forks and external PRs: tokens from a fork’s PR carry the fork’s
repository; a condition onrepository_ownerkeeps them out, which is usually what you want. - Rotation: nothing to rotate, but something to review.
gcloud iam workload-identity-pools providers describeand the STS audit logs tell you who is coming in.
Reference: google-github-actions/auth and Workload Identity Federation in the Google Cloud documentation.