Terraform Workspaces vs Directory Structure Prompt
Choose between Terraform workspaces and directory-per-env — when each is appropriate, migration patterns.
- Target user
- Terraform engineers structuring multi-env deployments
- Difficulty
- Intermediate
- Tools
- Claude, ChatGPT
The prompt
You are a senior Terraform engineer who has built multi-environment setups using both workspaces and directory-per-env, with strong opinions on which fits which case. I will provide: - The environment count + characteristics - Current structure - Goal Your job: 1. **Workspaces**: - Multiple state files via `terraform workspace` - Same backend, different keys - Same code, different state - `terraform.workspace` variable accessible 2. **Directory per env**: - Separate dirs: `envs/dev`, `envs/staging`, `envs/prod` - Each has own backend config - Code reuse via modules - Truly separate states 3. **When workspaces work**: - Many similar transient envs (review apps, branches) - Lightweight envs that share configuration - Single team managing all - Identical structure across envs 4. **When directories** (recommended for prod-like): - Real env separation - Different team ownership per env - Different cloud accounts - Different variable schemas - Promotion workflow (dev → staging → prod) 5. **For mixed approaches**: - Directories for prod tier separation - Workspaces within for variants 6. **For terraform.workspace pitfalls**: - Coding `count = terraform.workspace == "prod" ? 3 : 1` couples code to envs - Use variables instead 7. **For migration from workspaces → dirs**: - Pull state per workspace - Push to new backend per env - Update CI/CD 8. **For Terraform Cloud workspaces**: - Different concept; rich features (variables, runs, RBAC) - More like directory pattern but managed Mark DESTRUCTIVE: confusing OSS workspaces with TFC workspaces, sharing prod and dev state in one backend, using workspaces for cross-account access. --- Env count + characteristics: [DESCRIBE] Current structure: [DESCRIBE] Goal: [DESCRIBE]
Why this prompt works
This is a recurring architectural decision. This prompt walks tradeoffs.
How to use it
- For prod-like envs: directories.
- For ephemeral envs: workspaces.
- Don’t mix patterns without clear boundary.
- Migration: backup, transfer, verify.
Patterns
Directory per env (recommended for real envs)
infrastructure/
├── modules/
│ ├── network/
│ ├── compute/
│ └── data/
├── envs/
│ ├── dev/
│ │ ├── main.tf # module instantiation
│ │ ├── variables.tf
│ │ ├── terraform.tfvars # dev values
│ │ └── backend.tf # dev backend
│ ├── staging/
│ │ ├── main.tf
│ │ ├── terraform.tfvars
│ │ └── backend.tf
│ └── prod/
│ ├── main.tf
│ ├── terraform.tfvars
│ └── backend.tf
# envs/prod/main.tf
module "network" {
source = "../../modules/network"
cidr_block = var.cidr_block
az_count = var.az_count
}
# envs/prod/backend.tf
terraform {
backend "s3" {
bucket = "myorg-tfstate-prod"
key = "network/terraform.tfstate"
region = "us-east-1"
}
}
cd envs/prod
terraform init
terraform plan
terraform apply
Workspaces (for ephemeral / many similar)
# All workspaces use same backend
terraform workspace new pr-123
terraform workspace new pr-456
terraform workspace select pr-123
terraform plan -var="branch=pr-123"
terraform apply
# main.tf uses terraform.workspace
resource "aws_s3_bucket" "review" {
bucket = "review-${terraform.workspace}"
}
Bad pattern (avoid)
# DON'T: coupling code to env names
resource "aws_instance" "web" {
count = terraform.workspace == "prod" ? 3 : 1
instance_type = terraform.workspace == "prod" ? "m5.large" : "t3.small"
}
# DO: parametrize
variable "instance_count" {}
variable "instance_type" {}
resource "aws_instance" "web" {
count = var.instance_count
instance_type = var.instance_type
}
Comparison
| Aspect | Workspaces | Directory per env |
|---|---|---|
| State files | Multiple via same backend | One per dir |
| Code | Single | Single (via modules) |
| Variables | Same set | Different schemas allowed |
| Backend config | One | Per env |
| Cross-account | No | Yes (via provider) |
| Promotion | Same code; switch workspace | Same module, different config |
| Team isolation | Hard | Easy (dir permissions) |
| CI/CD | Switch workspace | cd into dir |
Migration from workspaces → dirs
# 1. Pull each workspace's state
for WS in dev staging prod; do
terraform workspace select $WS
terraform state pull > state-$WS.tfstate
done
# 2. Create directory structure
mkdir -p envs/{dev,staging,prod}
# Copy module references into each
# 3. Configure backend per dir
# envs/prod/backend.tf → s3 with key=prod/...
# 4. Push state per dir
cd envs/prod
terraform init # configures backend
terraform state push ../../state-prod.tfstate
# 5. Verify
terraform plan # should be no changes
Common findings this catches
- Single workspace state for dev + prod → split into directories.
terraform.workspaceeverywhere in code → parametrize.- Wrong workspace used in CI → directory pattern safer.
- Cross-account via workspace → use provider per env directory.
- Module changes affect prod and dev simultaneously → expected with workspaces; with dirs, separate promotion.
- Workspace deletion → state file orphans (not destroyed).
- TFC workspace ≠ OSS workspace → confusing.
When to escalate
- Migration across many state files — staged.
- Multi-team boundary changes — coordination.
- Promotion workflow design — strategic.
Related prompts
-
Terraform CI/CD: Atlantis, Cloud, GitOps Prompt
Choose and configure Terraform CI/CD — Atlantis, Terraform Cloud, Spacelift, custom CI; plan/apply workflows, approvals.
-
Terraform Multi-Environment Design Prompt
Design Terraform for multiple environments — dev/staging/prod separation, tfvars patterns, account boundaries, promotion workflow.
-
Terraform State Backend Design Prompt
Design Terraform state backend — S3+DynamoDB, GCS, Azure Blob, encryption, locking, versioning, cross-account access.