Skip to main content

Splitting State Files

เมื่อ project ใหญ่ขึ้น → แยก state file ออกเป็นหลายส่วน — เพื่อ blast radius เล็ก, plan เร็ว, ทีมทำงานคู่ขนาน

ทำไมต้องแยก State?

❌ Single State Problems

  • Slow plan — refresh ทุก resource ทุกครั้ง (1000 resources = 5 นาที!)
  • Big blast radius — ผิดพลาด = พังหลายส่วน
  • Lock contention — ทีมหลายคน apply พร้อมกันไม่ได้
  • Hard to grasp — มองภาพรวม config ใหญ่มากยาก

✅ Multiple States Benefits

  • Plan/apply เร็ว — แค่ส่วนที่เปลี่ยน
  • 🛡️ Small blast radius — error อยู่ใน scope เดียว
  • 👥 Team parallelism — แต่ละทีม manage state ตัวเอง
  • 📚 Better organization — แต่ละ state มี purpose ชัด

Strategies การแยก State

Strategy 1: By Layer (Component)

project/
├── network/ # VPC, subnets, security groups
│ └── terraform.tfstate
├── database/ # RDS, ElastiCache
│ └── terraform.tfstate
├── compute/ # EC2, ASG, ALB
│ └── terraform.tfstate
└── application/ # K8s, Lambda, ECR
└── terraform.tfstate

Pros: ตามวงจร change frequency

  • Network เปลี่ยนน้อย
  • Application เปลี่ยนบ่อย

Cons: ต้อง manage cross-state references

Strategy 2: By Environment

project/
├── dev/
│ └── terraform.tfstate
├── staging/
│ └── terraform.tfstate
└── prod/
└── terraform.tfstate

Pros: Isolated environments Cons: Code duplication ระหว่าง envs (แก้ด้วย module)

project/
├── network/
│ ├── dev/ terraform.tfstate
│ ├── staging/ terraform.tfstate
│ └── prod/ terraform.tfstate
├── database/
│ ├── dev/ terraform.tfstate
│ ...

→ Full matrix: layer × environment

Strategy 4: By Service

my-org/
├── auth-service/
├── billing-service/
├── notification-service/

→ แต่ละ microservice มี state ตัวเอง

Cross-State Reference

ใช้ terraform_remote_state data source:

application/main.tf
data "terraform_remote_state" "network" {
backend = "s3"
config = {
bucket = "my-tfstate"
key = "prod/network/terraform.tfstate"
region = "ap-southeast-1"
}
}

resource "aws_instance" "app" {
subnet_id = data.terraform_remote_state.network.outputs.private_subnet_ids[0]
vpc_security_group_ids = [data.terraform_remote_state.network.outputs.app_sg_id]
}

ก่อนใช้ — network state ต้อง output ค่าออกมาก่อน:

network/outputs.tf
output "vpc_id" {
value = aws_vpc.main.id
}

output "private_subnet_ids" {
value = aws_subnet.private[*].id
}

output "app_sg_id" {
value = aws_security_group.app.id
}

Workflow: Apply ตามลำดับ

แต่ละ layer apply เสร็จ → output ใช้ใน layer ถัดไป

cd network && terraform apply
cd ../database && terraform apply
cd ../compute && terraform apply
cd ../application && terraform apply

Destroy: Reverse Order

cd application && terraform destroy
cd ../compute && terraform destroy
cd ../database && terraform destroy
cd ../network && terraform destroy

→ ลบ application ก่อน เพราะอ้างอิง compute, etc.

Migrate จาก Single → Multiple State

Step 1: Move resource จาก state เก่า → state ใหม่

# State A → State B
terraform state mv \
-state-out=../newproject/terraform.tfstate \
aws_instance.web \
aws_instance.web

Step 2: Verify

cd ../newproject
terraform plan
# Should show: No changes

Step 3: ลบ resource จาก config เดิม

# old/main.tf — ลบ block ของ aws_instance.web
cd old
terraform plan
# Should show: 0 to add, 0 to change, 0 to destroy
# (ไม่ destroy เพราะ state ลบไปแล้ว ตอน mv)

Terragrunt — Make Splitting Easier

Terragrunt = wrapper ของ Terraform ที่ช่วย:

  • DRY config across environments
  • Manage multiple state file
  • Apply ทุก state พร้อมกัน
prod/network/terragrunt.hcl
include "root" {
path = find_in_parent_folders()
}

terraform {
source = "../../../modules/network"
}

inputs = {
environment = "prod"
vpc_cidr = "10.0.0.0/16"
}
# Apply ทุก state ใน prod/
cd prod && terragrunt run-all apply

ดูเพิ่มใน Section 17: Terragrunt

Workspaces (อีกทางเลือก)

terraform workspace new dev
terraform workspace new prod

# State แยก ตาม workspace
terraform workspace select dev
terraform apply -var-file=dev.tfvars

State paths:

backend.tf:
key = "terraform.tfstate"

ที่จริงเก็บที่:
env:dev/terraform.tfstate
env:prod/terraform.tfstate

ดูเพิ่มใน Section 15: Workspaces

Workspace vs Multiple Directories
  • Workspace — same code, different state (dev/staging/prod)
  • Multiple Dirs — different code + different state (network vs database)

มักใช้ คู่กัน — multi-dir for layers + workspace for envs

Best Practices

✅ DO:
- แยก state ตาม blast radius (network ≠ application)
- ใช้ terraform_remote_state อ้าง state อื่น
- Apply ตาม dependency order
- Document layer dependencies ใน README

❌ DON'T:
- ห้ามแยก state เพราะ "ไฟล์ใหญ่" — แยกตาม responsibility
- ห้ามมี circular dependency ระหว่าง state
- ห้าม hard-code resource ID — ใช้ remote_state output

ตัวอย่าง Real-World Structure

my-org-infra/
├── _bootstrap/ # S3 + DynamoDB สำหรับ state
│ └── terraform.tfstate
├── _global/ # Resources global (Route53, CloudFront, IAM)
│ └── terraform.tfstate
├── prod/
│ ├── network/ # VPC, NAT, Subnets
│ ├── data/ # RDS, ElastiCache
│ ├── compute/ # EKS, ALB
│ └── apps/ # K8s deployments, Helm releases
├── staging/
│ ├── network/
│ ├── data/
│ └── compute/
└── dev/
├── network/
└── compute/
# Update prod network → แค่ตรงนั้น apply
cd prod/network
terraform apply

สรุป

  • Single state file ⛔ scale ไม่ได้
  • แยก state ตาม layer (network/database/compute) + environment
  • ใช้ terraform_remote_state อ้าง state อื่น
  • Apply ตามลำดับ dependency, destroy reverse
  • Terragrunt + Workspace ช่วย manage หลาย state

ต่อไป → Versioning