Workspaces
Workspace = แยก state file หลายชุดในโฟลเดอร์เดียว — เหมาะกับ multi-environment (dev/staging/prod)
Workspace คืออะไร?
ปกติ Terraform มี state file ตัวเดียว:
my-project/
└── terraform.tfstate
ใช้ workspace → มีหลาย state file:
my-project/
└── terraform.tfstate.d/
├── dev/ terraform.tfstate
├── staging/ terraform.tfstate
└── prod/ terraform.tfstate
ใช้ code เดียวกัน แต่ state ต่างกัน ตาม workspace
Default Workspace
ทุก project มี workspace default อัตโนมัติ:
$ terraform workspace show
default
Workspace Commands
# สร้าง workspace
terraform workspace new dev
terraform workspace new staging
terraform workspace new prod
# List
terraform workspace list
# Switch
terraform workspace select prod
# Show current
terraform workspace show
# Delete
terraform workspace delete dev
ใช้ใน Config
locals {
environment = terraform.workspace # ← ค่า workspace ปัจจุบัน
}
resource "aws_instance" "web" {
ami = "ami-12345"
instance_type = local.environment == "prod" ? "t3.large" : "t3.micro"
tags = {
Name = "web-${terraform.workspace}"
Environment = terraform.workspace
}
}
ตัวอย่าง: Multi-Environment Workflow
# 1. Setup
terraform init
terraform workspace new dev
terraform workspace new staging
terraform workspace new prod
# 2. Deploy dev
terraform workspace select dev
terraform apply -var-file=dev.tfvars
# 3. Deploy staging
terraform workspace select staging
terraform apply -var-file=staging.tfvars
# 4. Deploy prod
terraform workspace select prod
terraform apply -var-file=prod.tfvars
ตัวอย่าง: Conditional by Workspace
locals {
is_prod = terraform.workspace == "prod"
}
resource "aws_db_instance" "main" {
identifier = "db-${terraform.workspace}"
engine = "postgres"
instance_class = local.is_prod ? "db.t3.large" : "db.t3.micro"
allocated_storage = local.is_prod ? 100 : 20
multi_az = local.is_prod
backup_retention_period = local.is_prod ? 30 : 7
deletion_protection = local.is_prod
tags = {
Environment = terraform.workspace
}
}
ตัวอย่าง: Per-Workspace Tfvars
# main.tf
locals {
config = {
dev = {
instance_type = "t3.micro"
replicas = 1
}
staging = {
instance_type = "t3.small"
replicas = 2
}
prod = {
instance_type = "t3.large"
replicas = 5
}
}
current = local.config[terraform.workspace]
}
resource "aws_instance" "web" {
count = local.current.replicas
ami = "ami-12345"
instance_type = local.current.instance_type
}
State Path
Local Backend
.
├── main.tf
└── terraform.tfstate.d/
├── dev/terraform.tfstate
└── prod/terraform.tfstate
Remote Backend (S3)
terraform {
backend "s3" {
bucket = "my-tfstate"
key = "infra/terraform.tfstate" # → env:/{workspace}/infra/terraform.tfstate
region = "ap-southeast-1"
}
}
ใน S3:
my-tfstate/
├── env:/dev/infra/terraform.tfstate
├── env:/staging/infra/terraform.tfstate
└── env:/prod/infra/terraform.tfstate
Workspace vs Multiple Directories
| Feature | Workspace | Multiple Dirs |
|---|---|---|
| Code | Shared | Separate |
| State | Separate | Separate |
| Config | Same .tf | Different .tf |
| Different resources per env | ⚠️ Hard | ✅ Easy |
| Easy to switch | ✅ workspace select | ❌ cd |
When to Use Workspace
- ✅ Same architecture, different size (dev/staging/prod)
- ✅ Quick experiments / feature branches
- ✅ Tightly coupled environments
When to Use Multiple Dirs
- ✅ Environments diverge significantly
- ✅ Different teams own different envs
- ✅ Different security boundaries
ตัวอย่าง: Feature Branch Pattern
# Developer creates ephemeral env per PR
git checkout -b feature/new-api
terraform workspace new pr-123
terraform apply -var-file=feature.tfvars
# After PR merged
terraform workspace select default
terraform workspace delete pr-123
→ Each PR has own infrastructure, easy cleanup
CI/CD Integration
on:
push:
branches: [main, develop]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3
- name: Init
run: terraform init
- name: Select Workspace
run: |
if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then
terraform workspace select prod
else
terraform workspace select dev
fi
- name: Apply
run: terraform apply -auto-approve
env:
TF_VAR_environment: ${{ github.ref == 'refs/heads/main' && 'prod' || 'dev' }}
TF_WORKSPACE Environment Variable
export TF_WORKSPACE=prod
terraform apply
# ← เท่ากับ workspace select prod + apply
ใช้ใน CI:
- name: Apply
env:
TF_WORKSPACE: ${{ github.ref_name }}
run: terraform apply -auto-approve
Limitations & Pitfalls
1. Same Backend
ทุก workspace ใช้ backend เดียวกัน — ถ้า bucket เสีย = ทุก env เสีย
→ Production ที่สำคัญ: ใช้ separate state files / accounts
2. Workspace ไม่ใช่ Security Boundary
ทุก workspace = same provider config → ถ้า dev ใช้ prod credentials → ทำลาย prod ได้
→ ใช้ separate AWS accounts สำหรับ env ต่างๆ
3. Missing Workspace = Default
ถ้าลืม workspace select → apply ที่ default — เผลอ deploy ผิด env
→ ตั้ง TF_WORKSPACE ใน CI หรือ check ใน script:
if [[ "$(terraform workspace show)" != "prod" ]]; then
echo "Error: must be in prod workspace"
exit 1
fi
4. ไม่เหมาะกับ Major Differences
ถ้า dev มี 3 instances, prod มี 30 + ELB + RDS Multi-AZ + ... → conditional logic ใน config ใหญ่
→ ใช้ separate dirs + module ดีกว่า
Best Practices
✅ DO:
- ใช้ workspace สำหรับ env ที่คล้ายกัน (size ต่าง)
- Document workspace usage ใน README
- Use TF_WORKSPACE ใน CI ป้องกันลืม
- ใช้ different AWS accounts ต่อ env (security)
❌ DON'T:
- ห้ามใช้ workspace เป็น security boundary
- ห้ามใช้ workspace ถ้า env ต่างกันมาก
- ห้าม share workspace ใน prod กับ dev
- ห้าม hard-code workspace name ใน config
Terraform Cloud Workspaces (ต่างกัน!)
⚠️ "Workspace" ใน Terraform Cloud / Enterprise = ความหมายต่างจาก CLI workspace
- CLI Workspace = state file ใน 1 directory
- TFC Workspace = ทั้ง project (state + variables + runs + history)
ใน TFC ไม่ใช้ CLI workspace — แต่ละ TFC workspace = 1 directory
ดูเพิ่มใน Section 19: HCP
สรุป
- Workspace = หลาย state file ใน 1 directory
- Commands:
workspace new/select/list/delete - Reference:
terraform.workspace - เหมาะกับ env ที่คล้ายกัน (dev/staging/prod with same arch)
- ไม่เหมาะกับ major architecture differences → ใช้ separate dirs + modules
- Terraform Cloud Workspace ≠ CLI Workspace
ต่อไป → GitHub Actions