docs/en/enterprise/features/secrets-manager/gcp-workload-identity.mdx
This guide configures Google Cloud Secret Manager as a secret provider using Workload Identity Federation: CrewAI Platform mints short-lived OIDC tokens, exchanges them for Google Cloud credentials via the Security Token Service, and reads your secrets — without a long-lived service account key being stored anywhere.
<Note> **Why this path:** secrets are resolved at automation execution time, so **rotated values propagate to the next kickoff with no re-deploy**. If you only need static credentials, see the simpler [GCP — service account key](/en/enterprise/features/secrets-manager/gcp) guide. </Note>secretmanager.googleapis.com:accessSecretVersion to read the secret, using the federated credential directly (the federated principal holds roles/secretmanager.secretAccessor — see Step 4).OIDC subject tokens are cached for ~1 hour to avoid re-issuing on every kickoff. Secret values are fetched fresh on every kickoff regardless of OIDC cache state, which is what makes this path rotation-aware.
The automation pod image must include CrewAI runtime version 1.14.5 or later.
A Google Cloud project with the Secret Manager API, Security Token Service API, and IAM Credentials API enabled. Enable them via the console or:
gcloud services enable secretmanager.googleapis.com sts.googleapis.com iamcredentials.googleapis.com \
--project=<YOUR_PROJECT_ID>
Permission in the project to create Workload Identity Pools, IAM roles, service accounts, and (if needed) secrets.
A CrewAI Platform organization where your user has the workload_identity_configs: manage and secret_providers: manage permissions. See Permissions (RBAC).
Your CrewAI Platform installation must be reachable from Google Cloud over HTTPS so that GCP STS can fetch the OIDC discovery document and JWKS during token validation. Confirm with your platform administrator that the host is internet-accessible.
</Note>Your CrewAI Platform installation publishes an OpenID Connect discovery document at https://<your-platform-host>/.well-known/openid-configuration. The issuer field there is the URL Google will register as a trusted OIDC provider.
Open the URL in a browser:
https://<your-platform-host>/.well-known/openid-configuration
You should see JSON containing:
{
"issuer": "https://<your-platform-host>",
"jwks_uri": "https://<your-platform-host>/oauth2/jwks",
...
}
Note the exact value of issuer — you'll use it in Step 3.
A Workload Identity Pool is a Google Cloud-side container for trusted external identities. You'll register CrewAI Platform as a provider inside this pool.
gcloud iam workload-identity-pools create crewai-pool \
--project=<YOUR_PROJECT_ID> \
--location=global \
--display-name="CrewAI Platform"
Or in the Workload Identity Pools console, click Create Pool.
gcloud iam workload-identity-pools providers create-oidc crewai-provider \
--project=<YOUR_PROJECT_ID> \
--location=global \
--workload-identity-pool=crewai-pool \
--display-name="CrewAI Platform OIDC" \
--issuer-uri="https://<your-platform-host>" \
--attribute-mapping="google.subject=assertion.sub,attribute.organization=assertion.organization_id" \
--attribute-condition="assertion.organization_id != ''"
The --attribute-mapping tells Google how to map JWT claims into Google attributes:
google.subject is the principal identifier — we map it to the JWT's sub claim, which CrewAI Platform sets to organization:<uuid>.attribute.organization is a custom attribute — we map it to the JWT's organization_id claim so you can reference it in IAM bindings later.The --attribute-condition is a defense-in-depth check that rejects tokens missing an organization_id claim.
Get the provider resource name (you'll need it for the audience and IAM bindings):
gcloud iam workload-identity-pools providers describe crewai-provider \
--project=<YOUR_PROJECT_ID> \
--location=global \
--workload-identity-pool=crewai-pool \
--format="value(name)"
Output looks like:
projects/<PROJECT_NUMBER>/locations/global/workloadIdentityPools/crewai-pool/providers/crewai-provider
This is your Workload Identity Provider value for CrewAI Platform in Step 6. CrewAI Platform automatically computes the OIDC audience as //iam.googleapis.com/<this-resource-name> when issuing tokens.
Bind both Secret Manager roles at project scope to the federated principal — one role enables the Secret Name autocomplete in the env-var form, the other allows reading secret values at automation kickoff. Both are required for the feature to work end-to-end.
PRINCIPAL_SET="principalSet://iam.googleapis.com/projects/<PROJECT_NUMBER>/locations/global/workloadIdentityPools/crewai-pool/attribute.organization/<YOUR_CREWAI_ORG_UUID>"
# Required for the Secret Name autocomplete (calls secretmanager.secrets.list)
gcloud projects add-iam-policy-binding <YOUR_PROJECT_ID> \
--member="$PRINCIPAL_SET" \
--role="roles/secretmanager.viewer"
# Required to read secret values at kickoff
gcloud projects add-iam-policy-binding <YOUR_PROJECT_ID> \
--member="$PRINCIPAL_SET" \
--role="roles/secretmanager.secretAccessor"
Replace <PROJECT_NUMBER> with the numeric project number (gcloud projects describe <YOUR_PROJECT_ID> --format='value(projectNumber)') and <YOUR_CREWAI_ORG_UUID> with the UUID of the CrewAI Platform organization that should be allowed to read your secrets. You can find the org UUID in the platform UI on the organization's settings page, or via the API. This scopes federation to a specific CrewAI organization — only tokens minted for that org's automations are accepted.
Or via the Google Cloud console:
principalSet://...attribute.organization/<YOUR_CREWAI_ORG_UUID> string.roles/secretmanager.viewer).roles/secretmanager.secretAccessor).gcloud secrets add-iam-policy-binding <SECRET_NAME> \
--member="$PRINCIPAL_SET" \
--role="roles/secretmanager.secretAccessor" \
--project=<YOUR_PROJECT_ID>
Keep roles/secretmanager.viewer at the project scope either way — secretmanager.secrets.list (which the autocomplete relies on) cannot be granted per-secret.
</Tip>
If you don't already have a secret to test against, create one via the gcloud CLI:
echo -n "hello from gcp" | gcloud secrets create crewai-test-keyword \
--data-file=- \
--project=<YOUR_PROJECT_ID> \
--replication-policy=automatic
Or via the Secret Manager console:
crewai-test-keyword. Secret value: paste your value.In CrewAI Platform, navigate to Settings → Workload Identity and click Add Workload Identity Config.
Fill the form:
gcp-prod.GCP.projects/<PROJECT_NUMBER>/locations/global/workloadIdentityPools/crewai-pool/providers/crewai-provider.Click Create.
Navigate to Settings → Secret Provider Credentials and click Add Credential.
Fill the form:
gcp-prod-wi.Google Cloud Secret Manager.Workload Identity.The form will only ask for Project ID under Workload Identity — the Service Account JSON field is intentionally hidden because it doesn't apply to this path; the federated identity comes from the linked WI config.
Click Create.
After saving the credential, click Test Connection. For workload-identity credentials this verifies the OIDC handshake: CrewAI Platform mints a JWT and exchanges it via the Security Token Service for a federated Google access token. A green result means the federation binding is healthy.
A successful Test Connection proves the Workload Identity Pool, OIDC provider, attribute mapping, and attribute condition are all wired correctly. It does not prove Secret Manager IAM is correct — secretmanager.secrets.list and secretmanager.versions.access are exercised separately when the Secret Name autocomplete loads or when an environment variable resolves at kickoff. See Troubleshooting for handshake failure modes.
Reference the secret on an automation, exactly as you would for any other Secrets Manager-backed env var. See Using the Secrets Manager for the form fields and behavior.
After the deployment is running, rotate the secret in GCP by adding a new version (Secret Manager always reads the latest enabled version by default):
echo -n "rotated value" | gcloud secrets versions add crewai-test-keyword \
--data-file=- \
--project=<YOUR_PROJECT_ID>
Trigger a new automation kickoff. The kickoff's environment will see "rotated value" — no re-deploy, no worker restart, no TTL wait.
To confirm in worker logs, look for:
Workload identity config '<id>' (gcp): N secret(s) resolved
This line appears for every kickoff and indicates a fresh accessSecretVersion call against GCP.
| Symptom | Likely cause |
|---|---|
| Test Connection fails with a handshake error | The STS token exchange was rejected. Verify the Workload Identity Pool exists, the OIDC provider's issuer matches the platform's issuer value, and the attribute condition accepts the JWT's claims. Confirm the platform's OIDC discovery URL is reachable from GCP over the public internet. |
Could not refresh access token: invalid_target | The audience claim doesn't match the Workload Identity Provider's expected audience. CrewAI Platform sets the audience automatically; if you customized it, ensure it matches //iam.googleapis.com/<provider-resource-name>. |
Failed to fetch JWKS from issuer | GCP STS can't reach your CrewAI Platform host. Confirm the host is internet-accessible and /.well-known/openid-configuration returns 200. |
Attribute condition rejected token | The OIDC provider's attribute condition (Step 3) requires organization_id. CrewAI Platform always sets this claim, so this usually means a misconfigured pool/provider. Re-check the provider's attribute condition. |
Secret Name autocomplete shows PERMISSION_DENIED: secretmanager.secrets.list | The federated principal is missing roles/secretmanager.viewer at project scope. The secretmanager.secrets.list permission is project-scoped only and cannot be granted per-secret. See Step 4. |
| Kickoff fails to resolve a secret even though Test Connection passes | The WI binding is healthy, but secretmanager.versions.access is missing on the failing secret. Audit roles/secretmanager.secretAccessor (project-scoped, or per-secret if you scoped it that way in Step 4). |
| Rotated value isn't picked up on the next kickoff | Confirm the env var on the automation is referencing a Workload Identity-backed credential (not a static-keys credential). The static path bakes values into the deploy image. |