Managing Google Cloud infrastructure with Terraform requires a reliable remote backend. The GCS backend block is the usual way to do it: it stores Terraform state files in a Google Cloud Storage bucket so a team can share them safely. The Terraform state file is a JSON record of the resources you've deployed. It maps your configuration to the actual resources in your Google Cloud project. Keeping that file in a GCS bucket instead of on a laptop gets you a few things: shared access across team members, durability, and protection against concurrent operations, which can lead to state file corruption and resource conflicts. Google Cloud Storage handles this locking automatically.
Why remote state matters with GCS
Local state files work for single-person experiments. Once you're past a single-person experiment, the GCS backend earns its keep in a few common situations: team collaboration where multiple engineers run terraform apply against the same configuration, CI/CD pipelines that need a persistent state location, and disaster recovery where state is stored in a managed object store.
The state file is named within the bucket using the prefix you define. The reference material describes the state file as
Basic GCS backend configuration
To use the GCS backend, you need a Google Cloud Storage bucket that already exists. It's a good idea to create a dedicated bucket for your Terraform state files.
A basic GCS backend block configuration looks like this:
hcl
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.
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. If migrating from local state, Terraform will ask if you want to copy existing state to the new backend.
Parameter reference:
| Parameter | Description | Required |
|---|---|---|
| bucket | Globally unique GCS bucket name for state | Yes |
| prefix | Path within bucket for state file | Yes |
| credentials | Optional service account key path | No |
Initialization and state migration
Initialize the GCS backend with:
bash
terraform init
If you migrate from local state, Terraform will ask if you want to copy existing state to the new backend. The init step also establishes locking configuration and creates the initial state object in the bucket.
When you switch between workspaces, Terraform automatically changes the prefix to include the workspace name, ensuring each environment has its own isolated state file.
Example workspace usage:
bash
terraform workspace new dev
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.
Authentication for GCS backend access
The GCS backend needs credentials to access your bucket. There are several ways to provide them.
Application Default Credentials is the simplest approach. When you authenticate with the gcloud CLI, it sets up Application Default Credentials which Terraform uses automatically.
Run Terraform commands without any extra authentication configuration:
bash
terraform init
Set your project if you have multiple:
bash
gcloud config set project <your-project-id>
Log in to GCP:
bash
gcloud auth application-default login
This approach keeps sensitive information out of your code.
Service account key file method:
bash
export GOOGLE_APPLICATION_CREDENTIALS="/path/to/your/keyfile.json"
terraform init
You should never hardcode credentials like service account keys directly in your configuration. Instead, use one of the supported authentication methods: gcloud CLI, or GOOGLEAPPLICATIONCREDENTIALS which points to a service account key file.
For 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. Storage Object Admin role is a common choice.
Authentication options:
| Method | Use case | Notes |
|---|---|---|
| Application Default Credentials | Local development with gcloud | Simplest approach |
| GOOGLEAPPLICATIONCREDENTIALS | Local development with key file | Avoid committing key |
| Attached service account | Compute Engine, Cloud Build | Most secure for GCP resources |
State locking and concurrency safety
GCS backend uses Google Cloud Storage object locking automatically — no extra configuration needed. If a lock gets stuck, you can remove the lock file manually from the bucket.
State locking is automatic. No extra configuration is needed for basic locking. Best practice configurations add versioning enabled for state history, uniform bucket-level access for simplified IAM, and CMEK for encryption compliance.
Use prefix to organize multiple projects or environments in a single bucket. Prefix organization allows one bucket to hold states for dev, staging, and prod with clear separation.
Organizing multiple environments
Prefix organization is central to GCS backend usage. A single bucket can hold many projects or environments.
Example with environment variable in OpenTofu:
```hcl
variable "env" {
type = string
default = "dev"
}
terraform {
backend "gcs" {
bucket = "my-terraform-state-${var.env}-bucket"
prefix = "my-app-${var.env}"
}
}
```
Now you switch environments by changing the env variable. You can pass it at the command line:
bash
tofu init -var="env=prod"
That cuts out the separate backend config files or wrapper scripts you'd otherwise write to juggle environments, so there's less to keep in sync by hand.
If one configuration deploys to several environments, you can use Terraform workspaces to keep a separate state file for each one inside the same bucket.
OpenTofu dynamic backend blocks vs Terraform CLI
Terraform won't let you use variables directly inside the backend block. But you can leave out sensitive or environment-specific values and supply them at runtime with a backend configuration file or command-line flags on terraform init.
Example using a file:
Run terraform init:
bash
terraform init -backend-config="backend.conf"
backend.conf:
hcl
bucket = "my-terraform-state-bucket"
main.tf partial config:
hcl
terraform {
backend "gcs" {
prefix = "my-app"
}
}
This method prevents sensitive information from being committed to source control.
The classic Terraform CLI still refuses to let you put variables in a backend block. OpenTofu, the fork of Terraform, took a different path here.
Starting with version 1.8, OpenTofu lets you use variables and local values inside the backend block. That answers one of the oldest feature requests in the Terraform community.
In practice that means you can write a more flexible, DRY backend configuration, which helps most when you run the same setup across several environments. If you manage a lot of near-identical environments, OpenTofu's dynamic backend blocks are worth a look.
Backend configuration patterns
Inline configuration is simplest for single environment:
hcl
terraform {
backend "gcs" {
bucket = "my-terraform-state-bucket"
prefix = "my-app"
}
}
Backend config file pattern separates sensitive values:
hcl
terraform {
backend "gcs" {
prefix = "my-app"
}
}
Supplied at init time with backend.conf containing bucket and other sensitive parameters.
Example repository layout
An example sets up a GCS backend with a minimal example of a state stored in it. It:
- Creates an GCS bucket with a random name (‘changeme-xxxxxxxxxxxxx’)
- Sets up an GCP VPC, storing state in that backend
These are the files used:
- destroy.sh
- Shell script to clean up any previous run of run.sh
- run.sh
- Run this whole example up, creating the bucket, backend, and GCP VPC
- googlestoragebucket/main.tf
- Terraform code to set up a bucket
- googlestoragebucket/run.sh
- Script to create just the bucket
- googlestoragebucket/destroy.sh
- Script to destroy just the bucket
- googlecomputenetwork/main_template
- Template file for Terraform code for GCP VPC
The remote example sets up a remote backend with a minimal example of a state stored in it. It connects to Terraform Cloud organization and creates/updates workspace, sets up an AWS VPC, storing state in that backend.
File inventory for the GCS example:
| File | Purpose |
|---|---|
| destroy.sh | Clean up previous run |
| run.sh | Create bucket, backend, GCP VPC |
| googlestoragebucket/main.tf | Bucket Terraform code |
| googlecomputenetwork/main_template | VPC template |
Operational best practices
Use a dedicated bucket for Terraform state files. Never hardcode credentials in configuration. Use Application Default Credentials for local development or attached service accounts for CI.
Enable versioning on the GCS bucket for state history and point-in-time recovery. Uniform bucket-level access simplifies IAM management. CMEK provides encryption compliance for regulated workloads.
Keep prefixes consistent with project and environment naming. Use workspaces or variable-driven prefixes to avoid collisions.
State locking is automatic with GCS backend, but monitor lock files and know how to remove a stuck lock if needed.
Conclusion
The GCS backend provides durable, shared, and locked state storage for Terraform on Google Cloud. The backend block with bucket and prefix is the core of the configuration, initialized with terraform init and accessed via Application Default Credentials or service account keys. State locking and concurrency safety are handled by GCS object locking without extra configuration.
Organizing state through prefix and workspaces enables a single bucket to serve multiple environments safely. Authentication should never be hardcoded; gcloud ADC, environment variables, or attached service accounts keep secrets out of source control.
Terraform CLI requires backend configuration files or flags for environment-specific values, while OpenTofu 1.8+ allows variables and locals directly inside the backend block for DRY multi-environment setups. Adding versioning, uniform bucket-level access, and CMEK raises the operational maturity of state storage for compliance and recovery needs. The combination of automatic locking, prefix organization, and secure credential handling makes the GCS backend a robust foundation for team Terraform workflows on GCP.