Remote State
เก็บ state ไว้ที่ remote backend (S3, GCS, Azure Blob, Terraform Cloud) แทน local file — เป็น มาตรฐานของทุก production
ทำไมต้องใช้ Remote State?
❌ Local State Problems
- ทีมหลายคน → conflict (ใครจะมี source of truth?)
- Local disk พัง = state หาย = infrastructure ไม่ recover ได้
- Push เข้า Git = secret leak
- ไม่มี locking → 2 คน apply พร้อมกัน → state corrupt
✅ Remote State Solutions
- Shared — ทีมเข้าถึงได้
- Durable — Cloud storage redundant
- Encrypted — at rest + in transit
- Locking — ป้องกัน concurrent apply
- Versioned — rollback ได้
Backend Types
| Backend | Description | Locking |
|---|---|---|
s3 | AWS S3 ⭐ (popular) | ✅ DynamoDB |
gcs | Google Cloud Storage | ✅ built-in |
azurerm | Azure Blob | ✅ built-in |
remote | Terraform Cloud / Enterprise | ✅ built-in |
consul | HashiCorp Consul | ✅ built-in |
local | Local file (default) | ❌ |
S3 Backend (AWS)
Setup ทีละขั้นตอน
1. สร้าง S3 Bucket (สำหรับเก็บ state)
bootstrap/main.tf
resource "aws_s3_bucket" "tfstate" {
bucket = "my-company-tfstate"
}
resource "aws_s3_bucket_versioning" "tfstate" {
bucket = aws_s3_bucket.tfstate.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_s3_bucket_server_side_encryption_configuration" "tfstate" {
bucket = aws_s3_bucket.tfstate.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}
resource "aws_s3_bucket_public_access_block" "tfstate" {
bucket = aws_s3_bucket.tfstate.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
2. สร้าง DynamoDB Table (สำหรับ locking)
resource "aws_dynamodb_table" "tfstate_lock" {
name = "terraform-locks"
billing_mode = "PAY_PER_REQUEST"
hash_key = "LockID"
attribute {
name = "LockID"
type = "S"
}
}
3. Configure Backend ใน Project
backend.tf
terraform {
backend "s3" {
bucket = "my-company-tfstate"
key = "prod/network/terraform.tfstate"
region = "ap-southeast-1"
encrypt = true
dynamodb_table = "terraform-locks"
}
}
4. Init
terraform init
→ Terraform จะถามว่าจะ migrate state จาก local ไป S3 ไหม → yes
State Key Convention
key ใน backend = path ใน S3 bucket
key = "prod/network/terraform.tfstate"
แนะนำ pattern:
<env>/<component>/terraform.tfstate
prod/network/terraform.tfstate
prod/database/terraform.tfstate
prod/application/terraform.tfstate
staging/network/terraform.tfstate
staging/database/terraform.tfstate
dev/...
→ เห็นโครงสร้าง infra จาก S3 ตรงๆ
Backend Config — Partial Configuration
ห้าม hard-code bucket name + region ใน backend block (จะ commit เข้า Git) → ใช้ partial config
backend.tf
terraform {
backend "s3" {
# ค่อยส่งตอน init
}
}
terraform init \
-backend-config="bucket=my-company-tfstate" \
-backend-config="key=prod/network/terraform.tfstate" \
-backend-config="region=ap-southeast-1" \
-backend-config="dynamodb_table=terraform-locks"
หรือใช้ file:
prod.backend.hcl
bucket = "my-company-tfstate"
key = "prod/network/terraform.tfstate"
region = "ap-southeast-1"
dynamodb_table = "terraform-locks"
terraform init -backend-config=prod.backend.hcl
Cross-State Reference (terraform_remote_state)
อ่าน output จาก state อื่น:
application/main.tf
data "terraform_remote_state" "network" {
backend = "s3"
config = {
bucket = "my-company-tfstate"
key = "prod/network/terraform.tfstate"
region = "ap-southeast-1"
}
}
resource "aws_instance" "app" {
subnet_id = data.terraform_remote_state.network.outputs.subnet_ids[0]
vpc_id = data.terraform_remote_state.network.outputs.vpc_id
}
→ Application state อ้างอิง network state ได้
Other Backends
GCS (Google Cloud Storage)
terraform {
backend "gcs" {
bucket = "my-tfstate"
prefix = "prod/network"
}
}
GCS มี locking + versioning built-in (ไม่ต้องตั้ง DynamoDB)
Azure Blob
terraform {
backend "azurerm" {
resource_group_name = "tfstate"
storage_account_name = "myorgtfstate"
container_name = "tfstate"
key = "prod.terraform.tfstate"
}
}
Terraform Cloud
terraform {
cloud {
organization = "my-org"
workspaces {
name = "prod-network"
}
}
}
Migrate State
Local → Remote
- เพิ่ม backend block
terraform init→ ตอบyes- ลบ local
.tfstateทิ้ง
Remote → Remote (e.g., S3 → GCS)
- เปลี่ยน backend block
terraform init -migrate-state
Best Practices
✅ DO:
- ใช้ remote backend ตั้งแต่ project แรก
- Enable versioning + encryption
- Setup locking (DynamoDB สำหรับ S3)
- IAM policy ที่จำกัด — เฉพาะคนที่ apply ได้
- Backup state file (S3 versioning ทำให้)
❌ DON'T:
- ห้าม commit state file
- ห้าม hard-code credentials ใน backend
- ห้าม disable encryption
- ห้าม share bucket กับ data ทั่วไป
ตัวอย่าง: Production-Grade Setup
bootstrap.tf (run ครั้งเดียว)
# S3 bucket for state
resource "aws_s3_bucket" "tfstate" {
bucket = "my-company-tfstate-${data.aws_caller_identity.current.account_id}"
}
resource "aws_s3_bucket_versioning" "tfstate" {
bucket = aws_s3_bucket.tfstate.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_s3_bucket_server_side_encryption_configuration" "tfstate" {
bucket = aws_s3_bucket.tfstate.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "aws:kms"
kms_master_key_id = aws_kms_key.tfstate.arn
}
}
}
resource "aws_kms_key" "tfstate" {
description = "Encryption for Terraform state"
deletion_window_in_days = 30
enable_key_rotation = true
}
# DynamoDB for locking
resource "aws_dynamodb_table" "tfstate_lock" {
name = "terraform-locks"
billing_mode = "PAY_PER_REQUEST"
hash_key = "LockID"
attribute {
name = "LockID"
type = "S"
}
point_in_time_recovery {
enabled = true
}
}
# Block public access
resource "aws_s3_bucket_public_access_block" "tfstate" {
bucket = aws_s3_bucket.tfstate.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
data "aws_caller_identity" "current" {}
สรุป
- Remote state = production standard
- S3 + DynamoDB สำหรับ AWS, GCS สำหรับ GCP, Azure Blob สำหรับ Azure
- ใช้
terraform_remote_stateอ้างอิง state อื่น - Setup: bucket + versioning + encryption + locking + IAM
- Backend config ส่วน sensitive ใช้
-backend-config
ต่อไป → State Locking