Secure your connection between Google Cloud and Terraform Cloud avoiding the exposure of your service account credentials.

Workload Identity Federation is a security feature provided by Google Cloud that allows you to securely authenticate between applications and Google Cloud services without exposing long-lived service account credentials. Traditionally, applications and services running on compute instances within Google Cloud used service account credentials to access other Google Cloud services. However, these credentials can be a security risk if compromised.

Workload Identity replaces the need for using long-lived service account keys and tokens. Instead, it uses short-lived credentials tied to the lifecycle of a compute instance or a Kubernetes pod. These credentials are automatically rotated and managed by Google Cloud, reducing the risk associated with credential exposure.

Luckily, Terraform Cloud also implements Dynamic Provider Credentials, which allows Terraform to acquire temporary security credentials dynamically from a cloud provider’s authentication system, enabling secure and short-lived access to cloud resources without the need to store or manage long-lived credentials within the Terraform configuration.

Benefits of Workload Identity

Integrating Terraform with Google Cloud using Workload Identity brings forth a multitude of benefits:

  • Enhanced Security: Workload Identity minimizes the risk of long-lived service account credentials being exposed and misused. It reduces the attack surface by using short-lived credentials that are rotated automatically.

  • Simplified Credential Management: With Workload Identity, there’s no need to manage and rotate service account keys manually. Google Cloud handles the rotation of credentials, freeing up administrative overhead.

  • Least Privilege Principle: Workload Identity allows you to apply the principle of least privilege more effectively. Each application or service only gets access to the resources it truly needs, limiting potential damage in case of a breach.

Setting up the infrastructure

Firstly, you need to set up an identity pool in Google Cloud. This resource lets you manage external identities.

In general, it is recommended to create a new pool for each non-Google Cloud environment that needs to access Google Cloud resources, such as development, staging, or production environments.

gcloud iam workload-identity-pools create demo-pool \
    --location="global" \
    --description="Pool for demo purposes" \
    --display-name="Demo Pool"

Right after creating the pool, it needs a workload identity pool provider. We will explain further the attribute mapping and condition fields.

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"

Attribute mapping

The attribute-mapping variable maps attributes from authentication credentials issued by Terraform Cloud to Google Cloud attributes. The full list of Terraform Cloud attributes can be found here. You can map to the following Google Cloud attributes:

  • google.subject: Mandatory. It must be mapped in order for Google to know who is the subject making the request. In the case of Terraform, we map it to assertion.sub. Even if it’s mandatory, this value won’t impact our architecture unless you set it as the condition to the principalset IAM binding, which we will see later.

  • google.groups: Optional. You could use this value to then give access to resources depending on this group. Same as google.subject, it won’t impact our architecture unless we use this group to filter who has access to our service account.

  • attribute.NAME: You can create new attributes that Google does not yet have so you can map them to those that Terraform provides which will allow you to set conditions on who can access the service account that will be impersonated by the pool. In our case, we will want to map the workspace name so that each workspace can only access its selected service account.

Therefore we will add the following two mappings:

"google.subject" = "assertion.sub"
"attribute.terraform_workspace_name" = "assertion.terraform_workspace_name"

Attribute condition

The attribute condition creates a check that need to be satisfied in order for a request to be considered. Later on, we will filter by Terraform Workspace, but for now, we will set the condition so that only our Terraform Cloud organization can access the pool. Consequently, we create the provider as such:

gcloud iam workload-identity-pools providers create-oidc demo-provider \
 --location="global" \
 --workload-identity-pool=demo-pool \
 --issuer-uri="https://app.terraform.io" \
 --attribute-mapping="google.subject=assertion.sub,attribute.terraform_workspace_name=assertion.terraform_workspace_name" \
 --attribute-condition="assertion.terraform_organization_name==\"name-of-your-terraform-organization\""

Service account

We need a service account to be impersonated by the pool. The workflow is the following:

  • Terraform cloud will receive a request from us to create some resources

  • Terraform will provide Google Cloud with the information about the organization, project, workspace, etc.

  • Google Cloud will check that the conditions are met and will allow Terraform to run the commands with the service account given.

We create the service account and give it instance admin role so it can create VMs. This will allow us to test the workload identity afterward.

gcloud iam service-accounts create demo-workload-identity \
 --display-name="Demo workload identity"

gcloud projects add-iam-policy-binding your-project-id \
 --member serviceAccount:demo-workload-identity@your-project-id.iam.gserviceaccount.com \
 --role

Allowing Terraform to connect to Google Cloud with Workload Identity

To allow Terraform Cloud to impersonate the service account, we need to give it that permission. We can check the section of the corresponding documentation here. There are three ways we can do this: by subject, by group, or by attribute.

By subject

Remember we mapped google.subject with assertion.sub? Here we can set the subject so that if it’s the same by that one stored in google.subject, the condition will be met. The command will be the following substituting the values in caps. Since we have already checked the organization, we will simply check the attribute.

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/organization:my-org:project:Default Project:workspace:my-workspace:run_phase:apply"

By attribute

We can set the condition by the attribute we mapped earlier, the terraform workspace. Applying this condition to the service account, that specific workspace in Terraform Cloud will be able to impersonate the service account through the pool (it also needs to be from the same Terraform Organization we set the condition before).

Before applying the command we create the terraform workspace. We called it workload-identity-demo.

Newly Terraform workspace


gcloud iam service-accounts add-iam-policy-binding demo-workload-identity@your-project-id.iam.gserviceaccount.com \
 --role=roles/iam.workloadIdentityUser \
 --member="principalSet://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/demo-pool/attribute.terraform_workspace_name/workload-identity-demo"

Setting up Terraform to connect to Google Cloud with Workload Identity

The last step is creating the proper variables in Terraform Cloud so that it knows the pool and service account it needs to impersonate. For that, we need to set these three variables:

  • TFC_GCP_PROVIDER_AUTH: Set to true so that Terraform connects to Google Cloud using Workload Identity.

  • TFC_GCP_RUN_SERVICE_ACCOUNT_EMAIL: The service account email to impersonate, in this case: demo-workload-identity@your-project-id.iam.gserviceaccount.com

  • TFC_GCP_WORKLOAD_PROVIDER_NAME: In this variable, we tell Terraform the google cloud project where the pool is, the id of the pool and the id of the provider created before. The format is:
    projects/PROJECT-NUMBER/locations/global/workloadIdentityPools/demo-pool/providers/demo-provider


Screenshot of workspace variables from a configuration dashboard showing three key-value pairs for Terraform with Google Cloud Platform authentication, service account email, and workload provider name, categorized as environment variables.

After this, every is set up and we can already use our Terraform workspace to create resources in Google cloud.

Testing the Workload Identity

We created a simple terraform file and applied it to see if all was working. Down here you can check the code for the VM.

resource "google_compute_instance" "default" {
  project      = "ornate-magnet-376615"
  name         = "test"
  machine_type = "e2-medium"
  zone         = "europe-west1-b"

  boot_disk {
    initialize_params {
      image = "debian-cloud/debian-11"
      labels = {
        my_label = "value"
      }
    }
  }

  network_interface {
    network = "default"

    access_config {
      // Ephemeral public IP
    }
  }
}

We apply and…

Terminal output displaying the message 'Apply complete! Resources: 1 added, 0 changed, 0 destroyed.', indicating the successful execution of a script, likely in a deployment or provisioning tool such as Terraform.

Conclusion

Implementing Workload Identity at Astrafy was a huge success in avoiding using service account keys in Terraform Cloud. It made our infrastructure and those of our clients more secure. It also reduced the time we spent thinking and configuring key management and key rotation.

We also developed a module to automate all the processes explained in this article so that the workspaces, service accounts, pools, and bindings are created seamlessly, saving us a lot of time when we want to create a new workspace in Terraform that needs to connect to Google Cloud. If you are interested in that let us know and we could write another article about that as well!

I have used the following resources and documentation to make this tutorial:

If you are looking for support on Data Stack or Google Cloud solutions, feel free to reach out to us at sales@astrafy.io.