TerraformPilot

Terraform

Terraform GCS Backend - Store State in Google Cloud Storage

Configure Terraform GCS backend to store state in Google Cloud Storage. Bucket setup, encryption, state locking, workspaces, and migration from local state.

LLuca Berton1 min read

Quick Answer

#
terraform {
  backend "gcs" {
    bucket = "my-terraform-state"
    prefix = "terraform/state"
  }
}

Create the State Bucket

#
# bootstrap/main.tf — Run this first with local state
resource "google_storage_bucket" "terraform_state" {
  name     = "${var.project}-terraform-state"
  location = var.region
  project  = var.gcp_project
 
  uniform_bucket_level_access = true
 
  versioning {
    enabled = true  # State history
  }
 
  lifecycle_rule {
    condition {
      num_newer_versions = 10  # Keep last 10 versions
    }
    action {
      type = "Delete"
    }
  }
 
  labels = {
    purpose     = "terraform-state"
    environment = "shared"
  }
}
 
# Prevent accidental deletion
resource "google_storage_bucket_iam_member" "state_admin" {
  bucket = google_storage_bucket.terraform_state.name
  role   = "roles/storage.objectAdmin"
  member = "serviceAccount:${var.terraform_sa_email}"
}

Backend Configuration

#
terraform {
  backend "gcs" {
    bucket      = "myproject-terraform-state"
    prefix      = "environments/production"
    credentials = "~/.config/gcloud/terraform-sa-key.json"  # Optional
  }
}

Partial Configuration (CI/CD)

#
# main.tf
terraform {
  backend "gcs" {}
}
# Pass config at init time
terraform init \
  -backend-config="bucket=myproject-terraform-state" \
  -backend-config="prefix=environments/production"

Customer-Managed Encryption Keys

#
resource "google_kms_key_ring" "terraform" {
  name     = "terraform-state"
  location = var.region
}
 
resource "google_kms_crypto_key" "terraform" {
  name            = "terraform-state-key"
  key_ring        = google_kms_key_ring.terraform.id
  rotation_period = "7776000s"  # 90 days
}
 
resource "google_storage_bucket" "terraform_state" {
  name     = "${var.project}-terraform-state"
  location = var.region
 
  uniform_bucket_level_access = true
  versioning { enabled = true }
 
  encryption {
    default_kms_key_name = google_kms_crypto_key.terraform.id
  }
}
terraform {
  backend "gcs" {
    bucket               = "myproject-terraform-state"
    prefix               = "production"
    kms_encryption_key   = "projects/myproject/locations/us-central1/keyRings/terraform-state/cryptoKeys/terraform-state-key"
  }
}

Migrate from Local to GCS

#
# 1. Add backend block to your config
# 2. Run init with migration
terraform init -migrate-state
 
# Terraform will ask:
# "Do you want to copy existing state to the new backend?"
# Answer: yes

Workspaces

#
terraform workspace new staging
terraform workspace new production
terraform workspace select production
 
# State stored at: gs://bucket/prefix/production.tfstate

State Locking

#

GCS backend uses Google Cloud Storage object locking automatically — no extra configuration needed. If a lock gets stuck:

terraform force-unlock LOCK_ID

Authentication

#
# Option 1: gcloud (local dev)
gcloud auth application-default login
 
# Option 2: Service account key
export GOOGLE_CREDENTIALS="/path/to/sa-key.json"
 
# Option 3: Workload Identity (CI/CD)
# Automatic in GKE, Cloud Build, etc.
#

Conclusion

#

Use GCS backend with versioning enabled for state history, uniform bucket-level access for simplified IAM, and CMEK for encryption compliance. State locking is automatic. Use prefix to organize multiple projects or environments in a single bucket.

#Terraform#GCP#State Management#Infrastructure as Code

Share this article