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