Chapter 2: The Google Provider, Projects & APIs
Topic 12

Service Account Impersonation

Auth

Service-account impersonation lets your human identity borrow a service account's permissions through short-lived tokens — without ever holding a key file. You authenticate as yourself through ADC, prove you hold roles/iam.serviceAccountTokenCreator on the target account, and the provider mints an hour-long token to act as it.

This is the recommended way for engineers and pipelines to act as a service account on GCP. The static key file it replaces is the single credential most likely to leak; impersonation gives the same power with nothing on disk, an expiry measured in minutes, and an audit trail showing exactly who borrowed which identity.

The Problem It Solves

Engineers need to apply infrastructure as a privileged service account — Hatch's Terraform runs as tf-admin@hatch-pipeline-dev.iam.gserviceaccount.com, which has the roles to create buckets, datasets, and service accounts. The naive way to let a human act as that account is to hand them its key file. That key is a leak waiting to happen: it lives in dotfiles, gets pasted into Slack, ends up in a git history.

Impersonation gives the same power with no static secret. The engineer never sees a key — they authenticate as themselves and borrow the service account on demand. Revoking access is removing one IAM binding, not rotating a key that may already have been copied a dozen places.

How It Works

You set impersonate_service_account on the provider block (or configure ADC impersonation through gcloud). When Terraform runs, it exchanges your own identity for a short-lived access token on the target service account, calling the IAM Credentials API. That exchange is gated: it only succeeds if your identity holds roles/iam.serviceAccountTokenCreator on the target.

Impersonating the Terraform admin SA
provider "google" {
  project = "hatch-pipeline-dev"
  region  = "us-central1"

  # You authenticate as yourself; the provider borrows this SA:
  impersonate_service_account = "tf-admin@hatch-pipeline-dev.iam.gserviceaccount.com"
}

Every API call this provider makes now runs as tf-admin, using a token minted from your identity. Your own login needs almost no permissions — only the right to impersonate. All the privileged roles live on the service account, and you borrow them for an hour at a time.

Granting the Right

Impersonation is itself an IAM grant. A user or group gets roles/iam.serviceAccountTokenCreator on the specific service account they are allowed to borrow — not at the project level, which would let them impersonate every account in the project. The grant scopes precisely who may act as which identity.

Granting impersonation to the developers group
resource "google_service_account_iam_member" "impersonate" {
  service_account_id = google_service_account.tf_admin.name
  role               = "roles/iam.serviceAccountTokenCreator"
  member             = "group:gcp-developers@hatch.io"
}

The grant is on service_account_id — the target account itself — so it authorizes gcp-developers@hatch.io to borrow only tf-admin, nothing else. Granting serviceAccountTokenCreator at the project level instead would let that group impersonate every service account in hatch-pipeline-dev, which is far too broad.

Impersonation Chains

Impersonation can chain: identity A impersonates B, which impersonates C. This is useful for delegation across project boundaries — a CI identity in one project borrows a deploy account in another, which borrows a narrowly-scoped account in a third. Each hop is a separate serviceAccountTokenCreator grant on the next link.

Chains are also where impersonation produces its most confusing errors. Miss one grant in the middle and you get an opaque token-creation failure that names neither the missing link nor the role. Keep chains short and reserve them for genuine cross-boundary delegation — every extra hop is another place a missing grant can break the whole path.

Why This Beats Keys

Three properties make impersonation strictly better than a key file. Tokens expire in roughly an hour, so a leaked token is useless by lunch — a leaked key is useful until someone notices and rotates it. Nothing is stored on disk, so there is no file to exfiltrate. And the audit log records both identities: the real human who initiated, and the service account they impersonated.

That audit property is the one teams underrate. With a shared key file, the logs show only the service account, so "who actually ran this destroy?" has no answer. With impersonation, the log shows the real engineer behind the borrowed identity — accountability a key file cannot provide. Prefer impersonation in every case where it is possible.

Key file vs impersonation
Key file
Long-lived JSON secret on disk; cannot be scoped per-user, leaves no trace of who used it. Leaks and never rotates.
Impersonation
Hour-long token gated by serviceAccountTokenCreator on the target SA; nothing on disk, logs both identities. Short-lived and auditable.
Key file vs impersonation

Key file — a long-lived JSON secret that grants the service account's full power to anyone holding the bytes. It cannot be scoped per-user, leaves no trace of who actually used it, and is revoked only by rotating the key everywhere it was copied.

Impersonation — mints an hour-long token gated by serviceAccountTokenCreator on the specific target SA, stores nothing on disk, and logs both the real and the borrowed identity. Prefer it in every case where it is possible.

Common Mistakes
  • Distributing a service-account key file to the team instead of granting serviceAccountTokenCreator for impersonation — the key cannot be scoped per-person or easily revoked once copied.
  • Forgetting that impersonation needs the role on the target service account, then debugging a "permission denied" that is just a missing serviceAccountTokenCreator.
  • Granting serviceAccountTokenCreator at the project level when it belongs on a single service account — that lets anyone in scope borrow any account in the project.
  • Breaking an impersonation chain by missing one link's grant and getting an opaque token-creation error that names neither the link nor the role.
  • Granting impersonation to individual users instead of a group like gcp-developers@hatch.io — the grants sprawl and offboarding misses some.
Best Practices
  • Use impersonate_service_account on the provider for engineers and pipelines instead of distributing key files.
  • Grant roles/iam.serviceAccountTokenCreator on the specific target service account, to a group like gcp-developers@hatch.io, never project-wide to individuals.
  • Read the audit logs to confirm impersonation events show both the real and the effective identity.
  • Reserve impersonation chains for genuine cross-boundary delegation and keep them as short as the design allows.
  • Keep the impersonated SA's roles minimal — borrowing it should grant exactly the access the work needs, no more.
Comparable tools AWS STS AssumeRole as the close analog Azure managed identities for borrowing scoped permissions Key files the static-secret anti-pattern impersonation replaces

Knowledge Check

Which IAM role gates impersonation, and on what resource must it be granted?

  • roles/iam.serviceAccountTokenCreator, granted on the specific target service account
  • roles/owner, granted at the enclosing project level so the principal inherits every permission and can mint tokens for any account it contains
  • roles/iam.serviceAccountKeyAdmin, granted on the calling user principal
  • roles/editor, granted at the parent organization level so the broad write access flows down and covers token creation everywhere

What does impersonation buy you over distributing a service-account key file?

  • Hour-long tokens with nothing stored on disk and an audit trail showing both the real and borrowed identity
  • Faster API calls, because the minted tokens are cached on local disk forever
  • The ability to skip IAM policy checks entirely for the borrowed service account
  • A single permanent credential that never expires and never needs rotation, so it can be distributed once to every runner and reused indefinitely without a refresh

Why grant serviceAccountTokenCreator on a single service account rather than at the project level?

  • A project-level grant lets the principal impersonate every service account in the project, which is far too broad
  • Project-level grants of this role are simply not supported by the IAM API, which rejects the binding unless it is attached to one specific service account resource
  • Single-account grants are the only ones that show up in the impersonation audit logs
  • Project-level grants silently expire and are revoked after one hour automatically

An impersonation chain A→B→C fails with an opaque token-creation error. What is the most likely cause?

  • One link is missing its serviceAccountTokenCreator grant on the next account in the chain
  • Impersonation chains longer than two hops are forbidden outright by GCP
  • The final account C in the chain has run out of its API quota, so the token mint at the last hop is throttled once the per-account request ceiling for the day is reached
  • Impersonation tokens cannot cross any project boundary in the chain at all

You got correct