Declarative Management of GCP Service Accounts and Terraform Authentication Strategies

Managing infrastructure on Google Cloud Platform (GCP) requires robust identity management to separate human access from automated workload execution. In the context of Infrastructure as Code, the interaction between Terraform and GCP identities is critical for security, auditability, and operational efficiency. The primary mechanism for this interaction is the GCP service account, a special type of Google account that belongs to a project rather than an individual user. Service accounts possess an email address, a key pair for authentication, and Identity and Access Management (IAM) role bindings that strictly determine their permissions. These accounts serve as the standard authentication method for automated workloads, including Terraform execution, CI/CD pipelines, and applications running directly on GCP. While personal Google accounts are suitable for experimentation, production environments demand dedicated identities with specific, least-privilege permissions to mitigate security risks and ensure proper access control.

Module Architecture and Declarative Provisioning

The terraform-google-service-accounts module is a specialized Infrastructure-as-Code solution designed to address the operational overhead of managing GCP service accounts. Maintained as part of the Cloud Foundation Toolkit, this module follows Google Cloud best practices for infrastructure management by providing a declarative interface for automating the provisioning of service accounts, assigning IAM roles at multiple scopes, generating optional keys, and distributing keys securely. The repository implements a modular architecture with three distinct entry points, each serving different service account management workflows. The root module utilizes Terraform's for_each meta-argument to manage multiple service accounts and their associated IAM bindings simultaneously, allowing for complex configurations in a single module instantiation.

The module is intended for use with Terraform version 0.13 and later, though it is tested and compatible with Terraform 1.0 and above. For users operating on legacy infrastructure using Terraform 0.12.x, the last released version intended for that specific major version is v3.0.1. The module triggers specific resource activations and deletions, including one or more service accounts, optional project-level IAM role bindings for each account, and optional billing IAM role bindings per service account at the organizational or billing account level. Furthermore, it supports two optional organization-level IAM bindings per service account, specifically to enable service accounts to create and manage Shared VPC networks. Each service account provisioned through this module can also have one optional service account key generated.

Feature Description
Module Source terraform-google-modules/service-accounts/google
Minimum Terraform Version 0.13
Recommended Terraform Version 1.0+
Legacy Support v3.0.1 for Terraform 0.12.x
Supported Scopes Project, Organization, Billing Account
Key Management Optional key generation per service account
VPC Support Optional Shared VPC network management bindings

To implement this module, users define a block in their Terraform configuration. A basic usage example involves specifying the source, version, project ID, a prefix for the service account names, and a list of names. Additionally, project roles can be assigned using a string format that maps specific project IDs to IAM roles. For instance, a configuration might assign the roles/viewer role to project-foo and the roles/storage.objectViewer role to project-spam.

hcl module "service_accounts" { source = "terraform-google-modules/service-accounts/google" version = "~> 4.0" project_id = "<PROJECT ID>" prefix = "test-sa" names = ["first", "second"] project_roles = [ "project-foo=>roles/viewer", "project-spam=>roles/storage.objectViewer", ] }

The module accepts various input variables to customize its behavior. A key variable is billing_account_id, which allows users to specify a particular billing account when assigning billing roles. If this variable is not set, the module defaults to assigning the role at the organizational level. This flexibility ensures that service accounts can be granted the necessary permissions to manage costs or access billing data across different administrative scopes within a Google Cloud organization.

Manual Provisioning and Role Assignment

While automation modules streamline the process, understanding the underlying manual procedures is essential for troubleshooting and initial setup. The gcloud command-line interface provides the foundational tools for creating service accounts and managing their permissions. Before creating a service account, the target project must be configured in the gcloud context.

```bash

Set the project context

export PROJECTID="my-project-123"
gcloud config set project $PROJECT
ID

Create the service account with a descriptive name

gcloud iam service-accounts create terraform \
--display-name="Terraform Service Account" \
--description="Used by Terraform to manage GCP infrastructure"

Verify the creation

gcloud iam service-accounts list
```

Once the service account exists, it possesses no permissions by default. Roles must be explicitly assigned using IAM policy bindings. For a general-purpose Terraform service account, the roles/editor role is sometimes used during initial setup to simplify the discovery of required permissions. However, this broad access is not recommended for production environments. Instead, a least-privilege approach should be adopted, where specific roles such as roles/cloudsql.admin are granted based on the resources Terraform needs to manage.

```bash

Grant a specific admin role

gcloud projects add-iam-policy-binding $PROJECTID \
--member="serviceAccount:terraform@${PROJECT
ID}.iam.gserviceaccount.com" \
--role="roles/cloudsql.admin"

Alternatively, grant broad editor access for initial setup (not recommended)

gcloud projects add-iam-policy-binding $PROJECTID \
--member="serviceAccount:terraform@${PROJECT
ID}.iam.gserviceaccount.com" \
--role="roles/editor"
```

When granting roles to attach a service account to other resources, such as Compute Engine instances, the principal performing the attachment (usually a human user) must have the roles/iam.serviceAccountUser role on the service account. This permission allows the user to specify the service account as the identity for the resource.

```bash

Grant the service account user role to a specific user

gcloud iam service-accounts add-iam-policy-binding SERVICEACCOUNTNAME@PROJECTID.iam.gserviceaccount.com \
--member="user:USER
EMAIL" \
--role=roles/iam.serviceAccountUser
```

Authentication Methods for Terraform

Configuring the Terraform Google provider to use the created service account involves selecting an appropriate authentication method. The choice of method impacts security, ease of use, and the operational environment in which Terraform runs. There are several supported methods, ranging from static key files to modern federation mechanisms.

Method 1: Service Account Key File

The traditional approach involves generating a JSON key for the service account and providing it to Terraform via a file path or environment variable. This method is straightforward but introduces the risk of managing long-lived credentials.

```bash

Create and download the key file

gcloud iam service-accounts keys create terraform-key.json \
--iam-account="terraform@${PROJECT_ID}.iam.gserviceaccount.com"

Secure the file permissions

chmod 600 terraform-key.json
```

In Terraform, the credentials can be passed directly in the provider block, although this is discouraged in favor of environment variables to avoid committing secrets to version control.

hcl provider "google" { project = var.project_id region = "us-central1" credentials = file("terraform-key.json") }

A more secure practice is to use environment variables. The GOOGLE_APPLICATION_CREDENTIALS variable points to the path of the JSON key file, while GOOGLE_CREDENTIALS can contain the raw JSON string. When these variables are set, the provider block does not need to specify the credentials attribute, as the provider reads from the environment.

```bash

Set credentials via environment variable pointing to file

export GOOGLEAPPLICATIONCREDENTIALS="/path/to/terraform-key.json"

Or set the raw JSON content

export GOOGLE_CREDENTIALS=$(cat terraform-key.json)
```

hcl provider "google" { project = var.project_id region = "us-central1" # Reads automatically from GOOGLE_APPLICATION_CREDENTIALS or GOOGLE_CREDENTIALS }

It is critical to note that service account keys are long-lived credentials. They do not expire automatically and must be manually rotated or deleted. If a key is compromised, it grants immediate access to the service account's permissions until revoked. Therefore, this method should be reserved for scenarios where keyless authentication is not feasible.

Method 2: Service Account Impersonation

Service account impersonation allows a user with appropriate permissions to act as another service account without requiring a key file for that specific service account. This is particularly useful for local development or when using Application Default Credentials (ADC). To use this method, the user must have the roles/iam.serviceAccountTokenCreator IAM role on the target service account.

To set up local ADC using impersonation, the following command is executed:

bash gcloud auth application-default login --impersonate-service-account SERVICE_ACCT_EMAIL

This command stores the impersonation credentials in the local ADC file. Terraform will then use these credentials automatically if no other specific provider credentials are defined. For environments where a shared primary authentication source is used, but the service account varies per environment, the impersonate_service_account field in the Terraform configuration can be set directly.

hcl provider "google" { impersonate_service_account = "SERVICE_ACCT_EMAIL" }

This approach reduces the number of static keys in circulation and relies on the identity of the caller (which may be a user or another service account) to obtain temporary tokens for the target service account.

Method 3: Workload Identity Federation

For applications and Terraform executions running outside of Google Cloud, such as on-premises data centers or other cloud providers, Workload Identity Federation is the preferred authentication method. This mechanism allows external Identity Providers (IdP) to issue credentials that Google Cloud accepts, eliminating the need for storing service account keys in non-GCP environments. This method provides a secure bridge between external CI/CD systems and GCP resources, ensuring that credentials are short-lived and scoped appropriately.

Authentication in Cloud Environments

When running Terraform within Google Cloud services, the authentication process is streamlined by the infrastructure itself. In Cloud Shell, for example, Terraform uses the credentials provided during the user's sign-in process. There is no need to configure service account keys or set environment variables for credentials, as the environment is already authenticated with the user's identity or the Cloud Shell instance's identity.

Similarly, when running Terraform on Compute Engine, App Engine, or Cloud Run functions, the service accounts attached to these resources provide the necessary credentials. Generally, attaching a service account is supported when the service's resources can run or include application code. In these scenarios, the google provider will automatically detect the metadata server or the attached service account and use it for authentication, provided that the attached service account has the necessary IAM roles to perform the desired operations. This "keyless" approach within GCP is the most secure and operationally simple method for automated infrastructure management.

Troubleshooting Common Authentication Errors

Despite careful configuration, errors frequently arise during the interaction between Terraform and GCP IAM. Understanding the specific error messages is crucial for resolution.

Error Message Cause Resolution
Permission Denied The service account lacks the specific IAM role required for the API call. Check the error message for the specific permission needed. List current roles using gcloud projects get-iam-policy with filters to identify missing roles.
API Not Enabled The GCP API required for the resource is not enabled in the project. Enable the specific API using gcloud services enable. Common APIs include compute.googleapis.com, container.googleapis.com, sqladmin.googleapis.com, and storage.googleapis.com.
Service Account Does Not Exist Incorrect email format or the service account was deleted. Verify the email format is NAME@PROJECT_ID.iam.gserviceaccount.com. Check the service account list to confirm existence.

For permission issues, users should inspect the current IAM policy to verify bindings. The following command helps filter the policy to show only the roles bound to a specific service account:

bash gcloud projects get-iam-policy $PROJECT_ID \ --flatten="bindings[].members" \ --filter="bindings.members:terraform@${PROJECT_ID}.iam.gserviceaccount.com" \ --format="table(bindings.role)"

If the error indicates an API is not enabled, the resolution is administrative rather than credential-related. Users must enable the corresponding service for the project. For example, if managing Cloud SQL instances, the sqladmin.googleapis.com API must be active.

Conclusion

The management of GCP service accounts through Terraform is a multifaceted process that involves provisioning identities, assigning granular permissions, and selecting the appropriate authentication method. The terraform-google-service-accounts module provides a robust, declarative framework for handling the lifecycle of these identities, supporting complex scenarios such as multi-project role bindings and shared VPC management. By leveraging the module, organizations can enforce consistent access control policies and reduce manual configuration errors.

Authentication strategies must be chosen based on the execution environment. For on-premises or external CI/CD systems, Workload Identity Federation offers the highest security posture by eliminating long-lived keys. For local development, service account impersonation or ADC setup provides flexibility. Within GCP, attached service accounts offer a seamless, keyless experience. Regardless of the method, the core principle remains the same: minimize the use of static keys, prefer short-lived credentials or federation, and strictly adhere to the principle of least privilege in IAM role assignment. By combining the automation capabilities of the Terraform module with secure authentication practices, technical teams can build resilient, secure, and auditable infrastructure management pipelines on Google Cloud.

Sources

  1. terraform-google-service-accounts
  2. How to Configure GCP Provider with Service Account
  3. Terraform Authentication
  4. terraform-google-service-accounts GitHub

Related Posts