Service account key files are the worst part of GCP for anyone who has ever had to clean one up after a leak. They are long-lived, they are portable, they rotate reluctantly, and they end up committed to repositories, embedded in CI variables, and pasted into Slack messages. Workload Identity Federation, which has been generally available since April 2021 and has matured substantially through 2024, is the mechanism GCP gives you to get rid of them.
This post is about using Workload Identity Federation specifically in supply-chain contexts: CI systems, external builders, SBOM uploaders, and the various pieces of automation that need to push or pull GCP resources. The shape of the problem in supply chain is distinct from the general case, because the threat you are guarding against is not just credential leak but credential misuse by a compromised pipeline.
What Workload Identity Federation actually does
At its core, Workload Identity Federation lets an external identity provider mint a short-lived token, which GCP exchanges for a short-lived service account impersonation token. The external provider can be an OIDC-compliant system like GitHub Actions, GitLab CI, or AWS IAM, or it can be a SAML 2.0 identity provider.
The key properties are: no long-lived GCP credential is ever created, the token lifetime is short (typically 15 minutes), and the exchange is gated by attribute conditions you write in CEL that evaluate claims from the external token. The last property is where supply-chain security lives. If you write the condition correctly, a stolen external token is useless for attacking GCP resources outside its intended scope.
The pool and provider structure
A Workload Identity Pool is a container that groups identities from one or more trust relationships. A Workload Identity Provider inside the pool defines the trust relationship with one external IdP. You impersonate a service account through the pool, not directly.
The design choice I recommend is one pool per trust boundary. "Trust boundary" here means a group of external systems that should all be able to impersonate the same set of GCP service accounts with the same constraints. For a typical setup, you have a pool for GitHub Actions, a separate pool for GitLab CI if you run both, a pool for your cloud provider federations (AWS workloads, Azure workloads), and a pool for any SaaS tools that integrate with GCP.
Do not put GitHub Actions and GitLab CI in the same pool. Do not put production and non-production in the same pool. A pool is a trust anchor, and mixing trust anchors collapses the separation you spent time designing.
GitHub Actions: the pattern
The GitHub Actions integration is the most common use case. GitHub provides an OIDC token on every workflow run, with claims that include the repository, the ref, the event name, and the job workflow. The token is signed by https://token.actions.githubusercontent.com/.well-known/jwks, which is what you configure as the JWKS URL on the provider.
The attribute condition is where the security lives. A naive policy has:
assertion.repository == "my-org/my-repo"
which is a good start but not enough. A better policy is:
assertion.repository_owner == "my-org" &&
assertion.repository == "my-org/my-repo" &&
assertion.ref == "refs/heads/main" &&
assertion.event_name == "push" &&
assertion.job_workflow_ref.startsWith("my-org/my-repo/.github/workflows/deploy.yml")
This scopes the impersonation to a specific workflow file on a specific branch of a specific repository, pushed as a specific event. A fork cannot trigger it. A pull request cannot trigger it. A tampered workflow file that bypasses the intended checks also cannot trigger it, because the job_workflow_ref includes the ref at which the workflow was loaded.
The trap I have seen twice is using repository_owner alone without repository. GitHub's owner field can be reused if an organization is renamed, and I once reviewed a policy where a deprecated organization name had been reclaimed by an unrelated third party. The pool still trusted it. Always require the full repository claim.
Service account impersonation, with minimal grants
The pool and provider establish trust. The service account is what actually has permissions. Grant the impersonation with roles/iam.workloadIdentityUser on the service account, with a principal set that matches the attribute mapping:
principalSet://iam.googleapis.com/projects/PROJECT/locations/global/workloadIdentityPools/POOL/attribute.repository/my-org/my-repo
This means anyone whose workflow resolves to that repository under the pool's attribute mapping can impersonate the service account, subject to the provider's attribute condition.
The service account itself should have the minimum roles for the task. For a workflow that publishes SBOMs to Artifact Registry, that means roles/artifactregistry.writer on the specific repository, roles/logging.logWriter, and nothing else. Do not grant roles/editor because it is convenient; the whole point of federation is to make scope exact.
Attribute conditions for external builders and SBOMs
A more specialized supply-chain use case is signing artifacts from a dedicated external builder. Sigstore's Fulcio, for example, issues short-lived certificates based on OIDC claims. If you trust those certificates to attest to GCP-hosted artifacts, you configure a provider that trusts Fulcio's issuer, and you write an attribute condition that restricts which signers can impersonate which GCP service accounts.
A condition for a Sigstore-backed SBOM publisher might be:
assertion.iss == "https://oauth2.sigstore.dev/auth" &&
assertion.email == "sbom-publisher@my-org.com"
Combined with a subject mapping that captures the signer identity, this gives you a narrow path by which external signing infrastructure can write to your Artifact Registry without ever possessing a GCP credential.
Audit logs, because federation does not eliminate the need to watch
Workload Identity Federation emits audit logs on every token exchange through the sts.googleapis.com service. The interesting events are GenerateAccessToken with claims that do not match recent patterns (a new repository, a new branch, a new event type) and any authentication failure that falls outside normal pipeline churn.
I route these to a Cloud Logging sink that writes to BigQuery, and I run a scheduled query every hour that flags anomalies. The first time the pipeline ran, it caught a repository that had been archived but still had an active workflow running on a schedule, which is a minor cleanup item but an interesting signal: federated identities outlive their purpose silently.
Pair this with audit of the pool and provider configurations themselves. Terraform the pool configuration, route Cloud Audit Logs for iam.googleapis.com/workloadIdentityPools to a security sink, and alert on any change. Changes to attribute conditions are especially interesting, because a weakened condition can be the first step of a supply-chain attack against the federation itself.
Organization policy to prevent regressions
Set constraints/iam.disableServiceAccountKeyCreation to enforced at the organization level. This prevents teams from creating new JSON key files, which is the antipattern Workload Identity Federation is designed to replace. Pair it with constraints/iam.disableServiceAccountKeyUpload to block uploading externally generated keys, because uploaded keys bypass rotation controls entirely.
Clean up existing keys with a scheduled job. Cloud Asset Inventory gives you a full list of iam.googleapis.com/ServiceAccountKey resources, and you can diff that list against a Workload Identity Federation migration plan to track progress. I use a Cloud Scheduler job that publishes to BigQuery monthly, and the platform-security team tracks the burn-down.
How Safeguard Helps
Safeguard inventories every Workload Identity Federation pool, provider, and principal binding across your GCP organization, and evaluates each configuration against policy: attribute condition strictness, principal set scope, service account privilege, and audit coverage. When a pool gains a new provider, an attribute condition is weakened, or a service account impersonable via federation gains a broad role, Safeguard flags the change against the specific pipeline or workflow that uses it. Long-lived service account keys still in use are tracked alongside the federated alternatives so you can see migration progress. The result is a federation program that stays tight as your pipelines evolve, with evidence ready for the next access review.