How to Create a Service Account for Terraform in GCP (IAM + JSON Key)

Step-by-step guide to create a GCP service account for Terraform: IAM roles, (optional) impersonation, generating a JSON key for local runs, and configuring the Terraform Google provider.

A Service Account is a special kind of account used by an application (Terraform in this case) to make authorized API calls. It is identified by its email address, which is unique to the account.

In this guide we will learn how to create and manage service accounts using the Identity and Access Management (IAM) API, the Google Cloud console, and the gcloud command-line tool. By default, each project can have up to 100 service accounts that control access to your resources. You can request a quota increase if necessary.

These are the main differences between service accounts and normal user accounts:

  • Service Accounts do not have passwords, and cannot log in via browsers.
  • Service Accounts are associated with private/public RSA key pairs that are used for authentication to Google.

Before we start deploying our Terraform code for GCP (Google Cloud Platform), we will need to create and configure a Service Account in the Google Console and set up a terraform provider to use it.

In this example, we will create a master Service Account with permissions at Organization-level and Project-level.

  For our example, we will grant a lot of permissions to the service account that you might not need in your case. Feel free to limit the permissions.  

Prerequisites

To follow along, ensure that you have the following:

  • A Project to create the Service Account
  • If you are using a service account, Install or update to the latest version of the Google Cloud CLI.
  • The account you are using should also have enough role permissions to create the service account

Required roles

To get the permissions that you need to manage service accounts, ask your administrator to grant you the following IAM roles on the project:

  • To view service accounts and service account metadata: View Service Accounts (roles/iam.serviceAccountViewer)
  • To view and create service accounts: Create Service Accounts (roles/iam.serviceAccountCreator)
  • To view and delete service accounts: Delete Service Accounts (roles/iam.serviceAccountDeleter)
  • To fully manage (view, create, update, disable, enable, delete, undelete, and manage access to) service accounts: Service Account Admin (roles/iam.serviceAccountAdmin)

Creating a Service Account

When you create a service account, you must provide an alphanumeric ID (SA_NAME in the samples below), such as terraform-user. The ID must be between 6 and 30 characters, and can contain lowercase alphanumeric characters and dashes. After you create a service account, you cannot change its name.

The service account’s name appears in the email address that is provisioned during creation, in the format SA_NAME@PROJECT_ID.iam.gserviceaccount.com.

Each service account also has a permanent, unique numeric ID, which is generated automatically.

To create a service account in Console, click on the IAM & Admin menu, Service Accounts option, and finally, on the + Create Service Accountbutton.

Enter a name and description for the Service Account and click the CREATE button.

Next, I will grant the Service Account access to the project. I will add the following Roles and click the CONTINUE button. Feel free to add more or remove more permissions in your case if you do not need them

  • Compute Storage Admin - Full access to Google Cloud Storage
  • Compute Admin - Full control of Compute Engine resources (Virtual Machines)
  • Kubernetes Engine Admin - Full management of Kubernetes Clusters

Finally, grant users access to the service account. After you create a service account, you might need to wait for 60 seconds or more before you use the service account.

Once the user is created, click on the + CREATE KEY button to generate our authentication key file. This key in JSON format will be used by Terraform to authenticate to GCP. Download the JSON file and store it in a secure folder or vault.

Creating a Service account using gcloud in terminal

For this step, you will need gcloud command installed and then log in to gcloud using these commands:

1
2
3
gcloud auth login

gcloud auth application-default login

To create the service account in terminal, run the gcloud iam service-accounts create command:

1
2
3
gcloud iam --project PROJECT_ID service-accounts create SERVICE_ACCOUNT_ID \
    --description="DESCRIPTION" \
    --display-name="DISPLAY_NAME"

Replace the following values:

  • PROJECT_ID: the project ID
  • SERVICE_ACCOUNT_ID: the ID for the service account
  • DESCRIPTION: an optional description of the service account
  • DISPLAY_NAME: a service account name to display in the Google Cloud console

This is the output on my system

1
2
3
4
5
$ gcloud iam --project citizix-one service-accounts create terraform-user0 \
    --description="Citizix Terraform User" \
    --display-name="Terraform User0"

Created service account [terraform-user0].

To grant your service account an IAM role on your project, run the gcloud projects add-iam-policy-binding command:

1
2
3
gcloud projects add-iam-policy-binding PROJECT_ID \
    --member="serviceAccount:SERVICE_ACCOUNT_ID@PROJECT_ID.iam.gserviceaccount.com" \
    --role="ROLE_NAME"

Replace the following values:

  • PROJECT_ID: the project ID
  • SERVICE_ACCOUNT_ID: the service account ID
  • ROLE_NAME: a role name, such as roles/compute.osLogin

This is the output on my machine

1
2
3
4
5
6
7
8
$ gcloud projects add-iam-policy-binding citizix-one \
    --member="serviceAccount:[email protected]" \
    --role="roles/compute.admin" \
    --role="roles/compute.storageAdmin" \
    --role="roles/container.admin"

Updated IAM policy for project [citizix-one].
...

To allow users to impersonate the service account, run the gcloud iam service-accounts add-iam-policy-binding command to grant a user the Service Account User role (roles/iam.serviceAccountUser) on the service account:

1
2
3
4
gcloud iam service-accounts add-iam-policy-binding \
  SERVICE_ACCOUNT_ID@PROJECT_ID.iam.gserviceaccount.com \
  --member="user:USER_EMAIL" \
  --role="roles/iam.serviceAccountUser"
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# Generate a key for your new service account
gcloud iam service-accounts keys create iam-key.json \
  --iam-account="[email protected]"

# Secure the key file before using it
chmod 600 iam-key.json

# Do not commit iam-key.json to git.
# Optionally export it via GOOGLE_APPLICATION_CREDENTIALS.
# export GOOGLE_APPLICATION_CREDENTIALS="$PWD/iam-key.json"

You can list your service accounts to help you audit service accounts and keys, or as part of a custom tool for managing service accounts.

1
gcloud iam service-accounts list

To update a service account:

1
2
3
4
gcloud iam service-accounts update \
    SA_NAME@PROJECT_ID.iam.gserviceaccount.com \
    --description="UPDATED_SA_DESCRIPTION" \
    --display-name="UPDATED_DISPLAY_NAME"

To disable and enable a service account:

1
2
3
4
5
# Disable
gcloud iam service-accounts disable SA_NAME@PROJECT_ID.iam.gserviceaccount.com

# Enable
gcloud iam service-accounts enable SA_NAME@PROJECT_ID.iam.gserviceaccount.com

To Delete a service account:

1
2
gcloud iam service-accounts delete \
    SA_NAME@PROJECT_ID.iam.gserviceaccount.com

Configuring the Terraform Provider

Terraform providers create, manage, and update infrastructure resources, through API calls. They need to authenticate with the api thus we need to configure with the credentials we just created.

In your Terraform directory, in providers.tf, add the following content:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
locals {
  gcp_project      = "citizix"
  credentials_path = "../terraform-user-xxxx.json"
  region           = "europe-west1"
}

terraform {
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "~> 4.20"
    }
    google-beta = {
      source  = "hashicorp/google-beta"
      version = "~> 4.20"
    }
  }
}

provider "google" {
  credentials = file(local.credentials_path)

  project = local.gcp_project
  region  = local.region
}

provider "google-beta" {
  credentials = file(local.credentials_path)

  project = local.gcp_project
  region  = local.region
}

In the above code, we are defining locals for the provider variables and then passing them when creating the providers.

Security notes for JSON keys

If you generate a JSON key for Terraform, treat it like a secret:

  • Do not commit iam-key.json to git.
  • Store it outside your repo (or in an encrypted secret store).
  • Restrict permissions (for example chmod 600 iam-key.json).
  • Rotate/delete the key if you suspect it was exposed.

Long-lived keys are convenient but increase blast radius. Prefer short-lived credentials by using one of these approaches:

  • Workload Identity Federation (OIDC) for CI/CD workloads
  • Service account impersonation so Terraform receives tokens at runtime

In both cases, your Terraform runs still authenticate, but you do not need to store a long-lived JSON key file.

Verification checklist

After configuring the Terraform provider, verify access with:

1
2
terraform init
terraform plan

If you still get authorization errors, confirm:

  • The service account email matches what you created/granted IAM roles to
  • The target resources (projects/buckets/etc.) grant the right IAM roles
  • The required APIs are enabled in the GCP project

Troubleshooting common errors

403 / permissionDenied

You are missing one or more IAM roles on the target resources. Review:

  • gcloud projects get-iam-policy PROJECT_ID
  • IAM bindings on the specific resources Terraform manages

service account not found

Double-check SERVICE_ACCOUNT_ID and PROJECT_ID and confirm the service account exists:

1
gcloud iam service-accounts list

Terraform still uses the wrong credentials

Check whether Terraform is picking up a different auth method from your environment (for example ADC):

  • If using GOOGLE_APPLICATION_CREDENTIALS, ensure it points to the correct JSON key file.
  • If using credentials = file(local.credentials_path), ensure the path is correct and readable.

Summary

You now have everything needed for Terraform on GCP: a service account, the IAM roles it needs, optional impersonation for short-lived access, a safe JSON key option for local runs, and working provider "google" / provider "google-beta" configuration.

comments powered by Disqus
Citizix Ltd
Built with Hugo
Theme Stack designed by Jimmy