terraform.tfstate 삭제했을 때 복구하는 법: 백업 파일, S3 버저닝, 재임포트 순서

4 분 소요

terraform plan을 실행했더니 어제까지 멀쩡히 관리되던 인프라 전부를 새로 만들겠다고 합니다.

출력 예시
Plan: 47 to add, 0 to change, 0 to destroy.

인프라는 AWS에 그대로 살아 있는데 테라폼만 기억을 잃은 상태, 즉 state가 삭제됐거나 손상된 상황입니다. 결론을 앞에 두겠습니다. 지금 절대 하지 말아야 할 일은 apply이고, 복구 경로는 세 가지가 우선순위대로 있습니다. 대부분의 경우 생각보다 가볍게 복구됩니다.

왜 apply 하면 안 되는가 #

테라폼은 state가 비어 있으면 아무것도 관리하지 않는다고 믿습니다(테라폼 기초 강좌 #5). 이 상태로 apply 하면 이미 있는 리소스를 또 만들려고 시도하고, 결과는 둘 중 하나입니다. 이름이 유일해야 하는 리소스(S3 버킷, IAM 역할)는 충돌 에러로 실패하고, 이름 제약이 없는 리소스(EC2 인스턴스 등)는 실제로 중복 생성됩니다. 운영 인프라 옆에 복제 인프라가 생기고 요금도 두 배가 되는 사고입니다. 복구가 끝날 때까지 apply는 봉인합니다.

경로 1: 로컬 백업 파일 #

로컬 백엔드(원격 백엔드 설정 전)였다면 테라폼이 남긴 직전 백업부터 확인합니다.

백업 파일 확인
ls -la terraform.tfstate*
# terraform.tfstate          ← 사라졌거나 손상된 파일
# terraform.tfstate.backup   ← 직전 상태의 자동 백업

테라폼은 state를 쓸 때마다 직전 내용을 terraform.tfstate.backup으로 남깁니다. 이 파일이 있다면 복구는 복사 한 번입니다.

백업 복원
cp terraform.tfstate.backup terraform.tfstate
terraform plan   # "No changes" 또는 마지막 작업 하나 분량의 diff면 성공

백업이 마지막 apply 직전 시점이므로, plan에 마지막 작업 한 건 분량의 변경이 나타날 수 있습니다. 그 정도면 정상 복구입니다.

경로 2: S3 버저닝에서 이전 버전 복원 #

S3 백엔드에 버저닝을 켜 두었다면(테라폼 기초 강좌 #9에서 “필수"라고 강조한 이유가 바로 이런 순간 때문입니다), 삭제·손상 이전 버전이 버킷에 그대로 남아 있습니다. 버전 목록을 확인합니다.

버전 목록 확인
aws s3api list-object-versions \
  --bucket myapp-tfstate \
  --prefix myapp/prod/terraform.tfstate \
  --query 'Versions[].{Id:VersionId, Time:LastModified, Latest:IsLatest}'

사고 이전 시각의 VersionId를 골라 그 버전을 최신으로 되돌립니다. 같은 키에 해당 버전을 복사하는 방식입니다.

버전 복원
aws s3api copy-object \
  --bucket myapp-tfstate \
  --key myapp/prod/terraform.tfstate \
  --copy-source "myapp-tfstate/myapp/prod/terraform.tfstate?versionId=<VersionId>"

파일이 삭제된 경우라면 삭제 마커(DeleteMarker)가 최신 버전으로 남아 있는 형태이므로, delete-object --version-id로 삭제 마커를 지우는 것만으로도 직전 버전이 되살아납니다. 복원 후 terraform plan으로 상태를 확인하고, 복원 시점 이후에 있었던 apply 분량의 어긋남이 있으면 그 차이만 정리하면 됩니다.

경로 3: 백업이 전혀 없다면 재임포트 #

로컬 백업도 버저닝도 없다면 남은 길은 state의 재구축입니다. 코드는 남아 있으므로(코드마저 없다면 그것부터 복구합니다), 코드의 리소스 하나하나를 실제 리소스에 다시 연결하는 작업이 됩니다. 도구는 import 블록이고, 절차는 terraform import 하는 법에 정리해 두었습니다. 요령만 추리면 이렇습니다.

  • 목록부터 만듭니다: 코드의 리소스 주소 목록을 뽑고(grep -r "^resource" 수준이면 충분합니다), 각각의 import ID를 콘솔·CLI에서 확인해 표로 만듭니다.
  • 의존성 순서로 진행합니다: VPC처럼 참조되는 쪽을 먼저, 참조하는 쪽을 나중에 편입해야 중간 plan이 덜 어지럽습니다.
  • 완료 기준은 “변경 없음” plan입니다: 전부 편입한 뒤 plan이 import 외 변경 0이면 재구축 완료입니다.

리소스가 수십 개면 반나절 이상 걸리는 노동이지만, 인프라를 부수지 않고 복구하는 확실한 길입니다.

재발을 구조로 막기 #

복구를 마쳤으면 같은 사고가 다시 일어나지 않을 구조를 확인합니다.

  1. 원격 백엔드 + 버저닝: 로컬 state는 이 사고에 무방비입니다. S3 백엔드로 옮기고 버킷 버저닝을 켭니다. 버저닝이 켜진 순간부터 이 글의 복구는 명령 두 줄로 줄어듭니다.
  2. state 버킷 삭제 방지: 버킷 자체의 실수 삭제를 막는 정책(퍼블릭 차단은 기본, 필요하면 MFA Delete나 버킷 정책의 삭제 제한)을 검토합니다.
  3. 사람이 state 파일을 직접 만지지 않기: 손상 사고의 상당수는 수동 편집에서 옵니다. 조작이 필요하면 state 명령과 moved·removed 블록을 씁니다(테라폼 운영 강좌 #2).

정리 #

  • state가 사라지면 plan이 전부 새로 만들겠다고 합니다. 이때 apply 하면 충돌 또는 중복 생성이므로 복구 전까지 봉인합니다
  • 복구 우선순위는 로컬 terraform.tfstate.backup 복사 → S3 버저닝 이전 버전 복원(삭제 마커 제거 포함) → import 재구축 순입니다
  • 버저닝 복원 후에는 복원 시점 이후 변경분만 plan으로 정리하면 됩니다
  • 재임포트는 리소스 목록 표를 만들고 의존성 순서로, “변경 없음” plan을 완료 기준으로 진행합니다
  • 재발 방지는 원격 백엔드와 버저닝입니다. 이 구조에서 state 분실은 사고가 아니라 명령 두 줄로 끝나는 가벼운 일이 됩니다
X