init -migrate-state.flowchart LR
Dev1 -- init/plan/apply --> S3[(State Bucket)]
Dev2 -- init/plan/apply --> S3
S3 -- 버전관리/암호화 --> S3v[Versioned Objects]
DDB[(DynamoDB Lock)] --- 잠금/해제 --- S3
Dev1/Dev2 → S3(State Bucket)
terraform init/plan/apply를 실행하면, 상태 파일(.tfstate)을 S3 버킷에 저장/읽기S3 — “버전관리/암호화” → Versioned Objects
DynamoDB Lock — 잠금/해제 — S3
apply 하면 망함. 그래서 DynamoDB 테이블이 “락(잠금)” 을 잡아 동시 실행을 막아 줌실제 값(버킷/테이블 이름)은 아래 3-2에서 만든 리소스에 맞춰 바꿔야함
# backend.hcl (예: 루트에 위치)
terraform {
backend "s3" {
bucket = "my-tfstate-bucket"
key = "project/dev/app.tfstate" # 워크스페이스/스택별로 경로 규칙화 추천
region = "ap-northeast-2"
encrypt = true
dynamodb_table = "tf-locks"
}
}
백엔드 선언 전에 한 번 만들어두면 편함 (별도 스택 권장)
variable "bucket_name" { default = "my-tfstate-bucket" }
variable "table_name" { default = "tf-locks" }
resource "aws_s3_bucket" "state" {
bucket = var.bucket_name
force_destroy = false
tags = { Name = var.bucket_name }
}
resource "aws_s3_bucket_versioning" "versioning" {
bucket = aws_s3_bucket.state.id
versioning_configuration { status = "Enabled" }
}
resource "aws_s3_bucket_server_side_encryption_configuration" "sse" {
bucket = aws_s3_bucket.state.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256" # 운영은 KMS 권장 (aws:kms + kms_key_id)
}
}
}
resource "aws_s3_bucket_public_access_block" "block" {
bucket = aws_s3_bucket.state.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
resource "aws_dynamodb_table" "lock" {
name = var.table_name
billing_mode = "PAY_PER_REQUEST"
hash_key = "LockID"
attribute { name = "LockID"; type = "S" }
}
app.tfstate 객체의 버전 탭을 보면 이전 버전들이 쌓입니다.terraform apply를 시도하면 한쪽이 “state is locked” 메시지를 보고 대기/실패합니다.LockID)을 수동 삭제는 신중히(정말 아무도 작업 중이 아니라고 확신될 때만).AccessDenied / KMS 권한 오류
kms:Encrypt/Decrypt/GenerateDataKey 포함 필요.state is locked
리전/버킷 이름 불일치
backend "s3"의 region, bucket, key가 실제 리소스와 맞는지 재확인.버전관리 미설정
project/env/stack.tfstate처럼 명확한 경로 규칙terraform init -migrate-state 로 안전 이전.tfstate, .tfstate.backup, .terraform/)S3에 상태를 버전/암호화로 안전하게 보관하고, DynamoDB 락으로 동시 실행을 막아 충돌과 손상 없는 팀 협업을 만든다.
terraform {
backend "s3" {
bucket = "my-tfstate-bucket"
key = "project/dev/network.tfstate"
region = "ap-northeast-2"
encrypt = true # KMS 연동 권장
dynamodb_table = "tf-locks" # 동시 실행 락
}
}
resource "aws_s3_bucket" "terraform_state" {
bucket = var.bucket_name
force_destroy = true
tags = { Name = var.bucket_name }
}
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 = "AES256" } # 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_lock" {
name = var.table_name
billing_mode = "PAY_PER_REQUEST"
hash_key = "LockID"
attribute { name = "LockID"; type = "S" }
}
terraform init -migrate-stateproject/${terraform.workspace}/stack.tfstate)로 분리ignore_changes 남용 주의global/s3참고 템플릿 (루트의
terraform { backend "s3" {} }블록에 적용)terraform { backend "s3" { bucket = "<your-bucket>" key = "tfstate/${terraform.workspace}/terraform.tfstate" region = "ap-northeast-2" dynamodb_table = "<your-lock-table>" encrypt = true } }