The earlier AWS setup established the useful pattern: CI should present a short-lived identity, not carry a permanent key. Google Cloud implements the same idea through Workload Identity Federation.
A service-account JSON key in GitHub Secrets works, but it creates three chores: prevent leakage, rotate the key, and eventually discover that somebody forgot to rotate the key. Federation removes the private key entirely.
Pool, provider, service account #
The configuration has three parts:
- A Workload Identity Pool is the namespace that accepts external identities.
- A Pool Provider defines GitHub as the issuer and maps claims from its token.
- A service account binding allows matching identities to impersonate one service account.
resource "google_iam_workload_identity_pool" "github" {
workload_identity_pool_id = "github-actions"
display_name = "GitHub Actions Pool"
}
resource "google_iam_workload_identity_pool_provider" "github" {
workload_identity_pool_id = google_iam_workload_identity_pool.github.workload_identity_pool_id
workload_identity_pool_provider_id = "github-provider"
oidc {
issuer_uri = "https://token.actions.githubusercontent.com"
}
attribute_mapping = {
"google.subject" = "assertion.sub"
"attribute.repository" = "assertion.repository"
"attribute.actor" = "assertion.actor"
"attribute.aud" = "assertion.aud"
}
attribute_condition = "attribute.repository == 'your-org/your-repo'"
}The condition is not decorative. It prevents an otherwise valid GitHub identity from a different repository using this provider.
Bind only the intended repository #
Create the service account and grant the repository’s principal set permission to impersonate it:
resource "google_service_account" "github_actions" {
account_id = "terraform-apply"
display_name = "GitHub Actions Terraform Apply"
}
resource "google_service_account_iam_member" "github" {
service_account_id = google_service_account.github_actions.name
role = "roles/iam.workloadIdentityUser"
member = "principalSet://iam.googleapis.com/${google_iam_workload_identity_pool.github.name}/attribute.repository/your-org/your-repo"
}The service account still needs narrowly scoped permissions for its actual work. Workload Identity Federation answers who may become the account; IAM roles answer what that account may do. Mixing those questions produces policies nobody wants to debug during an incident.
Authenticate from the workflow #
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4
- name: Authenticate to Google Cloud via OIDC
uses: google-github-actions/auth@v2
with:
project_id: your-gcp-project-id
workload_identity_provider: projects/123/locations/global/workloadIdentityPools/github-actions/providers/github-provider
service_account: [email protected]AWS calls the destination an IAM role; Google Cloud uses a pool, provider, and service-account impersonation. The resource names differ, but the architecture is now familiar: verify GitHub’s signed claims, restrict the repository, mint short-lived credentials, and keep long-lived keys out of CI.