Lab: ตั้งค่า Remote State บน S3

📖 text • 20 นาที

Lab: ตั้งค่า Remote State บน S3

สิ่งที่ต้องมี: Terraform + AWS Credentials ตั้งค่าแล้ว

เป้าหมาย

  1. สร้าง S3 bucket + DynamoDB table สำหรับเก็บ state
  2. ย้าย local state ไป remote
  3. ทดสอบว่า remote state ทำงานถูกต้อง

Part 1: สร้าง Backend Infrastructure

Step 1: สร้างโปรเจค

mkdir terraform-state-lab && cd terraform-state-lab
mkdir -p backend app

โครงสร้าง:

terraform-state-lab/
├── backend/   ← สร้าง S3 + DynamoDB (state ของตัวเองเก็บ local)
└── app/       ← โปรเจคจริง (ใช้ remote state)

Step 2: สร้าง Backend Resources

สร้างไฟล์ backend/main.tf:

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = "ap-southeast-1"
}

resource "random_id" "suffix" {
  byte_length = 4
}

# S3 Bucket สำหรับเก็บ state
resource "aws_s3_bucket" "state" {
  bucket = "tf-state-lab-${random_id.suffix.hex}"

  tags = {
    Name      = "Terraform State"
    ManagedBy = "terraform"
  }
}

# เปิด versioning
resource "aws_s3_bucket_versioning" "state" {
  bucket = aws_s3_bucket.state.id

  versioning_configuration {
    status = "Enabled"
  }
}

# เปิด encryption
resource "aws_s3_bucket_server_side_encryption_configuration" "state" {
  bucket = aws_s3_bucket.state.id

  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm = "AES256"
    }
  }
}

# Block public access
resource "aws_s3_bucket_public_access_block" "state" {
  bucket = aws_s3_bucket.state.id

  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

# DynamoDB Table สำหรับ state locking
resource "aws_dynamodb_table" "locks" {
  name         = "tf-state-locks-${random_id.suffix.hex}"
  billing_mode = "PAY_PER_REQUEST"
  hash_key     = "LockID"

  attribute {
    name = "LockID"
    type = "S"
  }

  tags = {
    Name      = "Terraform State Locks"
    ManagedBy = "terraform"
  }
}

# Output ค่าที่ต้องใช้ในโปรเจคอื่น
output "state_bucket" {
  value       = aws_s3_bucket.state.bucket
  description = "ชื่อ S3 bucket สำหรับเก็บ state"
}

output "dynamodb_table" {
  value       = aws_dynamodb_table.locks.name
  description = "ชื่อ DynamoDB table สำหรับ locking"
}

Step 3: Apply Backend

cd backend
terraform init
terraform apply

พิมพ์ yes — จดค่า output ไว้:

state_bucket   = "tf-state-lab-a1b2c3d4"
dynamodb_table = "tf-state-locks-a1b2c3d4"

จด output ไว้! จะใช้ใน Part 2

Part 2: ใช้ Remote State

Step 4: สร้าง App Project ด้วย Local State ก่อน

สร้างไฟล์ app/main.tf:

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = "ap-southeast-1"
}

resource "aws_s3_bucket" "app_data" {
  bucket = "tf-lab-app-data-${formatdate("YYYYMMDDhhmmss", timestamp())}"

  tags = {
    Name        = "App Data"
    Environment = "lab"
    ManagedBy   = "terraform"
  }
}

output "app_bucket_name" {
  value = aws_s3_bucket.app_data.bucket
}
cd ../app
terraform init
terraform apply -auto-approve

ตอนนี้ state อยู่ local:

ls -la terraform.tfstate
# -rw-r--r--  terraform.tfstate  ← อยู่บนเครื่อง

Step 5: Migrate ไป Remote State

เพิ่ม backend configuration ใน app/main.tf:

terraform {
  backend "s3" {
    bucket         = "tf-state-lab-XXXXXXXX"    # ← ใส่ค่าจาก output
    key            = "app/terraform.tfstate"
    region         = "ap-southeast-1"
    dynamodb_table = "tf-state-locks-XXXXXXXX"   # ← ใส่ค่าจาก output
    encrypt        = true
  }

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

แทนที่ XXXXXXXX ด้วยค่าจริงจาก Part 1 output

Step 6: Init เพื่อ Migrate

terraform init
Initializing the backend...
Do you want to copy existing state to the new backend?

  Enter a value: yes

Successfully configured the backend "s3"!

Step 7: ตรวจสอบ

# state ยังทำงานปกติ
terraform plan

ควรเห็น No changes. — state ย้ายสำเร็จ ข้อมูลครบ

# local state ไม่มีข้อมูลแล้ว
cat terraform.tfstate
{
  "version": 3,
  "backend": {
    "type": "s3",
    ...
  }
}

Local state เหลือแค่ backend config — ข้อมูล resource อยู่บน S3 แล้ว

Step 8: ตรวจสอบบน S3

aws s3 ls s3://tf-state-lab-XXXXXXXX/app/
terraform.tfstate

Part 3: Cleanup

ลบ App Resources

cd ../app
terraform destroy -auto-approve

ลบ Backend Resources

cd ../backend

# ต้องล้าง S3 bucket ก่อน (ไม่สามารถลบ bucket ที่มีไฟล์ได้)
aws s3 rm s3://tf-state-lab-XXXXXXXX --recursive

terraform destroy -auto-approve

ลบ Lab Directory

cd ../..
rm -rf terraform-state-lab

สิ่งที่ได้เรียนรู้

Concept สิ่งที่ฝึก
S3 Backend สร้าง S3 + DynamoDB สำหรับเก็บ state
Security Versioning, encryption, block public access
Migration ย้ายจาก local state ไป remote state
State Locking DynamoDB ป้องกัน concurrent access
Verification ตรวจสอบว่า remote state ทำงานถูกต้อง

สรุป

คุณเพิ่งตั้งค่า production-grade remote state:

  • S3 เก็บ state file อย่างปลอดภัย (encrypted + versioned)
  • DynamoDB ป้องกันไม่ให้ 2 คน apply พร้อมกัน
  • Migration ย้ายจาก local ไป remote ง่ายๆ ด้วย terraform init

บทถัดไปเราจะเรียนรู้เรื่อง Variables และ Outputs