GCP Workload Identity Federation
Configure exe.dev Identity Federation so VMs can impersonate Google Cloud service accounts
Identity Federation lets an attached VM mint short-lived exe.dev OIDC tokens.
Google Cloud trusts those tokens through a Workload Identity Pool provider, then
exchanges them for access to a service account. Use this instead of storing
Google Cloud service account keys on the VM.
Create the exe.dev integration in the web UI. Run the Google Cloud commands
from a machine with `gcloud`.
## Setup
### Set values
Google Cloud project and pool values. Choose these once for the project:
```
export PROJECT_ID=example-gcp-project
export POOL_ID=exe-dev
export PROJECT_NUMBER="$(gcloud projects describe "$PROJECT_ID" --format='value(projectNumber)')"
export GCP_POOL_RESOURCE="projects/${PROJECT_NUMBER}/locations/global/workloadIdentityPools/${POOL_ID}"
```
Choose a provider ID unique within this pool for this exe.dev user or team. For
example:
```
export PROVIDER_ID=exe-dev-example-team
export GCP_PROVIDER_RESOURCE="${GCP_POOL_RESOURCE}/providers/${PROVIDER_ID}"
export GCP_PROVIDER_AUDIENCE="https://iam.googleapis.com/${GCP_PROVIDER_RESOURCE}"
```
If this pool already contains a provider for the global issuer (commonly
`exe-dev`), use a different ID for this scoped issuer.
Per-service-account and per-exe.dev-integration values. Choose these for each
workload:
```
export INTEGRATION_NAME=gcpwif
export SERVICE_ACCOUNT_NAME=exe-dev-demo
export SERVICE_ACCOUNT_EMAIL="${SERVICE_ACCOUNT_NAME}@${PROJECT_ID}.iam.gserviceaccount.com"
```
### Add the exe.dev integration
Open the [Integrations page](/integrations), choose `Identity Federation`, and
select `GCP`.
- `Name`: `gcpwif`
- `Project ID`: the `PROJECT_ID` value from above
- `Project number`: the `PROJECT_NUMBER` value from above
- `Pool ID`: the `POOL_ID` value from above
- `Provider ID`: the `PROVIDER_ID` value from above
- `Service account`: the `SERVICE_ACCOUNT_EMAIL` value from above
- `Attach to`: the VM or tag that should use this service account
Copy the generated `Issuer` and `Subject`, then click `Run`. The Google Cloud
provider and IAM binding below must use those exact values.
<img src="/docs/integrations/gcp-wif/identity-federation-add-gcp-provider-trust.png" alt="Identity Federation modal configured for a GCP Workload Identity provider" width="100%"/>
Set the copied values in your shell before running the Google Cloud commands.
Replace these example values with the exact `Issuer` and `Subject` from the
integration:
```
export EXE_WIF_ISSUER=https://exe.dev/issuer/example-team-workload
export EXE_WIF_SUBJECT=sub-ABCDEFGHIJKLMNOPQRSTUVWXYZ
```
### Configure Google Cloud
```
gcloud services enable \
iam.googleapis.com \
sts.googleapis.com \
iamcredentials.googleapis.com \
cloudresourcemanager.googleapis.com \
--project "$PROJECT_ID"
gcloud iam workload-identity-pools create "$POOL_ID" \
--project "$PROJECT_ID" \
--location global \
--display-name "exe.dev"
gcloud iam workload-identity-pools providers create-oidc "$PROVIDER_ID" \
--project "$PROJECT_ID" \
--location global \
--workload-identity-pool "$POOL_ID" \
--display-name "exe.dev" \
--issuer-uri "$EXE_WIF_ISSUER" \
--allowed-audiences "$GCP_PROVIDER_AUDIENCE" \
--attribute-mapping "google.subject=assertion.sub"
gcloud iam service-accounts create "$SERVICE_ACCOUNT_NAME" \
--project "$PROJECT_ID" \
--display-name "exe.dev demo workload"
gcloud iam service-accounts add-iam-policy-binding "$SERVICE_ACCOUNT_EMAIL" \
--project "$PROJECT_ID" \
--role "roles/iam.workloadIdentityUser" \
--member "principal://iam.googleapis.com/${GCP_POOL_RESOURCE}/subject/${EXE_WIF_SUBJECT}"
```
Grant the service account only the roles your workload needs. For example:
```
gcloud projects add-iam-policy-binding "$PROJECT_ID" \
--member "serviceAccount:${SERVICE_ACCOUNT_EMAIL}" \
--role "roles/storage.objectViewer"
```
## Use it from the VM
From a VM that has the integration attached, use `/metadata` to read the Google
Cloud values you entered in the integration:
Choose the integration name:
```
export INTEGRATION_NAME=gcpwif
export EXE_WIF_URL="https://${INTEGRATION_NAME}.int.exe.xyz"
export EXE_WIF_METADATA_FILE="/tmp/exe-${INTEGRATION_NAME}-gcp-wif-metadata.json"
export GOOGLE_APPLICATION_CREDENTIALS="$HOME/.config/gcloud/exe-${INTEGRATION_NAME}-gcp-wif.json"
mkdir -p "$(dirname "$GOOGLE_APPLICATION_CREDENTIALS")"
curl -fsS "$EXE_WIF_URL/metadata" > "$EXE_WIF_METADATA_FILE"
```
For a team integration, use `https://${INTEGRATION_NAME}.team.exe.xyz` for
`EXE_WIF_URL` instead.
```
export PROJECT_NUMBER="$(jq -r .project_number "$EXE_WIF_METADATA_FILE")"
export POOL_ID="$(jq -r .pool_id "$EXE_WIF_METADATA_FILE")"
export PROVIDER_ID="$(jq -r .provider_id "$EXE_WIF_METADATA_FILE")"
export SERVICE_ACCOUNT_EMAIL="$(jq -r .service_account "$EXE_WIF_METADATA_FILE")"
export GCP_PROVIDER_RESOURCE="projects/${PROJECT_NUMBER}/locations/global/workloadIdentityPools/${POOL_ID}/providers/${PROVIDER_ID}"
gcloud iam workload-identity-pools create-cred-config "$GCP_PROVIDER_RESOURCE" \
--service-account "$SERVICE_ACCOUNT_EMAIL" \
--credential-source-url "$EXE_WIF_URL/token" \
--credential-source-type json \
--credential-source-field-name token \
--output-file "$GOOGLE_APPLICATION_CREDENTIALS"
gcloud auth login --cred-file="$GOOGLE_APPLICATION_CREDENTIALS"
```
Smoke-test:
```
gcloud auth print-access-token >/dev/null &&
echo "GCP Workload Identity Federation is working"
```
For client libraries, keep `GOOGLE_APPLICATION_CREDENTIALS` in the workload
environment. The credential config tells Google auth libraries and `gcloud` to
fetch fresh exe.dev tokens from `GET /token`; no local service account key or
exe.dev token file is needed.
## Use cases
Common things to run from the VM once the federated credentials are active.
Grant the service account only the roles each use case needs.
### Cloud Storage: artifacts and data
Sync build outputs, datasets, or static sites to a bucket:
```
gcloud storage rsync --recursive ./dist "gs://${BUCKET_NAME}/"
```
Grant `roles/storage.objectAdmin` scoped to the bucket.
### Artifact Registry: containers and packages
Push container images, or publish to private npm, pip, or Maven repositories
in the same registry:
```
gcloud auth configure-docker "${REGION}-docker.pkg.dev"
docker push "${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO}/${IMAGE}:${TAG}"
```
Grant `roles/artifactregistry.writer`.
### BigQuery: queries and data jobs
Run queries and load jobs, or run dbt: its BigQuery `oauth` method uses
application default credentials, which the WIF credential file provides.
```
bq query --use_legacy_sql=false "SELECT COUNT(*) FROM \`${PROJECT_ID}.${DATASET}.${TABLE}\`"
```
Grant `roles/bigquery.jobUser` plus dataset-level access.
### Cloud SQL: databases via the Auth Proxy
The Cloud SQL Auth Proxy connects over the instance's public IP with IAM
authorization and TLS; no authorized networks or VPC needed. Connect to
localhost with your normal client or migration tool:
```
cloud-sql-proxy "${PROJECT_ID}:${REGION}:${INSTANCE}" &
psql "host=127.0.0.1 dbname=${DB_NAME} user=${DB_USER}"
```
Grant `roles/cloudsql.client`.
### Vertex AI: model inference
Call Gemini and other models with the federated credentials. Client libraries
pick up `GOOGLE_APPLICATION_CREDENTIALS` automatically:
```
curl -fsS -X POST \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
-H "Content-Type: application/json" \
-d '{"contents":[{"role":"user","parts":[{"text":"Hello"}]}]}' \
"https://${REGION}-aiplatform.googleapis.com/v1/projects/${PROJECT_ID}/locations/${REGION}/publishers/google/models/${MODEL}:generateContent"
```
Grant `roles/aiplatform.user`.
### Terraform / IaC: state and deploys
Run `terraform plan` and `terraform apply` with the GCS state backend; the
`google` provider reads the same credential file via application default
credentials:
```
terraform init -backend-config="bucket=${STATE_BUCKET}"
terraform apply
```
Grant access to the state bucket plus the roles for whatever the config
manages.
## Keep it narrow
- Use one exe.dev WIF integration per service account or workload.
- Attach the integration only to the VM or tag that needs it.
- Bind `roles/iam.workloadIdentityUser` to the exact exe.dev subject.
- Give the Google Cloud service account only the roles the workload needs.
- Do not create or store Google Cloud service account keys as a fallback.
Additional integrations owned by the same exe.dev user or team can reuse its
Workload Identity Pool and OIDC provider when they use the same provider
audience. A different exe.dev user or team has a different issuer and needs its
own provider, though it can use the same pool.
Google references:
- [Workload Identity Federation with other providers](https://cloud.google.com/iam/docs/workload-identity-federation-with-other-providers)
- [Create an OIDC workload identity pool provider](https://cloud.google.com/sdk/gcloud/reference/iam/workload-identity-pools/providers/create-oidc)
- [Create credential configurations](https://cloud.google.com/sdk/gcloud/reference/iam/workload-identity-pools/create-cred-config)