
Service Account Impersonation Instead of Keys: gcloud, Terraform & CI Patterns | 2026
Exporting long-lived service account JSON keys to developer laptops or CI secrets is an outdated security anti-pattern. Service Account Impersonation lets authenticated human users or workloads dynamically generate short-lived (max 1-hour) OAuth2 access tokens for a target service account. This guide covers how to set up `roles/iam.serviceAccountTokenCreator`, configure gcloud CLI flags, bind Terraform Google provider impersonation blocks, and secure local developer workflows without storing a single credential file.
By Mateusz Chmielewski · Aug 22, 2026 · 14 min read
What is Service Account Impersonation?
Service Account Impersonation is an IAM mechanism that allows an authenticated principal (such as a human user logged into gcloud, or a Workload Identity container) to assume the identity and permissions of a target Service Account without needing its private JSON key.
Think of an exported service account key like a physical physical master key — anyone who finds it on the street can unlock the building indefinitely. Impersonation is like showing your photo ID to a security guard, who hands you a temporary visitor badge valid for 60 minutes. The guard logs who asked for the badge, and when time expires, the badge stops working automatically.
| Concept | Explanation | When to use |
|---|---|---|
| Target Service Account | The service account possessing the actual GCP IAM roles (e.g., Storage Admin, Pub/Sub Editor) required to perform tasks. | Designate dedicated target service accounts per application or deployment domain. |
| Origin Principal | The authenticated identity requesting the token — a human user account (user:[email protected]) or group. | Keep origin principals separate from resource permissions. |
| Token Creator Role | The IAM role (`roles/iam.serviceAccountTokenCreator`) granted on the target Service Account to authorize token generation. | Grant directly on target service accounts to specific team members or groups. |
| Short-Lived Access Token | An OAuth 2.0 bearer token generated by the IAM Credentials API, valid for 1 hour by default (expandable up to 12h). | Used automatically by gcloud SDKs and Terraform provider calls. |
Service Account Keys vs IAM Impersonation
Service account JSON keys never expire unless manually rotated. They frequently leak into git repositories, developer backups, or CI log outputs. Once leaked, attackers gain persistent background access without triggering password reset alerts.
Service Account Impersonation eliminates credential files entirely. Authentication relies on Google OAuth login or Workload Identity, issuing ephemeral tokens that automatically expire within 1 hour.
| Feature | thisService | altA | altB |
|---|---|---|---|
| Lifespan | Impersonation: Ephemeral (1 hour default) | JSON Key: Infinite (until manually revoked) | OAuth Refresh Token: Variable |
| Storage Requirement | Zero file storage required | JSON file stored on disk/secrets manager | Local cache file |
| Audit Traceability | Logs BOTH human identity AND service account | Logs service account ONLY (anonymous caller) | Logs user account ONLY |
| Key Rotation Complexity | Zero key rotation overhead | High manual overhead (key rotation schedules) | Managed by OAuth library |
| Compromise Impact | Low — token expires in <60 minutes | Catastrophic — persistent background access | Medium — refresh token compromise |
Prerequisites
- GCP project with IAM Credentials API (`iamcredentials.googleapis.com`) enabled
- gcloud CLI v480.0+ authenticated via `gcloud auth login`
- Terraform CLI v1.6+
- IAM Admin permissions to assign service account roles
Step-by-Step Guide
Step 1: Create Target Service Account and Grant Token Creator Role
Create the target service account and assign `roles/iam.serviceAccountTokenCreator` to your human user identity or developer Google Group. Granting the Token Creator role on the specific service account (resource-level IAM) ensures least privilege instead of granting global project-wide token creation rights.
# Step 1: Create target service account
gcloud iam service-accounts create terraform-deployer \
--display-name="Terraform Deployment Target SA" \
--project=PROJECT_ID
# Step 2: Grant target SA permissions on the project
gcloud projects add-iam-policy-binding PROJECT_ID \
--member="serviceAccount:terraform-deployer@PROJECT_ID.iam.gserviceaccount.com" \
--role="roles/editor"
# Step 3: Allow human developer to impersonate this specific SA
gcloud iam service-accounts add-iam-policy-binding \
terraform-deployer@PROJECT_ID.iam.gserviceaccount.com \
--member="user:[email protected]" \
--role="roles/iam.serviceAccountTokenCreator" \
--project=PROJECT_ID
Step 2: Configure gcloud CLI for Transparent Impersonation
Use the `--impersonate-service-account` flag or set gcloud configuration variables to impersonate the target service account automatically. Developers can run gcloud commands against production or staging projects using target SA permissions without needing direct project IAM roles on their user accounts.
# Option A: One-off gcloud command with impersonation flag
gcloud storage ls --impersonate-service-account=terraform-deployer@PROJECT_ID.iam.gserviceaccount.com
# Option B: Set persistent gcloud configuration property
gcloud config set auth/impersonate_service_account terraform-deployer@PROJECT_ID.iam.gserviceaccount.com
# Verify active user and impersonation configuration
gcloud auth list
gcloud config get-value auth/impersonate_service_account
# Option C: Unset impersonation when switching back to personal user permissions
gcloud config unset auth/impersonate_service_account
Step 3: Configure Terraform Google Provider for Impersonation
Use the `impersonate_service_account` parameter inside the Terraform Google provider block to execute all infrastructure changes via short-lived impersonation tokens. This pattern allows developers to run `terraform plan` and `terraform apply` locally using their personal `gcloud auth application-default login` credentials, while Terraform executes operations as the privileged deployment service account.
# provider.tf — Impersonation pattern for Terraform
terraform {
required_version = ">= 1.6.0"
required_providers {
google = {
source = "hashicorp/google"
version = "~> 5.0"
}
}
}
# Step 1: Authenticate provider using short-lived impersonated token
provider "google" {
alias = "impersonated"
}
# Fetch short-lived token using local user ADC credentials
data "google_service_account_access_token" "default" {
provider = google.impersonated
target_service_account = "terraform-deployer@${var.project_id}.iam.gserviceaccount.com"
scopes = ["userinfo-email", "cloud-platform"]
lifetime = "1200s" # 20 minutes
}
# Step 2: Primary provider using impersonated token
provider "google" {
project = var.project_id
region = "europe-west1"
access_token = data.google_service_account_access_token.default.access_token
request_timeout = "60s"
}
Step 4: Express/Node.js SDK Impersonation Pattern
Instantiate Google Cloud client libraries using Google Auth Library impersonated credentials. Allows backend services running locally or in development environments to access GCP APIs without downloading JSON key files.
// storage_service.js — Node.js SDK Impersonation
const { Storage } = require('@google-cloud/storage');
const { Impersonated, GoogleAuth } = require('google-auth-library');
async function getImpersonatedStorageClient() {
// Base user or workload identity authentication
const auth = new GoogleAuth({
scopes: ['https://www.googleapis.com/auth/cloud-platform']
});
const targetPrincipal = 'app-backend-sa@PROJECT_ID.iam.gserviceaccount.com';
// Wrap source credentials in Impersonated client
const impersonatedClient = new Impersonated({
sourceClient: await auth.getClient(),
targetPrincipal: targetPrincipal,
targetScopes: ['https://www.googleapis.com/auth/cloud-platform'],
lifetime: 3600 // 1 hour
});
return new Storage({ authClient: impersonatedClient });
}
// Test GCS bucket listing via impersonation
async function run() {
const storage = await getImpersonatedStorageClient();
const [buckets] = await storage.getBuckets();
console.log('Buckets accessible via impersonation:', buckets.map(b => b.name));
}
run().catch(console.error);
Step 5: Audit Impersonation Events in Cloud Logging
Run BigQuery / Log Analytics queries to track who generated tokens for service accounts. Impersonation maintains non-repudiation by logging both the human user who requested the token and the service account that performed the action.
-- Log Query: Track all Token Creation events (Who impersonated whom)
SELECT
timestamp,
protoPayload.authenticationInfo.principalEmail AS origin_user,
protoPayload.resourceName AS target_service_account,
protoPayload.methodName AS action,
severity
FROM `PROJECT_ID.global_logging.cloudaudit_googleapis_com_activity`
WHERE
protoPayload.serviceName = "iamcredentials.googleapis.com"
AND protoPayload.methodName = "google.identity.iamcredentials.v1.IAMCredentials.GenerateAccessToken"
AND timestamp >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 7 DAY)
ORDER BY timestamp DESC
Verification & Health Check
Best Practices
- Grant Token Creator Role at Resource Level, Not Project Level
- Disable Service Account Key Creation via Org Policy
Common Mistakes
- {"errorCode":"PERMISSION_DENIED_GENERATE_TOKEN","symptoms":"gcloud or Terraform returns Error 403: Google API Error: permissionDenied on iamcredentials.googleapis.com.","rootCause":"The origin principal lacks roles/iam.serviceAccountTokenCreator on the target service account, or IAM Credentials API is disabled.","fixCommand":"gcloud services enable iamcredentials.googleapis.com --project=PROJECT_ID","code":"gcloud iam service-accounts add-iam-policy-binding TARGET_SA_EMAIL \\\n --member=\"user:USER_EMAIL\" \\\n --role=\"roles/iam.serviceAccountTokenCreator\"\n","language":"bash","filename":"fix_token_permissions.sh","prevention":"Add IAM role verification steps in local Makefile or developer onboarding scripts."}
Cost Analysis
| Feature | metric | cost1k | cost10k | cost100k | cost1m |
|---|---|---|---|---|---|
| IAM Credentials API Calls | $0.00 | $0.00 | $0.00 | $0.00 | |
| Service Account Key Storage & Management | $0.00 | $0.00 | $0.00 | $0.00 | |
| Total Cost | $0.00 / month | $0.00 / month | $0.00 / month | $0.00 / month |