I changed one attribute and the file was recreated
한국어 원문으로 표시합니다.
목표
진짜 선언형 도구(OpenTofu)가 내는 계획을 읽고, 종료 코드·저장된 계획·계획 JSON 으로 자동 판정을 만들며, prevent_destroy 가 막는 것과 못 막는 것을 직접 확인합니다.
왜 중요한가
앞 실습에서 셸로 흉내 낸 plan 의 기호가 실제 도구에서는 프로바이더의 규칙을 따릅니다. 파일 내용 한 줄을 고쳤을 뿐인데 리소스가 교체되는 일은 흔하고, 그걸 계획에서 읽어 내지 못하면 적용 뒤에야 알게 됩니다. 자동화는 계획을 사람 대신 읽어야 하므로 종료 코드와 JSON 을 약속대로 다뤄야 하고, 리뷰한 것과 적용된 것이 같다는 보장은 저장된 계획에서 나옵니다. 삭제 보호 역시 설정 안에 있을 때만 효력이 있다는 한계를 알아야 리뷰에서 무엇을 볼지 정해집니다.
단계
/root/iac-plan/main.tf에 local·random 프로바이더를 요구하고 리소스 셋을 선언하세요.local_file.motd는${path.module}/out/motd.txt에hello v1한 줄(끝에 줄바꿈)을 쓰고,terraform_data.release는 input 이v1,random_pet.suffix는 keepers 가team = "blue"입니다.tofu init과tofu apply -auto-approve로 적용하세요./root/iac-plan/gate.sh <작업디렉터리>를 만드세요. 그 디렉터리에서tofu plan -detailed-exitcode를 돌려 변경이 없으면clean을 출력하고 0, 변경이 있으면changes와 2, 오류면error와 1 로 끝냅니다. 출력은 그 낱말 한 줄뿐입니다. 채점기는 변경 없음·변경 있음·설정 오류 세 가지 임시 디렉터리를 만들어 이 스크립트를 돌립니다.main.tf에서 세 값을 바꾸세요: motd 내용은hello v2, release 의 input 은v2, suffix 의 keepers team 은green. 적용하지 말고tofu plan -out=/root/iac-plan/change.tfplan으로 계획을 저장한 뒤,/root/iac-plan/actions.txt에 바뀌는 리소스마다<주소> <create|update|replace|delete>한 줄씩 적으세요. 무엇이 제자리 수정이고 무엇이 교체인지는 계획 출력의 기호와tofu show -json의 actions 로 판단합니다.- 먼저
tofu apply change.tfplan으로 저장한 계획을 그대로 적용하세요. 그다음 motd 내용을hello v3로 바꾸고tofu plan -out=/root/iac-plan/late.tfplan으로 저장합니다. 이 계획을 적용하기 전에 다른 사람이 끼어든 상황을 만듭니다:tofu apply -auto-approve -replace=random_pet.suffix -target=random_pet.suffix를 먼저 실행하세요(suffix 하나만 교체하고 motd 는 건드리지 않습니다). 이제tofu apply late.tfplan을 실행해 표준 출력과 오류를 함께/root/iac-plan/stale.txt에 저장하세요. /root/iac-plan/summary.sh <계획JSON>을 만드세요.tofu show -json결과 파일을 받아create=<수> update=<수> replace=<수> delete=<수>한 줄을 출력합니다. 교체(삭제와 생성이 한 리소스에 함께 있는 것)는 replace 한 건으로만 셉니다. 그리고 지금 작업 디렉터리의 계획을/root/iac-plan/now.json으로 저장하고(tofu plan -out후tofu show -json), 그 요약을/root/iac-plan/now-summary.txt에 저장하세요.local_file.motd에lifecycle { prevent_destroy = true }를 더하세요. 내용이hello v3인 채로tofu plan을 돌려 출력(오류 포함)을/root/iac-plan/guard.txt에 저장합니다. 그다음 motd 내용을hello v2로 되돌려 계획이 다시 깨끗해지게 하세요(작업 디렉터리의 계획이 변경 없음이어야 합니다). 채점기는 사본에서 motd 의 내용을 바꾼 계획을 세워 보호가 실제로 막는지 확인합니다./root/iac-plan를 상태 파일까지 통째로/root/iac-plan-bypass로 복사하세요(cp -a). 사본의main.tf에서local_file.motd리소스 블록을 lifecycle 까지 통째로 지우고, 사본에서tofu plan -out=/root/iac-plan-bypass/bypass.tfplan을 저장합니다. 적용하지는 않습니다. 원본은 그대로 둡니다./root/iac-plan/review.sh <작업디렉터리>를 만드세요. 그 디렉터리의 계획을 임시 파일로 저장해summary.sh로 세고, 변경이 없으면CLEAN과 0, 추가·수정만 있으면AUTO로 시작하는 한 줄과 2, 교체나 삭제가 하나라도 있으면REVIEW로 시작하는 한 줄과 3, 계획이 실패하면ERROR와 1 로 끝냅니다. 작업 디렉터리에 계획 파일을 남기지 않습니다. 채점기는 원본(CLEAN), 우회 사본(REVIEW), 그리고 스스로 만든 임시 디렉터리들로 확인합니다.
참고
- 파드에는 OpenTofu 1.9.0 이
tofu(같은 바이너리를terraform으로도 부를 수 있음)로 들어 있고, local·random·null·tls 프로바이더는 파드 안 미러에서 받습니다. 인터넷은 필요 없습니다. - 계획 JSON:
tofu show -json <계획파일>— 계획 파일은 그것을 만든 작업 디렉터리에서 읽어야 프로바이더 스키마를 찾습니다. - 흔한 실수: 스크립트에
set -e를 걸어 -detailed-exitcode 의 2 에서 스크립트가 먼저 죽는 것. - 흔한 실수: 교체를 추가 하나와 삭제 하나로 두 번 세는 것.
- tofu plan (-detailed-exitcode) · tofu apply (Saved Plan Mode) · JSON Output Format · lifecycle · Terraform plan
세 가지 리소스를 선언하고 한 번 적용한다
/root/iac-plan/main.tf 에 local·random 프로바이더를 요구하고 리소스 셋을 선언하세요. local_file.motd 는 ${path.module}/out/motd.txt 에 hello v1 한 줄(끝에 줄바꿈)을 쓰고, terraform_data.release 는 input 이 v1, random_pet.suffix 는 keepers 가 team = "blue" 입니다. tofu init 과 tofu apply -auto-approve 로 적용하세요.
local·random 은 파드 안 미러에서 받아지므로 인터넷이 없어도 init 이 됩니다. terraform_data 는 프로바이더 없이 쓰는 내장 리소스입니다. 적용 뒤 tofu state list 로 상태가 아는 주소를 확인하세요.
종료 코드 세 가지를 낱말로 바꾸는 게이트
/root/iac-plan/gate.sh <작업디렉터리> 를 만드세요. 그 디렉터리에서 tofu plan -detailed-exitcode 를 돌려 변경이 없으면 clean 을 출력하고 0, 변경이 있으면 changes 와 2, 오류면 error 와 1 로 끝냅니다. 출력은 그 낱말 한 줄뿐입니다. 채점기는 변경 없음·변경 있음·설정 오류 세 가지 임시 디렉터리를 만들어 이 스크립트를 돌립니다.
-detailed-exitcode 는 0·1·2 세 값을 냅니다. set -e 를 걸면 2 에서 스크립트가 먼저 끝나 버립니다. 계획만 세우는 명령에는 -lock=false 를 붙여 남의 작업을 막지 않게 할 수 있습니다.
속성 하나 고쳤는데 새로 만들어진다
main.tf 에서 세 값을 바꾸세요: motd 내용은 hello v2, release 의 input 은 v2, suffix 의 keepers team 은 green. 적용하지 말고 tofu plan -out=/root/iac-plan/change.tfplan 으로 계획을 저장한 뒤, /root/iac-plan/actions.txt 에 바뀌는 리소스마다 <주소> <create|update|replace|delete> 한 줄씩 적으세요. 무엇이 제자리 수정이고 무엇이 교체인지는 계획 출력의 기호와 tofu show -json 의 actions 로 판단합니다.
~ 는 제자리 수정, -/+ 는 지우고 다시 만드는 교체입니다. JSON 에서는 actions 가 ["update"] 인지 ["delete","create"] 인지로 보입니다. 어떤 속성이 교체를 강제하는지는 프로바이더가 정합니다 — 계획 출력에서 # forces replacement 표시를 찾으세요.
리뷰한 계획만 적용되고, 낡은 계획은 거절된다
먼저 tofu apply change.tfplan 으로 저장한 계획을 그대로 적용하세요. 그다음 motd 내용을 hello v3 로 바꾸고 tofu plan -out=/root/iac-plan/late.tfplan 으로 저장합니다. 이 계획을 적용하기 전에 다른 사람이 끼어든 상황을 만듭니다: tofu apply -auto-approve -replace=random_pet.suffix -target=random_pet.suffix 를 먼저 실행하세요(suffix 하나만 교체하고 motd 는 건드리지 않습니다). 이제 tofu apply late.tfplan 을 실행해 표준 출력과 오류를 함께 /root/iac-plan/stale.txt 에 저장하세요.
저장된 계획에는 계획을 세울 때의 상태가 함께 들어 있습니다. 그 사이에 상태가 바뀌었으면 OpenTofu 는 계획을 적용하지 않습니다. -replace 는 설정을 바꾸지 않고 그 리소스를 교체하게 하는 옵션이고, -target 은 계획의 범위를 그 리소스로 좁힙니다(-target 이 없으면 main.tf 의 v3 변경까지 함께 적용됩니다). -no-color 를 붙이면 저장한 파일이 읽기 쉽습니다.
계획 JSON 에서 동작을 센다
/root/iac-plan/summary.sh <계획JSON> 을 만드세요. tofu show -json 결과 파일을 받아 create=<수> update=<수> replace=<수> delete=<수> 한 줄을 출력합니다. 교체(삭제와 생성이 한 리소스에 함께 있는 것)는 replace 한 건으로만 셉니다. 그리고 지금 작업 디렉터리의 계획을 /root/iac-plan/now.json 으로 저장하고(tofu plan -out 후 tofu show -json), 그 요약을 /root/iac-plan/now-summary.txt 에 저장하세요.
resource_changes 의 각 원소에 change.actions 배열이 있습니다. 변경 없는 리소스는 ["no-op"] 이라 어느 칸에도 세지 않습니다. jq 에서 배열끼리 == 로 비교할 수 있습니다.
보호를 걸었더니 교체도 막혔다
local_file.motd 에 lifecycle { prevent_destroy = true } 를 더하세요. 내용이 hello v3 인 채로 tofu plan 을 돌려 출력(오류 포함)을 /root/iac-plan/guard.txt 에 저장합니다. 그다음 motd 내용을 hello v2 로 되돌려 계획이 다시 깨끗해지게 하세요(작업 디렉터리의 계획이 변경 없음이어야 합니다). 채점기는 사본에서 motd 의 내용을 바꾼 계획을 세워 보호가 실제로 막는지 확인합니다.
local_file 의 content 는 바뀌면 교체입니다. 교체에는 삭제가 들어 있으므로 prevent_destroy 가 걸린 리소스에서는 계획 자체가 오류로 끝납니다. 오류 메시지에서 리소스 주소와 이유를 읽으세요.
블록을 지우면 보호도 함께 사라진다
/root/iac-plan 를 상태 파일까지 통째로 /root/iac-plan-bypass 로 복사하세요(cp -a). 사본의 main.tf 에서 local_file.motd 리소스 블록을 lifecycle 까지 통째로 지우고, 사본에서 tofu plan -out=/root/iac-plan-bypass/bypass.tfplan 을 저장합니다. 적용하지는 않습니다. 원본은 그대로 둡니다.
prevent_destroy 는 설정 안에 있는 동안만 효력이 있습니다. 블록을 지우면 그 설정도 사라지므로 계획은 오류 없이 삭제를 제안합니다. 이 차이가 코드 리뷰에서 무엇을 봐야 하는지를 알려 줍니다.
파괴가 섞인 계획은 사람에게 넘긴다
/root/iac-plan/review.sh <작업디렉터리> 를 만드세요. 그 디렉터리의 계획을 임시 파일로 저장해 summary.sh 로 세고, 변경이 없으면 CLEAN 과 0, 추가·수정만 있으면 AUTO 로 시작하는 한 줄과 2, 교체나 삭제가 하나라도 있으면 REVIEW 로 시작하는 한 줄과 3, 계획이 실패하면 ERROR 와 1 로 끝냅니다. 작업 디렉터리에 계획 파일을 남기지 않습니다. 채점기는 원본(CLEAN), 우회 사본(REVIEW), 그리고 스스로 만든 임시 디렉터리들로 확인합니다.
앞 단계의 gate.sh 와 summary.sh 를 이어 붙이는 문제입니다. 임시 파일은 mktemp 로 만들고 trap 으로 지웁니다. 계획 파일 없이 JSON 을 얻을 수는 없습니다 — -out 으로 저장한 뒤 show 합니다.