Package: gs-aws-to-gcp-workload-identity
AWS to GCP Workload Identity FederationIf you have workloads running in AWS and want to access GCP services without exporting static GCP service account credentials, this module can leverage workload identity federation to dynamically exchange credentials from an IAM Role in AWS for either an OAuth2 Access Token or Identity Token for a given GCP Service Account.Getting StartedAWS SetupSet some shell variables for AWS related items for easier setup:exportAWS_ACCOUNT_ID="123456789012"exportAWS_ROLE_NAME="demo-wif-iam-role"exportAWS_ASSUME_ROLE_ARN="arn:aws:sts::${AWS_ACCOUNT_ID}:assumed-role/${AWS_ROLE_NAME}"Note that theAWS_ROLE_NAMEcan be the name of an existing role (for example, attached to an EC2 instance or Lambda) or you can create a new one.  It needs no specific policies attached on the AWS side, but you can if that role needs to use AWS services as well.Here's an example role that can be attached to EC2 or Lambda with no AWS related policies attached:awsiamget-role--role-namedemo-wif-iam-role{"Role":{"Path":"/","RoleName":"demo-wif-iam-role","RoleId":"AROA3K4K4NKZMWMTAAMLD","Arn":"arn:aws:iam::123456789012:role/demo-wif-iam-role","CreateDate":"2022-07-08T15:38:50+00:00","AssumeRolePolicyDocument":{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"Service":["ec2.amazonaws.com","lambda.amazonaws.com"]},"Action":"sts:AssumeRole"}]},"Description":"Allows EC2 instances to call AWS services on your behalf.","MaxSessionDuration":3600,"Tags":[{"Key":"env","Value":"dev"}],"RoleLastUsed":{"LastUsedDate":"2022-07-29T14:40:18+00:00","Region":"us-east-1"}}}GCP SetupSet some shell variables for GCP related items:exportGCP_PROJECT_ID="my-project-id"exportGCP_WORKLOAD_POOL_ID="aws-to-gcp-pool"exportGCP_WORKLOAD_PROVIDER_ID="aws-to-gcp-provider"exportGCP_SA_NAME="my-wif-sa"Authenticate withgcloudas anroles/owneron the target GCP project, and set the current project:gcloudconfigsetproject"${GCP_PROJECT_ID}"Obtain the GCP Project Number:exportGCP_PROJECT_NUMBER="$(gcloudprojectsdescribe${GCP_PROJECT_ID}--format='value(projectNumber)')"Enable the necessary services in the project:gcloudservicesenablests.googleapis.comiamcredentials.googleapis.comCreate the Workload Identity Pool:gcloudiamworkload-identity-poolscreate"${GCP_WORKLOAD_POOL_ID}"\--location="global"\--display-name="${GCP_WORKLOAD_POOL_ID}"\--description="${GCP_WORKLOAD_POOL_ID}"Create anawsProvider in the Workload Identity Pool:gcloudiamworkload-identity-poolsproviderscreate-aws"${GCP_WORKLOAD_PROVIDER_ID}"\--account-id="${AWS_ACCOUNT_ID}"\--location="global"\--workload-identity-pool="${GCP_WORKLOAD_POOL_ID}"Create a dedicated GCP service account that the AWS IAM Role will be connected to:exportGCP_SA_EMAIL="${GCP_SA_NAME}@${GCP_PROJECT_ID}.iam.gserviceaccount.com"gcloudiamservice-accountscreate"${GCP_SA_NAME}"\--display-name="${GCP_SA_NAME}"\--description="WIF from AWS to${GCP_SA_NAME}"Or reference an existing GCP service account:exportGCP_SA_EMAIL="my-existing-sa@my-project-id.iam.gserviceaccount.com"Grant permissions directly on the service account so the AWS Role ARN can exchange STS for its GCP token(s):gcloudiamservice-accountsadd-iam-policy-binding"${GCP_SA_EMAIL}"\--member"principalSet://iam.googleapis.com/projects/${GCP_PROJECT_NUMBER}/locations/global/workloadIdentityPools/${GCP_WORKLOAD_POOL_ID}/attribute.aws_role/${AWS_ASSUME_ROLE_ARN}"\--roleroles/iam.workloadIdentityUserGrant permissions as usual for the GCP service account to access services in GCP.  Next, list the workload identity pools/providers to get the fullgcp_workload_provider_pathto use in the module:forpoolin"$(gcloudiamworkload-identity-poolslist--locationglobal--format='value(name)')";dogcloudiamworkload-identity-poolsproviderslist--locationglobal--workload-identity-pool="${pool}"--format='value(name)';doneprojects/4556456456456/locations/global/workloadIdentityPools/aws-to-gcp-pool/providers/aws-to-gcp-providerUsage#!/usr/bin/env python3fromgs_aws_to_gcp_workload_identity.mainimportAwsToGcpTokenServiceaws_to_gcp_token_service=AwsToGcpTokenService(gcp_workload_provider_path="<insert full workload identity path>",gcp_service_account_email="<insert GCP service account email>",aws_assume_role_arn=None# Insert a role ARN to explicitly assume first if needed)# Most GCP services take the OAuth2 access token as the Bearer Tokenaccess_token,expiry_utc_epoch=aws_to_gcp_token_service.get_oauth_token()# Certain services like GCP Functions require Identity Tokens for Authenticated# invocations and need the function URL in the token's `audience` fieldfunction_url="https://us-central1-my-project-id.cloudfunctions.net/hello_world"id_token,expiry_utc_epoch=aws_to_gcp_token_service.get_identity_token(audience=function_url)# Call GCP services hereSamplesSeeSAMPLES.mdfor working examples.FAQWhat about performance and rate limits?- Performing this token exchange requires several calls to AWS' STS APIs and GCP's STS/IAM APIs, so this module caches the credentials after the first exchange and refreshes them only if they are expired or under 300s from expiring.  This should help reduce the API quote/rate limit usage.  Running hundreds of Lambdas at once is likely to hit some of these, for example.