Skip to content

Thực hành: State Management

🎯 Mục tiêu

🎯 Sau bài thực hành này, bạn sẽ:

  • Cấu hình remote backend (S3 + DynamoDB) cho state storage
  • Thực hiện state migration từ local sang remote
  • Import resource tồn tại vào Terraform state

Mô tả bài tập

Team của bạn đang dùng local state — mỗi người có bản state riêng, gây conflict và corruption. Bạn cần migrate sang remote backend để team cộng tác an toàn, đồng thời import một số resource đã tạo thủ công vào Terraform quản lý.

Yêu cầu

Bài 1: Tạo Remote Backend Infrastructure

Trước khi dùng remote backend, cần tạo S3 bucket và DynamoDB table:

hcl
# backend-setup/main.tf
# Chỉ chạy 1 lần để bootstrap backend infrastructure

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

resource "aws_s3_bucket" "terraform_state" {
  bucket = "my-team-terraform-state"

  # TODO: Bật versioning để recovery
  # TODO: Bật server-side encryption
}

resource "aws_s3_bucket_versioning" "enabled" {
  # TODO: Enable versioning
}

resource "aws_s3_bucket_server_side_encryption_configuration" "default" {
  # TODO: AES256 encryption
}

resource "aws_s3_bucket_public_access_block" "public_access" {
  # TODO: Block ALL public access
}

resource "aws_dynamodb_table" "terraform_locks" {
  name         = "terraform-state-locks"
  billing_mode = "PAY_PER_REQUEST"
  hash_key     = "LockID"

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

Bài 2: Migrate Local → Remote State

Sau khi backend infrastructure sẵn sàng, cấu hình project để dùng remote backend:

hcl
# main.tf — Thêm backend configuration
terraform {
  backend "s3" {
    bucket         = "my-team-terraform-state"
    key            = "dev/infrastructure/terraform.tfstate"
    region         = "ap-southeast-1"
    dynamodb_table = "terraform-state-locks"
    encrypt        = true
  }
}

Quy trình migration:

bash
# 1. Thêm backend config vào main.tf
# 2. Chạy init để migrate
terraform init -migrate-state

# 3. Xác nhận state đã chuyển
terraform state list

# 4. Xóa local state file (đã có trên remote)
# Terraform tự xử lý, nhưng verify trước khi xóa .terraform/

Câu hỏi: State key nên theo convention nào? Tại sao dùng dev/infrastructure/ prefix?

Bài 3: Import Existing Resource

Đồng nghiệp đã tạo thủ công 1 S3 bucket qua console. Import vào Terraform:

hcl
# Step 1: Viết HCL resource block TRƯỚC
resource "aws_s3_bucket" "legacy_data" {
  bucket = "legacy-data-bucket-12345"

  tags = {
    Name      = "Legacy Data"
    ManagedBy = "terraform"
  }
}

# Step 2: Import
# TODO: Viết lệnh terraform import

Sau import, chạy terraform plan để kiểm tra drift:

bash
# Nếu plan hiển thị changes, cần cập nhật HCL cho khớp với actual state
terraform plan
# Mục tiêu: "No changes. Your infrastructure matches the configuration."

Gợi ý

💡 Xem gợi ý
  • Bài 1: S3 versioning cho phép recovery state file bị corrupt. DynamoDB cung cấp locking. Public access block là bắt buộc
  • Bài 2: terraform init -migrate-state tự động copy local state lên remote. Luôn backup state trước khi migrate
  • Bài 2: State key convention: <env>/<component>/terraform.tfstate — phân tách theo environment và component
  • Bài 3: Syntax: terraform import aws_s3_bucket.legacy_data legacy-data-bucket-12345. Sau import, chạy plan để phát hiện config drift
  • Lưu ý: terraform import chỉ import state, KHÔNG generate HCL code (Terraform 1.5+ có import block)

Lời giải

✅ Xem lời giải

Bài 1: Backend Setup hoàn chỉnh

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

resource "aws_s3_bucket" "terraform_state" {
  bucket = "my-team-terraform-state"
  tags   = { Name = "Terraform State" }
}

resource "aws_s3_bucket_versioning" "enabled" {
  bucket = aws_s3_bucket.terraform_state.id
  versioning_configuration {
    status = "Enabled"
  }
}

resource "aws_s3_bucket_server_side_encryption_configuration" "default" {
  bucket = aws_s3_bucket.terraform_state.id
  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm = "aws:kms"
    }
  }
}

resource "aws_s3_bucket_public_access_block" "public_access" {
  bucket                  = aws_s3_bucket.terraform_state.id
  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

resource "aws_dynamodb_table" "terraform_locks" {
  name         = "terraform-state-locks"
  billing_mode = "PAY_PER_REQUEST"
  hash_key     = "LockID"

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

  tags = { Name = "Terraform Lock Table" }
}

Bài 3: Import commands

bash
# Import existing bucket
terraform import aws_s3_bucket.legacy_data legacy-data-bucket-12345

# Check for drift
terraform plan

# Nếu có drift, cập nhật HCL cho khớp
# Terraform 1.5+ import block (khai báo trong HCL):
import {
  to = aws_s3_bucket.legacy_data
  id = "legacy-data-bucket-12345"
}

Liên kết liên quan