Bước 6: Remote State với S3 + DynamoDB
Mặc định Terraform lưu state tại terraform.tfstate trên máy local. Khi làm nhóm hoặc CI/CD, cần remote state để:
- Chia sẻ state giữa nhiều người
- Tránh conflict khi nhiều người apply cùng lúc (locking)
- Không mất state khi máy local hỏng
Kiến trúc Remote State
┌─────────────────────────────────────────────────────┐
│ Remote State │
│ │
│ ┌──────────────────┐ ┌──────────────────────┐ │
│ │ S3 Bucket │ │ DynamoDB Table │ │
│ │ │ │ │ │
│ │ terraform.tfstate│ │ LockID (primary key)│ │
│ │ (encrypted) │ │ State locking │ │
│ └──────────────────┘ └──────────────────────┘ │
│ ▲ ▲ │
│ │ read/write │ lock/unlock │
│ └────────────┬───────────┘ │
│ │ │
│ Terraform CLI │
└─────────────────────────────────────────────────────┘
Bước 1: Tạo S3 Bucket và DynamoDB (một lần duy nhất)
Tạo file bootstrap/main.tf — chạy trước khi setup backend:
# bootstrap/main.tf — Chạy một lần để tạo infrastructure cho remote state
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "ap-southeast-1"
}
# S3 bucket lưu state files
resource "aws_s3_bucket" "terraform_state" {
bucket = "myapp-terraform-state-${random_id.suffix.hex}"
# Ngăn xóa nhầm bucket
lifecycle {
prevent_destroy = true
}
}
resource "random_id" "suffix" {
byte_length = 4
}
# Bật versioning — có thể rollback state
resource "aws_s3_bucket_versioning" "terraform_state" {
bucket = aws_s3_bucket.terraform_state.id
versioning_configuration {
status = "Enabled"
}
}
# Mã hóa state file
resource "aws_s3_bucket_server_side_encryption_configuration" "terraform_state" {
bucket = aws_s3_bucket.terraform_state.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}
# Chặn public access
resource "aws_s3_bucket_public_access_block" "terraform_state" {
bucket = aws_s3_bucket.terraform_state.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
# DynamoDB table cho state locking
resource "aws_dynamodb_table" "terraform_locks" {
name = "myapp-terraform-locks"
billing_mode = "PAY_PER_REQUEST"
hash_key = "LockID"
attribute {
name = "LockID"
type = "S"
}
}
output "s3_bucket_name" {
value = aws_s3_bucket.terraform_state.bucket
}
output "dynamodb_table_name" {
value = aws_dynamodb_table.terraform_locks.name
}
cd bootstrap
terraform init
terraform apply
# Ghi lại output: s3_bucket_name và dynamodb_table_name
Bước 2: Cấu hình Backend trong Project Chính
Cập nhật versions.tf:
terraform {
required_version = ">= 1.5"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
# Thêm backend block
backend "s3" {
bucket = "myapp-terraform-state-XXXX" # Thay bằng tên bucket thật
key = "ec2-guide/terraform.tfstate"
region = "ap-southeast-1"
encrypt = true
dynamodb_table = "myapp-terraform-locks"
}
}
Key path theo environment
Dùng key khác nhau cho dev/prod:
ec2-guide/dev/terraform.tfstateec2-guide/prod/terraform.tfstate
Bước 3: Migrate State Local → Remote
Nếu đã có state local, migrate lên S3:
# Init lại với backend mới — Terraform sẽ hỏi có muốn migrate không
terraform init -migrate-state
# Output:
# Initializing the backend...
# Do you want to copy existing state to the new backend? (yes/no)
# > yes
# Successfully configured the backend "s3"!
Bước 4: Kiểm tra
# Xem state đang ở đâu
terraform state list
# Pull state về xem (không modify)
terraform state pull
# Kiểm tra trên AWS Console: S3 bucket → object terraform.tfstate
Partial Backend Config (Khuyến nghị cho Team)
Không hardcode tên bucket trong versions.tf — truyền qua file config:
# versions.tf — chỉ khai báo type
terraform {
backend "s3" {}
}
# backend.hcl — gitignore file này nếu có thông tin nhạy cảm
# hoặc commit nếu không có secret
bucket = "myapp-terraform-state-xxxx"
key = "ec2-guide/terraform.tfstate"
region = "ap-southeast-1"
encrypt = true
dynamodb_table = "myapp-terraform-locks"
# Init với backend config file
terraform init -backend-config="backend.hcl"
State Locking hoạt động như thế nào?
# Khi bạn chạy terraform apply:
# 1. Terraform ghi lock vào DynamoDB: LockID = "myapp-terraform-state-xxx/terraform.tfstate"
# 2. Apply chạy...
# 3. Nếu người khác chạy apply cùng lúc → báo lỗi:
# Error: Error acquiring the state lock
# Lock Info: ID=xxx, Who=user@machine, Operation=apply
# Nếu process bị kill và lock không tự release:
terraform force-unlock LOCK_ID
Commit
git add versions.tf bootstrap/
git commit -m "feat(terraform): thêm S3 remote state backend với DynamoDB locking"