Using the GCS Backend Block in Terraform

Learn how to use the GCS backend for your Terraform state

If you're using Terraform to manage your infrastructure on Google Cloud, you'll inevitably need to configure a remote backend. The GCS backend block is the standard solution for storing your Terraform state files in a Google Cloud Storage bucket. This is an essential practice for team collaboration, state locking, and maintaining the integrity of your infrastructure as code.

The Terraform state file is a JSON file that acts as a record of your deployed resources. It maps your Terraform configuration to the actual resources in your Google Cloud project. Storing this file remotely with the GCS backend provides several key benefits:

  • Collaboration: It allows multiple team members to work on the same infrastructure without overwriting each other's changes.
  • State Locking: It prevents concurrent terraform apply operations, which can lead to state file corruption and resource conflicts. Google Cloud Storage handles this locking automatically.
  • Security & Durability: State files can contain sensitive data. Storing them in a secure, encrypted, and highly available GCS bucket is a core best practice.

Basic Usage and Configuration

To use the GCS backend, you need a pre-existing Google Cloud Storage bucket. It is a best practice to create a dedicated bucket for your Terraform state files.

Here's a basic GCS backend block configuration:

terraform {
  backend "gcs" {
    bucket = "my-terraform-state-bucket"
    prefix = "my-app"
  }
}
  • bucket: The globally unique name of your GCS bucket.
  • prefix: The path within the bucket where the state file will be stored. The state file will be named <prefix>/terraform.tfstate.

After adding this block to your main Terraform configuration file, you must run terraform init. This command initializes the backend and prompts you to migrate any existing local state to the remote GCS bucket.


Use Cases and Best Practices

The GCS backend is essential for any serious Terraform project.

  • Team Projects: When multiple developers are working on a single infrastructure, the GCS backend ensures everyone is using the same, up-to-date state file.
  • CI/CD Pipelines: In a continuous integration and continuous deployment (CI/CD) workflow, the pipeline needs a consistent place to read and write the state file. The GCS backend provides a reliable and secure endpoint for tools like Cloud Build or GitHub Actions to execute Terraform.
  • Production Environments: For production infrastructure, the GCS backend is non-negotiable. Its built-in state locking and data durability features are critical for preventing downtime and ensuring the integrity of your production environment.

Best Practices:

  • Separate State: Use a separate GCS bucket or a unique prefix for each environment. This isolates state files and prevents accidental cross-environment changes.
  • Permission Scoping: Use IAM policies to grant the minimum required permissions to your users or service accounts. They should only have permissions to read from and write to their specific state bucket.
  • Enable versioning on your GCS bucket: This gives you a history of your state files and allows you to revert to a previous version if something goes wrong.

Authentication and Examples

You should never hardcode credentials like service account keys directly in your configuration. Instead, use one of the supported authentication methods:

  • gcloud CLI: The most common approach for local development. When you log in with the gcloud CLI, Terraform can use those credentials automatically.
  • Environment Variables: Terraform can use credentials set in environment variables like GOOGLE_APPLICATION_CREDENTIALS which points to a service account key file.
  • Service Accounts: The most secure method for resources running in GCP. A Compute Engine instance or Cloud Build job can use a service account with the necessary permissions to access the GCS bucket.
  • Workload Identity: The recommended way to authenticate for applications running on Google Cloud, especially with GKE, as it links a Kubernetes service account to a Google Cloud service account.

Examples

gcloud CLI

This is the easiest way to get started with local development. When you authenticate with the gcloud CLI, it sets up Application Default Credentials (ADC) which Terraform uses automatically.

Run Terraform commands without any extra authentication configuration:

terraform init

Set your project (if you have multiple):

gcloud config set project <your-project-id>

Log in to GCP:

gcloud auth application-default login

Environment Variables

This approach keeps sensitive information out of your code. You can download a service account key file and point to it using an environment variable.

export GOOGLE_APPLICATION_CREDENTIALS="/path/to/your/keyfile.json"

terraform init

Service Accounts

This is the most secure method for authenticating resources running inside GCP, such as a Compute Engine instance or Cloud Build job. You attach a service account to the resource, and Terraform assumes that service account's permissions automatically.

  1. Create a Service Account with a role that grants permissions to access the GCS bucket. The Storage Object Admin role is a common choice.
  2. Attach the Service Account to your Compute Engine instance or Cloud Build project. Terraform, when run on that resource, will automatically authenticate using the credentials provided by the service account. No secrets are stored on the resource itself.

Advanced Concepts

Partial Backend Configuration

Terraform's design prevents you from using variables directly inside the backend block. However, you can leave out sensitive or environment-specific information and supply it at runtime using a backend configuration file or command-line flags with terraform init.

Example using a file:

Run terraform init:

terraform init -backend-config="backend.conf"

backend.conf (sensitive info):

bucket = "my-terraform-state-bucket"

main.tf (partial config):

terraform {
  backend "gcs" {
    prefix = "my-app"
  }
}

This method prevents sensitive information from being committed to source control.

Managing Multiple Environments with Workspaces

For a single configuration that deploys to multiple environments, Terraform workspaces can be used to manage different state files within the same bucket.

Example:

# Create and switch to a development workspace
terraform workspace new dev

# Create and switch to a production workspace
terraform workspace new prod

When you switch between workspaces, Terraform automatically changes the prefix to include the workspace name (e.g., my-app/env:/dev/terraform.tfstate), ensuring each environment has its own isolated state file.


A Note on OpenTofu and Dynamic Backends

While the classic Terraform CLI maintains its strict rule against dynamic backend blocks, the OpenTofu project, a fork of Terraform, has broken with this convention.

OpenTofu, starting with version 1.8, introduced the ability to use variables and local values within the backend block. This is a significant feature that addresses one of the most long-standing user requests in the Terraform community.

With OpenTofu, you can now write a more flexible and DRY (Don't Repeat Yourself) backend configuration, particularly useful for multi-environment setups.

Here's how a dynamic gcs backend block could look in OpenTofu:

variable "env" {
  type    = string
  default = "dev"
}

terraform {
  backend "gcs" {
    bucket = "my-terraform-state-${var.env}-bucket"
    prefix = "my-app-${var.env}"
  }
}

This simple configuration allows you to switch between environments by simply changing the env variable. You can pass the variable at the command line:

tofu init -backend-config="env=prod"

This removes the need for separate backend configuration files or complex scripts to handle different environments, making your codebase cleaner and less prone to manual errors. For teams managing many similar environments, or for those who simply prefer a more concise and variable-driven configuration, OpenTofu's support for dynamic backend blocks is a game-changer.