State Architecture: Multi-Environment Workspaces and GCS
Scenario
You are building infrastructure that supports "dev", "staging", and "prod" environments. Instead of using completely separate Git directories with duplicate code, you will use a single codebase leveraging Terraform Workspaces and a GCS Remote Backend. You will dynamically size a Compute Engine instance based directly on the name of the currently active workspace.
- In step 1, define the `terraform { backend "gcs" { ... } }` block.
- Deploy a google_compute_instance.
- Map its `machine_type` using a Terraform `map` local variable, looking up the value using terraform.workspace.
- Deploy the configuration twice via the CLI using `terraform workspace new dev` and `terraform workspace new prod`.
Additional Context
GCS Backend Structure: When using the GCS backend with workspaces, Terraform stores state files like this: `gs://[BUCKET_NAME]/[PREFIX]/default.tfstate`. If you create a workspace called "prod", it stores it at: `gs://[BUCKET_NAME]/[PREFIX]/env:/prod/default.tfstate`.
Dynamic Sizing: `terraform.workspace` is a natively available string inside HCL containing the current active workspace name (e.g. "default", "dev", "prod"). You can use `lookup(local.map, terraform.workspace, "fallback")` to configure different values without needing separate `.tfvars` files.
backend.tf
terraform {
backend "gcs" {
# You MUST create this bucket out-of-band BEFORE running terraform init!
bucket = "my-terraform-state-bucket-555"
prefix = "multi-env/state"
}
}variables.tf
variable "project_id" { type = string }main.tf
provider "google" {
project = var.project_id
region = "us-central1"
}
# Map environments to different compute sizes
locals {
machine_sizes = {
default = "f1-micro"
dev = "e2-micro"
stag = "e2-small"
prod = "e2-medium"
}
# Fetch the size based on workspace. If workspace is unnamed in map, fallback to default.
active_size = lookup(local.machine_sizes, terraform.workspace, local.machine_sizes["default"])
}
resource "google_compute_instance" "app_server" {
# Dynamically name the resource based on the environment!
name = "app-server-${terraform.workspace}"
machine_type = local.active_size
zone = "us-central1-a"
boot_disk {
initialize_params { image = "debian-cloud/debian-11" }
}
network_interface {
network = "default"
}
}outputs.tf
output "deployed_machine" {
value = "${google_compute_instance.app_server.name} running on ${google_compute_instance.app_server.machine_type}"
}Workflow Commands
# Initialize the Remote Backend connection
terraform init
# --- DEV DEPLOYMENT ---
terraform workspace new dev
terraform apply -var="project_id=my-gcp-project-123"
# Output shows: app-server-dev running on e2-micro
# --- PROD DEPLOYMENT ---
terraform workspace new prod
terraform apply -var="project_id=my-gcp-project-123"
# Output shows: app-server-prod running on e2-medium
# --- CLEANUP (Must destroy workspaces individually) ---
terraform workspace select dev
terraform destroy -auto-approve -var="project_id=my-gcp-project-123"
terraform workspace select prod
terraform destroy -auto-approve -var="project_id=my-gcp-project-123"
