Terraform moved 블록, 이거 모르면 손해다
오늘 알게 된 건데, 아직도 terraform state mv 명령어로 리소스 옮기는 팀이 꽤 많더라. 이거 진짜 위험한 방식이다. 명령어 한 번 잘못 치면 state 파일이 꼬이고, 최악의 경우 리소스가 통째로 재생성된다. 우리 팀도 작년까지는 그렇게 했다가 프로덕션 RDS를 날려먹을 뻔한 뒤로는 무조건 moved 블록만 쓴다.
moved 블록이 뭔데?
Terraform 1.1에서 나온 기능인데, 리소스 주소가 바뀌었다는 걸 코드로 선언한다. 그게 전부다. from, to 두 개만 있으면 된다.
moved {
from = aws_instance.web
to = aws_instance.web_server
}
terraform plan 돌리면 destroy/create 대신 # (moved from ...) 이렇게 표시된다. 이게 핵심. 상태 파일은 그대로 두고 주소만 갱신하기 때문에 실제 인프라는 건드리지 않는다.
근데 왜 state mv 대신 이걸 써야 하나
세 가지 이유가 있다.
첫째, 코드 리뷰가 된다. terraform state mv aws_instance.web aws_instance.web_server 이건 누가 언제 왜 실행했는지 흔적이 안 남는다. moved 블록은 PR에 올라오니까 리뷰어가 검토할 수 있다.
둘째, CI/CD에서 동작한다. state mv는 사람이 로컬에서 쳐야 하는 명령이다. 여러 워크스페이스에 같은 변경을 적용하려면 워크스페이스 개수만큼 반복해야 한다. moved 블록은 한 번 커밋하면 모든 환경에서 자동으로 처리된다.
셋째, plan 단계에서 검증된다. 오타가 있으면 plan이 실패한다. state mv는 실행 후에야 잘못됐다는 걸 안다.
실무에서 자주 쓰는 패턴
count → for_each 전환할 때 특히 유용하다. 이거 예전엔 지옥이었다.
# 예전 코드
resource "aws_iam_user" "team" {
count = length(var.team_members)
name = var.team_members[count.index]
}
# 새 코드
resource "aws_iam_user" "team" {
for_each = toset(var.team_members)
name = each.value
}
# moved 블록 추가
moved {
from = aws_iam_user.team[0]
to = aws_iam_user.team["alice"]
}
moved {
from = aws_iam_user.team[1]
to = aws_iam_user.team["bob"]
}
리소스를 모듈 안으로 옮기는 것도 마찬가지다.
moved {
from = aws_s3_bucket.logs
to = module.logging.aws_s3_bucket.logs
}
주의할 점 하나
moved 블록은 리팩터링 끝났다고 바로 지우면 안 된다. HashiCorp 공식 권장은 "무기한 유지"인데, 특히 공유 모듈이면 필수다. 왜냐하면 이 모듈을 쓰는 다른 팀이 아직 옛날 버전 코드를 갖고 있을 수 있기 때문이다. moved 블록을 지워버리면 그 팀들 plan에서 destroy/create가 뜬다. 최소 몇 개 릴리즈 주기 정도는 남겨두는 게 안전하다.
우리 팀 규칙은 "메이저 버전 두 개 지날 때까지 유지". 이 정도면 대부분 팀이 따라온다.
혹시 아직 state mv 쓰는 분 계시면 오늘부터 갈아타시길. plan에서 미리 확인할 수 있는 것만으로도 마음이 편해진다.