TerraformPilot

Terraform

Terraform GCS Backend - Store State in Google Cloud Storage

Configure Terraform GCS backend to store state in Google Cloud Storage: bucket setup, default_kms_key_name encryption, state locking, and workspaces.

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 (default_kms_key_name on google_storage_bucket)

#

Set the default_kms_key_name attribute inside the encryption block of a google_storage_bucket resource to encrypt Terraform state with a customer-managed KMS key instead of Google's default encryption:

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