The most surprising truth about authenticating external workloads to GCP without service account keys is that you’re already doing it, and likely have been for years.
Let’s see it in action. Imagine you have an application running on AWS EC2, and it needs to talk to a Google Cloud Storage bucket. Traditionally, you’d create a service account in GCP, download its JSON key file, and securely store that file on your EC2 instance. Then, your application would load that key file to authenticate. This is brittle; key rotation is a pain, and a leaked key is a disaster.
Instead, we can leverage workload identity federation. Here’s a simplified scenario:
GCP Setup:
-
Create a Workload Identity Pool:
gcloud iam workload-identity-pools create my-external-pool \ --identity-provider-name=my-aws-provider \ --display-name="External Workloads Pool" \ --description="Pool for external workloads"This creates a container for federated identities.
-
Create a Workload Identity Provider (for AWS):
gcloud iam workload-identity-pools providers create-aws my-aws-provider \ --workload-identity-pool=my-external-pool \ --account-id=123456789012 \ --display-name="My AWS Provider"This tells GCP how to trust AWS. The
account-idis your AWS Account ID. -
Grant Permissions to a GCP Resource (e.g., a GCS bucket): We don’t grant permissions to the external workload directly. Instead, we grant permissions to a service account that the external workload will impersonate.
First, create a GCP service account:
gcloud iam service-accounts create workload-impersonator@my-project.iam.gserviceaccount.com \ --display-name "Workload Impersonator SA"Then, grant this service account the necessary role (e.g., Storage Object Viewer) on your bucket:
gcloud storage buckets add-iam-policy-binding gs://my-gcs-bucket \ --member="serviceAccount:workload-impersonator@my-project.iam.gserviceaccount.com" \ --role="roles/storage.objectViewer"Finally, grant the federated identity permission to impersonate this service account:
gcloud iam workload-identity-pools providers add-iam-policy-binding \ --workload-identity-pool=my-external-pool \ --provider=my-aws-provider \ --member="principalSet://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/my-external-pool/attribute.aws_account=123456789012" \ --role="roles/iam.workloadIdentityUser"PROJECT_NUMBERis your GCP project number. TheprincipalSetpart is crucial: it specifies that any AWS entity (from account123456789012) that successfully authenticates via our AWS provider can potentially impersonate the service account. We’ll refine this later using attribute conditions.
AWS Setup:
-
Create an IAM Role for your EC2 Instance: This role needs permission to call
sts:AssumeRoleWithWebIdentity. This is the AWS API call that allows an AWS entity (like an EC2 instance) to exchange its AWS credentials for a temporary GCP token.{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "sts:AssumeRoleWithWebIdentity", "Resource": "arn:aws:iam::123456789012:role/MyEC2Role" } ] }Attach this policy to an IAM role (e.g.,
MyEC2Role) that your EC2 instance assumes. -
Configure Trust Policy for the IAM Role: This is where you tell AWS who can assume this role. You’ll link it to your GCP Workload Identity Pool.
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Federated": "arn:aws:iam::123456789012:oidc-provider/YOUR_GCP_WORKLOAD_IDENTITY_POOL_ID" }, "Action": "sts:AssumeRoleWithWebIdentity", "Condition": { "StringEquals": { "YOUR_GCP_WORKLOAD_IDENTITY_POOL_ID:aud": "https://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/my-external-pool/audiences/my-aws-provider" } } } ] }Replace
YOUR_GCP_WORKLOAD_IDENTITY_POOL_IDwith the actual ID of your workload identity pool (e.g.,projects/1234567890/locations/global/workloadIdentityPools/my-external-pool). Theaud(audience) condition ensures that the token being presented is intended for your specific GCP pool and provider.
EC2 Instance (Your External Workload):
-
Install Google Cloud SDK: Ensure
gcloudis installed. -
Configure
gcloudto use Workload Identity Federation:gcloud auth login --no-launch-browser \ --cred-file=/path/to/aws/credentials \ --update-adc-config=workload_identity_federation gcloud config set auth/impersonate_service_account workload-impersonator@my-project.iam.gserviceaccount.com \ --auth-type=workload_identity_federationThe first command tells
gcloudhow to get temporary AWS credentials and how to exchange them. The second command tellsgcloudwhich GCP service account to impersonate using those federated credentials.
Now, any application running on that EC2 instance, using the default Google Cloud client libraries or gcloud commands, will automatically authenticate as workload-impersonator@my-project.iam.gserviceaccount.com and be able to access gs://my-gcs-bucket.
The mental model is that AWS (or any other OIDC provider) acts as a trusted identity broker. When your workload on AWS needs to access GCP, it first gets temporary AWS credentials. Then, it uses these credentials to request a short-lived, GCP-signed OIDC token from AWS. This token is sent to GCP’s Workload Identity Federation endpoint. GCP verifies the token against the configured provider, checks the trust policy and any conditions, and if valid, issues a short-lived GCP access token that allows the workload to impersonate the designated GCP service account.
The most powerful and often overlooked aspect of this setup is the ability to use attribute-based conditions for fine-grained access control. Instead of simply allowing any AWS principal from account 123456789012 to impersonate the GCP service account, you can add conditions to the add-iam-policy-binding command in GCP. For example, you could restrict access based on the AWS tag of the EC2 instance:
gcloud iam workload-identity-pools providers add-iam-policy-binding \
--workload-identity-pool=my-external-pool \
--provider=my-aws-provider \
--member="principalSet://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/my-external-pool/attribute.aws_account=123456789012" \
--role="roles/iam.workloadIdentityUser" \
--condition='expression=attribute.aws_tag.environment=="production",title=environment_tag_prod,description=Allow only if EC2 instance has environment=production tag'
This means the federated identity can only impersonate the GCP service account if the EC2 instance it originates from has a tag environment with the value production. This allows you to define policies in GCP that are dynamically enforced based on attributes of the external workload.
The next hurdle you’ll encounter is managing multiple distinct external workloads, each needing to impersonate different GCP service accounts based on varying attribute conditions.