Skip to main content

GCP Workload Identity

Workload Identity is the recommended way for GKE pods to authenticate to Google Cloud APIs. It replaces node-level service account keys with a pod-level IAM binding that uses Kubernetes ServiceAccount tokens.

How It Works

Pod → Kubernetes ServiceAccount
(token projection)
GKE Metadata Server
(token exchange)
Google Security Token Service (STS)

GCP Service Account → Google Cloud APIs

The Kubernetes ServiceAccount and GCP Service Account are linked by an IAM binding, with no JSON key files required.

1. Create a GCP Service Account

terraform/environments/production/workload-identity.tf
resource "google_service_account" "payments_api" {
account_id = "payments-api"
display_name = "payments-api (production)"
project = var.project_id
}

# Grant access to Secret Manager
resource "google_project_iam_member" "payments_api_secrets" {
project = var.project_id
role = "roles/secretmanager.secretAccessor"
member = "serviceAccount:${google_service_account.payments_api.email}"
condition {
title = "payments-secrets-only"
expression = "resource.name.startsWith(\"projects/${var.project_id}/secrets/payments-\")"
}
}

# Grant access to GCS bucket
resource "google_storage_bucket_iam_member" "payments_api_storage" {
bucket = "devopsgenie-payments-production"
role = "roles/storage.objectAdmin"
member = "serviceAccount:${google_service_account.payments_api.email}"
}

# The IAM binding that links KSA → GSA
resource "google_service_account_iam_binding" "payments_api_workload_identity" {
service_account_id = google_service_account.payments_api.name
role = "roles/iam.workloadIdentityUser"

members = [
"serviceAccount:${var.project_id}.svc.id.goog[team-payments/payments-api]"
]
}

2. Annotate the Kubernetes ServiceAccount

kubernetes/namespaces/team-payments/serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: payments-api
namespace: team-payments
annotations:
# This annotation links the KSA to the GSA
iam.gke.io/gcp-service-account: payments-api@devopsgenie-production.iam.gserviceaccount.com

3. Access GCP Resources in Code

The Google Cloud SDK automatically picks up the Workload Identity token:

src/services/secrets_client.py
from google.cloud import secretmanager

# No credentials needed — Workload Identity provides them automatically
client = secretmanager.SecretManagerServiceClient()

name = "projects/devopsgenie-production/secrets/payments-db-password/versions/latest"
response = client.access_secret_version(request={"name": name})
password = response.payload.data.decode("utf-8")
src/services/storageClient.ts
import { Storage } from "@google-cloud/storage";

// Application Default Credentials automatically uses Workload Identity
const storage = new Storage();
const bucket = storage.bucket("devopsgenie-payments-production");

4. Verify Workload Identity

# Confirm the annotation is applied
kubectl get serviceaccount payments-api -n team-payments -o yaml

# Exec into the pod and verify the identity
kubectl exec -it deploy/payments-api -n team-payments -- \
gcloud auth print-identity-token

# Should return a JWT with the GCP service account in the sub claim
kubectl exec -it deploy/payments-api -n team-payments -- \
curl -s -H "Metadata-Flavor: Google" \
"http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/email"
# Expected: payments-api@devopsgenie-production.iam.gserviceaccount.com

GCP Secret Manager via External Secrets Operator

Sync GCP secrets directly into Kubernetes Secrets:

kubernetes/secrets/payments-gcp.yaml
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: gcp-secretmanager
namespace: team-payments
spec:
provider:
gcpsm:
projectID: devopsgenie-production
auth:
workloadIdentity:
clusterLocation: us-central1
clusterName: devopsgenie-production
serviceAccountRef:
name: payments-api

---
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: payments-db-credentials
namespace: team-payments
spec:
refreshInterval: 1h
secretStoreRef:
name: gcp-secretmanager
kind: SecretStore
target:
name: payments-db-credentials
creationPolicy: Owner
data:
- secretKey: db-password
remoteRef:
key: payments-db-password
- secretKey: api-key
remoteRef:
key: payments-api-key