Configure workload identity federation with deployment pipelines

This guide describes how to use workload identity federation to let deployment pipelines authenticate to Google Cloud.

Depending on the CI/CD system you're using, your deployment pipelines might have access to ambient, environment-specific credentials. For example:

  • GitHub Actions workflows can obtain a GitHub OIDC token that uniquely identifies the workflow and its repository.
  • GitLab SaaS lets CI/CD jobs access an ID token that uniquely identifies the job and its project, environment, and repository.
  • Terraform Cloud can provide an OIDC token to your your Terraform configuration that uniquely identifies the workspace and environment.

You can configure your deployment pipelines to use these credentials to authenticate to Google Cloud by using workload identity federation. This approach eliminates the maintenance and security burden associated with service account keys.

Prepare your external IdP

GitHub Actions

You don't need to make any configuration changes in your GitHub account.

After you configure a workload identity pool to trust your GitHub repository, you can let workflows in that repository use their GitHub OIDC token to obtain short-lived Google Cloud credentials.

GitLab SaaS

You don't need to make any configuration changes in your GitLab account.

After you configure a workload identity pool to trust your GitLab group, you can enable workload identity federation for individual CI/CD jobs.

Terraform Cloud

You don't need to make any configuration changes in your Terraform Cloud account.

After you configure a workload identity pool to trust Terraform Cloud, you can enable workload identity federation for individual workspaces.

Configure workload identity federation

You must perform these steps for each GitHub organization, GitLab group, or Terraform Cloud organization.

To start configuring workload identity federation, do the following:

  1. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  2. It's best to use a dedicated project to manage workload identity pools and providers.
  3. Make sure that billing is enabled for your Google Cloud project.

  4. Enable the IAM, Resource Manager, Service Account Credentials, and Security Token Service APIs.

    Enable the APIs

Define an attribute mapping and condition

The environment-specific credentials of your deployment pipeline can contain multiple attributes, and you must decide which attribute you want to use as subject identifier (google.subject) in Google Cloud.

Optionally, you can map additional attributes. You can then refer to these additional attributes when you grant access to resources.

GitHub Actions

Your attribute mappings can use any of the claims in the GitHub Actions OIDC token. These token claim keys and their values are controlled by GitHub. At minimum, you should map google.subject to assertion.sub, which corresponds to the GitHub Actions OIDC token subject:

google.subject=assertion.sub

The value for the GitHub Actions OIDC token subject can vary depending on the source event. Other claim attributes can include:

  • repository: Contains the owner and repository name–for example "google/guava".

  • repository_id: Contains the unique repository ID–for example "20300177".

  • repository_owner: Contains the owner, which can be a username or the name of a GitHub organization–for example "google".

  • repository_owner_id: Contains the unique owner ID–for example "1342004".

This list above is a subset of the possible claims–see the GitHub documentation on example claims for a complete list. Be sure to map any claims that you plan to use as attribute conditions or as part of a future principalSet condition.

GitLab SaaS

Your attribute mappings can use the claims embedded in the GitLab ID token as source attributes, including the following:

  • sub: the project name and Git reference—for example, project_path:groupname/projectname:ref_type:branch:ref:main.
  • namespace_id: the unique group ID.
  • project_id: the unique project ID.
  • user_id: the unique user ID.
  • environment: the environment that the job applies to.
  • ref_path: the Git reference—for example refs/heads/main.

The following attribute mapping sets google.subject to the sub claim from the GitLab ID token. Because the sub claim contains both the project name and Git reference, this mapping lets you control access by repository and branch:

google.subject=assertion.sub

Controlling access by repository and branch can be useful if certain branches (for example, main) need different access to resources than other branches (for example, feature branches).

In some cases, it might be sufficient to only differentiate access by project or group. The following mapping therefore includes two additional attributes that contain the GitLab project_id and namespace_id:

google.subject=assertion.sub
attribute.project_id=assertion.project_id
attribute.namespace_id=assertion.namespace_id

Terraform Cloud

Your attribute mappings can use the claims embedded in the Terraform Cloud OIDC token, including the following

  • terraform_organization_id: Contains the unique ID of the organization—for example org-xxxxxxxxxxxxxxxx.
  • terraform_workspace_id: Contains the unique ID of the workspace—for example ws-xxxxxxxxxxxxxxxx.
  • terraform_workspace_name: Contains the display name of the workspace.
  • sub: Contains the display name of the organization, workspace, and phase—for example organization:example-org:workspace:example-workspace:run_phase:apply.

The following attribute mapping sets google.subject to the terraform_workspace_id claim from the Terraform Cloud OIDC token:

google.subject=assertion.terraform_workspace_id

This mapping lets you control access to Google Cloud resources by workspace.

Attribute conditions are CEL expressions that can check assertion attributes and target attributes. If the attribute condition evaluates to true for a given credential, the credential is accepted. Otherwise, the credential is rejected. You must have an attribute mapping for all attribute condition fields.

GitHub Actions

Use the following attribute condition to restrict access to tokens issued by your GitHub organization:

assertion.repository_owner=='ORGANIZATION'

Replace ORGANIZATION with the name of your GitHub organization.

Optionally, extend the attribute condition to restrict access to a subset of workflows or branches. For example, the following condition limits access to workflows that use the Git branch main:

assertion.repository_owner=='ORGANIZATION' && assertion.ref=='refs/heads/main'

GitLab SaaS

Use the following attribute condition to restrict access to tokens issued by your GitLab group

assertion.namespace_id=='GROUP_ID'

Replace GROUP_ID with the group ID that's shown on your GitLab group's home page.

Optionally, extend the attribute condition to restrict access to a subset of projects, branches, or environments. For example, the following condition limits access to jobs that use the environment production:

assertion.namespace_id=='GROUP_ID' && assertion.environment=='production'

Terraform Cloud

Use the following attribute condition to restrict access to tokens issued by your Terraform Cloud organization:

assertion.terraform_organization_id=='ORGANIZATION_ID'

Replace ORGANIZATION_ID with the unique ID of your organization—for example org-xxxxxxxxxxxxxxxx. Optionally, extend the attribute condition to restrict access to a subset of workflows or branches. For example, the following attribute condition limits access to a specific workspace:

assertion.terraform_organization_id=='ORGANIZATION_ID' && terraform_workspace_id=='WORKSPACE_ID'

Create the workload identity pool and provider

Required roles

To get the permissions that you need to configure workload identity federation, ask your administrator to grant you the following IAM roles on the project:

For more information about granting roles, see Manage access.

You might also be able to get the required permissions through custom roles or other predefined roles.

Alternatively, the IAM Owner (roles/owner) basic role also includes permissions to configure identity federation. You should not grant basic roles in a production environment, but you can grant them in a development or test environment.

You've now collected all the information you need to create a workload identity pool and provider:

Console

  1. In the Google Cloud console, go to the New workload provider and pool page.

    Go to New workload provider and pool

  2. Under Create an identity pool, enter the following:

    • Name: Name for the pool. The name is also used as the pool ID. You can't change the pool ID later.
    • Description: Text that describes the purpose of the pool.
  3. Click Continue.

  4. Configure provider settings:

    GitHub Actions

    • Select a provider: OpenID Connect (OIDC).
    • Provider name: Name for the provider. The name is also used as the provider ID. You cannot change the provider ID later.
    • Issuer URL: https://token.actions.githubusercontent.com/
    • Audiences: Default audience

    GitLab SaaS

    • Select a provider: OpenID Connect (OIDC).
    • Provider name: Name for the provider. The name is also used as the provider ID. You cannot change the provider ID later.
    • Issuer URL: https://gitlab.com
    • Audiences: Default audience

    Terraform Cloud

    • Select a provider: OpenID Connect (OIDC).
    • Provider name: Name for the provider. The name is also used as the provider ID. You cannot change the provider ID later.
    • Issuer URL: https://app.terraform.io
    • Audiences: Default audience
  5. Click Continue.

  6. Under Configure provider attributes, add the attribute mappings that you've identified previously.

  7. Under Attribute conditions, enter the attribute condition that you've identified previously.

  8. Click Save to create the workload identity pool and provider.

gcloud

  1. Create a new workload identity pool:

    gcloud iam workload-identity-pools create POOL_ID \
        --location="global" \
        --description="DESCRIPTION" \
        --display-name="DISPLAY_NAME"
    

    Replace the following values:

    • POOL_ID: the unique ID for the pool.
    • DISPLAY_NAME: the name of the pool.
    • DESCRIPTION: the description of the pool. This description appears when granting access to pool identities.
  2. Add a workload identity pool provider:

    GitHub Actions

    gcloud iam workload-identity-pools providers create-oidc PROVIDER_ID \
        --location="global" \
        --workload-identity-pool="POOL_ID" \
        --issuer-uri="https://token.actions.githubusercontent.com/" \
        --attribute-mapping="MAPPINGS" \
        --attribute-condition="CONDITIONS"
    

    Replace the following values:

    GitLab SaaS

    gcloud iam workload-identity-pools providers create-oidc PROVIDER_ID \
        --location="global" \
        --workload-identity-pool="POOL_ID" \
        --issuer-uri="https://gitlab.com" \
        --attribute-mapping="MAPPINGS" \
        --attribute-condition="CONDITIONS"
    

    Replace the following values:

    Terraform Cloud

    gcloud iam workload-identity-pools providers create-oidc PROVIDER_ID \
        --location="global" \
        --workload-identity-pool="POOL_ID" \
        --issuer-uri="https://app.terraform.io" \
        --attribute-mapping="MAPPINGS" \
        --attribute-condition="CONDITIONS"
    

    Replace the following values:

Authenticate a deployment pipeline

You must perform these steps for each GitHub Actions workflow or Terraform Cloud workspace.

Create a service account for the deployment pipeline

  1. Enable the IAM, Security Token Service, and Service Account Credentials APIs.

    Enable the APIs

  2. Create a service account that represents the workload. We recommend that you use a dedicated service account for each deployment pipeline.

    The service account doesn't need to be in the same project as the workload identity pool.

  3. Grant the service account access to resources that you want external identities to access.

Allow the deployment pipeline to impersonate the service account

To allow external identities to impersonate a service account, you grant them the Workload Identity User role (roles/iam.workloadIdentityUser) on the service account. You can grant the role to a specific external identity, or to multiple external identities:

  • For a specific external identity, write an attribute condition that checks the google.subject attribute.
  • For a group of external identities, write an attribute condition that checks the google.groups attribute or a custom attribute attribute.NAME.

Console

To allow external identities to impersonate a service account using the Google Cloud console, do the following:

  1. In the Google Cloud console, go to the Workload Identity Pools page.

    Go to Workload Identity Pools

  2. Find the workload identity pool you want to update and select it.

  3. To grant access to the selected workload identity pool, click Grant access.

  4. In the Service account list, select the service account for the external identities to impersonate.

  5. To choose which identities in the pool can impersonate the service account, perform one of the following actions:

    • To allow only specific identities of the workload identity pool to impersonate the service account, select Only identities matching the filter.

      In the Attribute name list, select the attribute that you want to filter on.

      In the Attribute value field, enter the expected value of the attribute; for example, if you use an attribute mapping google.subject=assertion.sub, set Attribute name to subject and Attribute value to the value of the sub claim in tokens issued by your external identity provider.

  6. To save the configuration, click Save and then Dismiss.

gcloud

To allow external identities to impersonate a service account using gcloud CLI, do the following:

  1. To obtain the project number of your current project, execute the following command:

    gcloud projects describe $(gcloud config get-value core/project) --format=value\(projectNumber\)
    
  2. To grant the Workload Identity User role (roles/iam.workloadIdentityUser) to external identities that meet a certain criteria:

    By subject

    gcloud iam service-accounts add-iam-policy-binding SERVICE_ACCOUNT_EMAIL \
        --role=roles/iam.workloadIdentityUser \
        --member="principal://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/subject/SUBJECT"
    

    By group

    gcloud iam service-accounts add-iam-policy-binding SERVICE_ACCOUNT_EMAIL \
        --role=roles/iam.workloadIdentityUser \
        --member="principalSet://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/group/GROUP"
    

    By attribute

    gcloud iam service-accounts add-iam-policy-binding SERVICE_ACCOUNT_EMAIL \
        --role=roles/iam.workloadIdentityUser \
        --member="principalSet://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/attribute.ATTRIBUTE_NAME/ATTRIBUTE_VALUE"
    

    Replace the following:

    • SERVICE_ACCOUNT_EMAIL: the email address of the service account
    • PROJECT_NUMBER: the project number of the project that contains the workload identity pool
    • POOL_ID: the pool ID of the workload identity pool
    • SUBJECT: the expected value for the attribute that you've mapped to google.subject
    • GROUP: the expected value for the attribute that you've mapped to google.groups
    • ATTRIBUTE_NAME: the name of a custom attribute in your attribute mapping

Configure the deployment pipeline

You're now ready use workload identity federation in your deployment pipeline.

GitHub Actions

The google-github-actions/auth action lets you automatically generate a credential configuration file during workflow execution. Client libraries and tools such as terraform can then use this credential configuration file to automatically obtain Google credentials.

Edit your GitHub Actions YAML file and add the following:

  • Allow the job to fetch a GitHub ID token by adding the following configuration:

    permissions:
      id-token: write
      contents: read
    
  • Add a step to create a credentials configuration file:

    - id: 'auth'
      name: 'Authenticate to Google Cloud'
      uses: 'google-github-actions/auth@v1'
      with:
        create_credentials_file: true
        workload_identity_provider: 'projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/providers/PROVIDER_ID'
        service_account: 'SERVICE_ACCOUNT_EMAIL'
    

Replace the following values:

  • PROJECT_NUMBER: the project number of the project that contains the workload identity pool
  • POOL_ID: the ID of the workload identity pool
  • PROVIDER_ID: the ID of the workload identity pool provider
  • SERVICE_ACCOUNT_EMAIL: the email address of the service account

Example:

jobs:
  build:
    # Allow the job to fetch a GitHub ID token
    permissions:
      id-token: write
      contents: read

    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3

      - id: 'auth'
        name: 'Authenticate to Google Cloud'
        uses: 'google-github-actions/auth@v1'
        with:
          create_credentials_file: true
          workload_identity_provider: 'projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/providers/PROVIDER_ID'
          service_account: 'SERVICE_ACCOUNT_EMAIL'

For further details on using the google-github-actions/auth action, see Setting up Workload Identity Federation.

GitLab SaaS

Edit your .gitlab-ci.yml file and add the following to the job configuration:

job:
  variables:
    WORKLOAD_IDENTITY_PROJECT_NUMBER: PROJECT_NUMBER
    WORKLOAD_IDENTITY_POOL: POOL_ID
    WORKLOAD_IDENTITY_PROVIDER: PROVIDER_ID
    SERVICE_ACCOUNT: SERVICE_ACCOUNT_EMAIL
    GOOGLE_APPLICATION_CREDENTIALS: $CI_BUILDS_DIR/.workload_identity.wlconfig

  id_tokens:
    WORKLOAD_IDENTITY_TOKEN:
      aud: https://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/providers/PROVIDER_ID

  script:
    - |-
      echo $WORKLOAD_IDENTITY_TOKEN > $CI_BUILDS_DIR/.workload_identity.jwt
      cat << EOF > $GOOGLE_APPLICATION_CREDENTIALS
      {
        "type": "external_account",
        "audience": "//iam.googleapis.com/projects/$WORKLOAD_IDENTITY_PROJECT_NUMBER/locations/global/workloadIdentityPools/$WORKLOAD_IDENTITY_POOL/providers/$WORKLOAD_IDENTITY_PROVIDER",
        "subject_token_type": "urn:ietf:params:oauth:token-type:jwt",
        "token_url": "https://sts.googleapis.com/v1/token",
        "credential_source": {
          "file": "$CI_BUILDS_DIR/.workload_identity.jwt"
        },
        "service_account_impersonation_url": "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/$SERVICE_ACCOUNT:generateAccessToken"
      }
      EOF

Replace the following values:

  • PROJECT_NUMBER: the project number of the project that contains the workload identity pool
  • POOL_ID: the ID of the workload identity pool
  • PROVIDER_ID: the ID of the workload identity pool provider
  • SERVICE_ACCOUNT_EMAIL: the email address of the service account

The configuration does the following:

  1. Instruct GitLab to issue an ID token, and make it available in the environment variable named WORKLOAD_IDENTITY_TOKEN. The ID token uses your workload identity pool provider as audience.
  2. Save the ID token to a temporary file named .workload_identity.jwt.
  3. Create a credential configuration file that instructs client libraries to read the ID token from .workload_identity.jwt and use it to impersonate a service account.
  4. Set the environment variable GOOGLE_APPLICATION_CREDENTIALS to point to the credential configuration file.

Terraform Cloud

Configure your Terraform Cloud workspace so that it uses workload identity federation to authenticate to Google Cloud:

  1. In Terraform Cloud, open your workspace and go to Variables.

  2. Add the following variables:

    Variable category Key Value
    Environment variable TFC_GCP_PROVIDER_AUTH true
    Environment variable TFC_GCP_RUN_SERVICE_ACCOUNT_EMAIL The email address of the service account, for example terraform@my-project-123.iam.gserviceaccount.com
    Environment variable TFC_GCP_PROJECT_NUMBER The project number of the project that contains the workload identity pool
    Environment variable TFC_GCP_WORKLOAD_POOL_ID The ID of the workload identity pool
    Environment variable TFC_GCP_WORKLOAD_PROVIDER_ID The ID of the workload identity pool provider

    Optionally, you can add additional environment variables to let Terrform Cloud use different service accounts for the plan and apply phases. For more information see Optional Environment Variables.

  3. In the list of variables, verify that Category is set to env for the five variables that you added in the previous step.

  4. Verify that your Terraform configuration uses version 4.48.0 or newer of the Google Cloud provider, and update it if necessary, as follows:

    terraform {
      required_providers {
        google = {
          source  = "hashicorp/google"
          version = "~> 4.48.0"
        }
      }
    }
    
  5. Submit the changes to your source code repository.

What's next