moved는 리소스의 주소(address)가 바뀌었을 때, 기존 상태(State)를 파괴 없이 새 주소로 이전하라고 Terraform에 알려주는 선언형 리팩터링 도구. (Terraform v1.1+)
aws_instance.web, aws_subnet.public["ap-northeast-2a"], aws_sg.alb[0]count → for_each 전환, 리소스 분리/병합)으로 주소가 바뀌면 기본적으로 TF는 “옛 주소 Destroy, 새 주소 Create” 를 계획합니다.moved 블록을 두면: Destroy/Create 없이 State만 새 주소로 이동 → 다운타임/재생성 방지!문법(모듈 최상위에 선언)
moved {
from = <이전_주소>
to = <새_주소>
}
보통 “새 주소가 존재하는 모듈” 안에
moved를 둡니다. 여러 항목을 옮길 땐moved블록을 여러 개 나란히 작성.
aws_security_group.web → aws_security_group.albcount → for_each 전환: 인덱스 기반을 키 기반으로 바꿈주의: 일반적으로 모듈 경계를 넘는 이동(부모↔자식) 은
moved만으로 해결되지 않습니다. 그런 경우엔 상황에 따라terraform state mv(아래 비교) 등을 병행하세요.
# 이전
resource "aws_security_group" "web" { ... }
# 이후 (이름만 바꿈)
resource "aws_security_group" "alb" { ... }
# 상태 이전
moved {
from = aws_security_group.web
to = aws_security_group.alb
}
효과: 실제 SG는 그대로, State만 web → alb 주소로 이동.
count → for_each 전환 (인덱스를 키로 매핑)# 이전 (count)
resource "aws_subnet" "public" {
count = 2
cidr_block = var.public_cidrs[count.index]
# ...
}
# 이후 (for_each, 키는 AZ 이름)
resource "aws_subnet" "public" {
for_each = { "ap-northeast-2a" = "10.0.1.0/24", "ap-northeast-2c" = "10.0.2.0/24" }
cidr_block = each.value
}
# 상태 이전(인덱스→키를 1:1로 대응)
moved { from = aws_subnet.public[0] to = aws_subnet.public["ap-northeast-2a"] }
moved { from = aws_subnet.public[1] to = aws_subnet.public["ap-northeast-2c"] }
팁: 매핑을 정확히 하지 않으면 누락된 인스턴스가 새로 생성되거나 기존이 파괴될 수 있어요.
# 이전: SG 블록 안에 인라인 ingress 한 개
resource "aws_security_group" "web" {
name = "web-sg"
# ...
ingress { from_port=80 to_port=80 protocol="tcp" cidr_blocks=["0.0.0.0/0"] }
}
# 이후: 전용 리소스로 분리
resource "aws_security_group" "web" { name = "web-sg" /* ingress 제거 */ }
resource "aws_security_group_rule" "web_http" {
type = "ingress"
from_port=80 to_port=80 protocol="tcp"
cidr_blocks=["0.0.0.0/0"]
security_group_id = aws_security_group.web.id
}
# 상태 이전 (인라인 → 전용 리소스의 특정 주소로)
moved {
from = aws_security_group.web.ingress[0]
to = aws_security_group_rule.web_http
}
효과: 기존 인라인 규칙이 새 리소스로 자연스럽게 이전되어 재생성 최소화.
# 이전
resource "aws_iam_user" "u" {
for_each = { a = "alice", b = "bob" }
name = each.value
}
# 이후 (key b → bob, key a → alice-kr 처럼 변경)
resource "aws_iam_user" "u" {
for_each = { bob = "bob", alice-kr = "alice" }
name = each.value
}
moved { from = aws_iam_user.u["a"] to = aws_iam_user.u["alice-kr"] }
moved { from = aws_iam_user.u["b"] to = aws_iam_user.u["bob"] }
포인트: 키는 주소의 일부라 바뀌면 새 리소스로 인식됩니다. moved로 안전 이전!
moved 블록 추가 (필요한 경우 여러 개)terraform plan으로 Destroy/Create가 없는지 확인terraform apply로 상태 이전 수행moved 블록을 한동안 유지(새로 합류한 환경 배려) 후 정리from/to는 정확한 주소여야 해요. (키/인덱스 철저히 매핑)plan에서 확인 (destroy/create가 뜨면 매핑 재점검)for_each 키는 안정적인 값으로 설계 (환경/가변 값 기반 키는 피하기)moved는 주로 같은 모듈 안의 주소 변경에 사용terraform state mv가 필요할 수 있음)moved만으론 불가moved vs terraform state mv| 항목 | moved (선언형) | terraform state mv (명령형) |
|---|---|---|
| 적용 범위 | 코드에 남음(리뷰/재현성, CI에 유리) | 한 번만 실행(그 스테이트에만) |
| 협업/환경 수 | 여러 환경에 일관 적용 | 환경마다 반복 실행 필요 |
| 크로스 모듈 이동 | 제한적/비권장 | 가능 |
| 사용 난이도 | 쉽고 안전 (Plan으로 검증) | 숙련 필요, 잘못하면 드리프트 |
| 추천 용도 | 일반적인 리팩터링(이름/키/모드 전환) | 특수 처리/긴급 핫픽스/경계 넘는 이동 |
실무에선 기본은
moved로, 예외 상황만state mv를 보완적으로 사용.
Q. moved 블록은 영원히 놔둬야 하나요?
A. 필수는 아니지만, 모든 환경/파이프라인이 변경을 소화할 때까지 유지하는 걸 권장합니다. 이후 깔끔하게 제거해도 돼요.
Q. moved로 타입(리소스 종류)까지 바꿀 수 있나요?
A. 보통 같은 타입/동등한 인스턴스 간 주소 이전에 적합합니다. 프로바이더/타입이 바뀌면 재생성될 수 있어요.
Q. moved만 추가했는데도 새로 만들겠다고 해요.
A. from/to 매핑이 틀렸거나 누락, 혹은 실제 속성 변경이 교체 조건을 만들어서 그래요. Plan에서 원인을 확인하고 매핑을 보완하세요.
moved= “이 주소에서 저 주소로 상태만 옮겨라”는 선언.
이름·키·반복 방식이 바뀌어도 파괴 없이 리팩터링하려면, 작은 단위로moved를 추가하고plan으로 검증하자.
.tfstate 와.tfstate.backupmoved 블록은 “리소스 주소만 바뀌었다”는 걸 Terraform에 알려 상태(state) 를 파괴 없이 새 주소로 이전하라고 지시한다..tfstate 파일의 내용(주소 키) 이고, 적용 시 기존 파일은 .tfstate.backup 으로 자동 백업된다.moved 블록을 추가하고 apply를 하면 Terraform은:
moved { from → to } 매핑대로 state 내부의 리소스 주소를 재매핑.tfstate.backup 으로 복사.tfstate 를 저장Plan만 하고 Apply를 하지 않으면
.tfstate/.tfstate.backup은 그대로다.
상태 파일이 실제로 바뀌는 시점은 Apply다.
resource "aws_security_group" "web" { ... }
resource "aws_security_group" "alb" { ... }
moved {
from = aws_security_group.web
to = aws_security_group.alb
}
적용 전 Plan: moved from aws_security_group.web to aws_security_group.alb 표시
적용 후:
.tfstate 안의 주소가 web → alb로 바뀜.tfstate.backup(적용 직전 상태) 생김terraform.tfstate와 terraform.tfstate.backup 파일이 생김.어떤 백엔드든 **moved는 “state의 키 이동”**을 수행하고, 백업은 ‘state 쓰기’ 과정의 일반 동작이다.
# 변경 전/후 주소 목록 비교
terraform state list
# 특정 리소스의 상태 보기
terraform state show aws_security_group.alb
moved의 from/to를 재확인.moved는 state 키 이동만 한다(실물 리소스 무변경).tfstate가 갱신되고 **.tfstate.backup**이 자동 생성terraform state mv(주의) 또는 백업/버전에서 복원