0. Contents
1. Overview
VPC·Aurora 와 Terraform 의 기존 경계
- kukie-dev 의 VPC 와 Aurora 는 Terraform 보다 먼저 있었다
- 2026-09-06 CloudFormation 스택 하나로 만들어졌다
- 그 위에 Terraform 으로 EKS 와 NAT 를 얹었다(03편)
- 그동안은 “CloudFormation 이 만든 것은 Terraform 이 건드리지 않는다” 는 규칙으로 둘을 나눠 왔다
계기: DB 보안 그룹의 5432 규칙 한 줄
- 앱 배포를 앞두고 이 경계가 처음으로 걸렸다
- 클러스터의 Pod 가 Aurora 에 붙으려면 DB 보안 그룹에 5432 규칙 한 줄이 필요했다
- 그 보안 그룹의 주인은 CloudFormation 이었다
- 한 줄을 CloudFormation 으로 더할 수도 있었지만, 배포 전에 스택 전체를 Terraform 으로 옮기기로 했다
이번 편: 전환의 기록
- 리소스 열넷의 분할
- 옮기는 일을 할 역할을 좁힌 방법
- 적용 전에 리뷰가 찾아낸 IAM 의 빈틈들
- 스택을 지우고 Terraform 으로 가져온 적용 과정(7장)
2. Background — 두 도구가 섞인 경위
CloudFormation 스택
kukie-dev-db 생성. VPC, public subnet 2개, IGW, 라우팅, Aurora Serverless v2, DB 보안 그룹. 팀원 PC 에서 개발용으로 쓰려고 만든 것이라 보안 그룹은 공인 IP 만 허용Terraform 도입. state, OIDC, 배포 역할
같은 VPC 안에 EKS, private subnet, NAT 를 Terraform 으로 추가
앱이 DB 에 붙어야 할 시점. 스택 전환 결정
옮기기로 한 이유 세 가지
- 스택 템플릿 원본이 조직의 어느 저장소에도 없었다
- DB 보안 그룹이 템플릿과 달랐다(콘솔에서 추가된 허용 규칙 하나)
- 그 변경들이 PR, plan,
/apply흐름 밖에서 일어났다 - 문제는 CloudFormation 이 나쁜 도구라는 점이 아니었다
- 나머지가 모두 Terraform 인데 하나만 다른 도구라는 점이었다
3. Import Plan — 리소스 열넷의 분할
3.1 Two Roots — network 와 database
root 단위의 분할
- 스택의 리소스 열넷을 Terraform root 둘로 나눴다
- root 는 코드 폴더 하나, state 파일 하나, 배포 역할 하나가 한 묶음이다
- 나눈다는 건 이 셋을 같이 나눈다는 뜻이다
dev/networkVPC, public subnet 2, IGW 와 연결, public 라우트 테이블, 기본 경로, subnet 연결 2
EKS 와 Aurora 가 모두 그 위에 있다. 바뀌는 일이 거의 없다
dev/databaseAurora 클러스터, writer, subnet group, 파라미터 그룹, 보안 그룹
데이터가 있는 곳이라 다른 root 와 state, 역할을 나눈다
eks-platform 과도 나눈 이유
- eks-platform 에 넣지 않은 것도 같은 이유다
- EKS 는 노드 교체와 버전 업그레이드로 자주 바뀐다
- 한 state 에 두면
- EKS 작업의 plan 에 DB 가 함께 걸린다
- 넓은 eks-platform 역할의 실수가 DB 까지 번진다
3.2 Import Block — 실물을 건드리지 않는 가져오기
import 블록의 동작
- Terraform 1.5 부터
import블록으로 이미 있는 리소스를 코드로 가져온다 - plan 에서 “가져오기” 로 표시된다
- 코드가 실물과 같으면 바꾸는 것이 없다
import {
to = aws_vpc.this
id = "<VPC ID>"
}
import 가 필요한 리소스
- Terraform 이 직접 만든 리소스: 만들 때 AWS 가 돌려준 ID 를 바로 장부에 적는다 → import 가 필요 없다
- CloudFormation 이 만든 리소스: Terraform 이 ID 를 모른다 → 사람이 import 로 알려 준다
- 다른 블록으로는 ID 를 알려 줄 수 없다
resource블록에는 ID 를 적는 칸이 없다- ID 로 쓰는
data블록은 읽기만 할 뿐 관리하지 않는다
위험: 코드와 실물의 차이
- 가져오기의 위험은 코드와 실물이 다를 때다
- 예: CIDR 이 한 글자라도 다르면 가져오기가 아니라 교체(삭제 후 생성) 가 된다
- 이 VPC 안에 클러스터가 있다 → 교체는 곧 재구성이다
- 대비
- VPC, subnet, IGW, Aurora 에
prevent_destroy를 건다 - 모의 테스트가 CIDR 을 CloudFormation 값과 비교한다
- VPC, subnet, IGW, Aurora 에
3.3 Local Plan — 적용 전 실측
가져오기 · 변경 · 교체의 차이
- 가져오기: 실물을 state 에 연결만 하고 AWS 는 바꾸지 않는다
- 변경: 연결한 뒤 코드와 다른 부분(대부분 태그)을 실물에 그 자리에서 맞추는 일이다
- 교체: 지우고 다시 만드는 일로, 변경과는 다르다
로컬 state 로 돌린 plan
- 코드를 레포에 넣기 전에 관리자 자격증명으로 로컬 state 를 써서 plan 만 돌렸다
- AWS 는 바꾸지 않는다
plan 결과
- network: 가져오기 8 · 생성 0 · 변경 5 · 삭제 0
- database: 가져오기 5 · 생성 3 · 변경 5 · 삭제 0
- 변경은 대부분 Terraform 의 기본 태그
ManagedBy를 붙이는 일이었다
실물과 값이 달랐던 두 곳
- writer 인스턴스의
promotion_tier: 실물 1, 코드 기본값 0. 코드를 1로 맞췄다 - 파라미터 그룹의
rds.force_ssl = 1: CloudFormation 템플릿에 적혀 있었지만 PostgreSQL 17 의 기본값과 같아 사용자 값으로 기록되지 않았다. 실제 값은 이미 1이라 바뀌는 것은 없다
database 의 생성 3: 새 보안 그룹 규칙
- 관리자 접속 규칙 둘
- 클러스터 → DB 5432 규칙 하나
- 옛 관리자 규칙 둘은 가져오지 않았다
- Terraform 주소의 키가 IP 가 된다 → 가져오려면 코드에 공인 IP 를 적어야 한다
- 스택 정리 단계에서 회수하고 같은 목록을 새로 만든다
Aurora 의 manage_master_user_password
- import 는 이 값을 다시 읽어 오지 못한다
- 그래서 첫 plan 이 “비밀번호 관리를 RDS 에 맡겨라” 는 변경을 다시 보내려 했다
- 이미 그렇게 되어 있지만 비밀번호에 닿는 호출이다 →
ignore_changes로 아예 보내지 않게 했다
4. Apply Roles — 지우지 못하는 배포 역할
배포 흐름: GitHub Actions 와 OIDC
- 이 레포에서 Terraform 은 사람이 아니라 GitHub Actions 가 돌린다
- PR 에
/apply댓글을 달면 그 root 의 배포 역할이 OIDC 로 임시 키를 받는다(01편, 02편) - root 를 추가하려면 그 역할부터 관리자가 만들어야 한다
가져온 VPC, subnet, IGW, 라우트 테이블의 태그, 속성, public 경로, IGW 연결
삭제와 IGW 분리(
KeepNetworkCore), subnet 연결 변경Aurora 네 리소스의 수정과 태그, DB 보안 그룹 규칙
삭제 다섯 가지(
KeepDatabase), RDS 관리 비밀번호 해제(KeepManagedMasterPassword), Secret 값 읽기EKS, 노드, NAT. EC2 는
ec2:*가져온 여섯 리소스(
KeepAdoptedResources), subnet 연결 교체와 해제(KeepSubnetAssociations)두 역할의 공통 제한
- 두 역할 모두 main 브랜치에서만 쓸 수 있다
- 코드의
prevent_destroy와 RDS 의 삭제 방지가 이미 있다 - 그래도 역할 자체에도 삭제를 막아 두었다 → 코드를 고쳐서 우회할 수 없게 하려는 것이다
5. IAM Pitfalls — 리뷰가 찾은 빈틈
리뷰 여덟 라운드
- 권한 PR 은 여덟 번 리뷰를 돌았다
- Claude 와 Codex 가 번갈아 지적했고, 모두 고친 뒤 마지막 라운드는 “필수 수정 없음” 이었다
- 지적 수: 심각도 P1 넷, P2 넷, 코드 지적 하나, 문서 지적 셋
- P1 넷과
Null조건 지적은 그대로 적용했다면 실제로 뚫리거나 멈췄을 것이었다
5.1 Route Table Association — 라우트 테이블만 보는 권한 확인
노드 subnet 의 길이 NAT 대신 IGW 로
조치 · network 역할에서 연결 권한 제거
public subnet 의 NAT 가 인터넷을 잃음
조치 · eks-platform 역할 교체 · 해제 전역 거부
표지판 · 명찰 · 경비원으로 본 연결 검사
- 표지판 두 개 — 같은 종류, 다른 내용 — public 과 private 라우트 테이블은 같은 리소스(
aws_route_table)다. 다른 것은 적힌 길과 주인이다- public:
0.0.0.0/0 → IGW. public subnet(NAT, ALB)에 붙어 있고, CloudFormation 이 만든 것을 network root 가 가져왔다 - private:
0.0.0.0/0 → NAT. EKS 노드가 있는 private subnet 에 붙어 있고, eks-platform root 가 만들었다
- public:
- 명찰 두 개 — network 역할(
kukie-terraform-network-apply)과 eks-platform 역할(kukie-terraform-infra-apply, ROOTS 키가infra라 이름이 다르다). 둘 다 GitHub Actions 가 OIDC 로 빌려/apply때 단다 - 경비원 — AWS IAM — 검사는 요청을 받는 AWS 쪽에서 한다. 규칙의 내용은 우리가 foundation root 의 정책으로 쓰고, 어떤 요청에서 무엇을 대조할지는 AWS 가 정한다
- 경비원이 보는 것 — 표지판 주소만 — subnet 연결 동작 셋(붙이기
AssociateRouteTable, 바꿔 달기ReplaceRouteTableAssociation, 떼기DisassociateRouteTable)에서 IAM 은 라우트 테이블 ARN 으로 명찰마다 허용과 거부를 가른다. 요청에 들어 있는 subnet ARN 은 대조하지 않는다
- 구멍 ① network 역할 — public 라우트 테이블에 붙이기 권한이 있었다. 노드 subnet 에 public 표지판을 붙이면 노드는 NAT 를 건너뛰고 IGW 로 바로 나가려 한다. 노드에는 공인 IP 가 없어 바깥으로 나가지 못한다
- 구멍 ② eks-platform 역할 —
ec2:*라 자기 private 라우트 테이블로 public subnet 의 연결을 바꿔 달거나 뗄 수 있었다. public subnet ARN 에 걸어 둔 거부는 대조되지 않는다. public subnet 이 IGW 길을 잃으면 그 안의 NAT 도 인터넷을 잃고, 결국 노드도 나가지 못한다
- 조치 — 두 역할 모두 연결을 바꿀 수 없게 했다
권한 제거 (두 연결은 가져온 뒤 바꿀 일이 없다)
유지. public subnet 에는 이미 연결이 있어 AWS 가 거부한다
권한 제거
대상과 상관없이 거부 (
KeepSubnetAssociations)5.2 ForAllValues — 키가 없을 때 참인 조건
태그 키 조건의 함정과 조치
- 막으려던 것 — database 역할에 DB 보안 그룹 규칙의 태그를 달고 떼는 권한을 줬다. 아무 태그나 건드리지 못하게, 태그 키를
Project,Environment,ManagedBy셋으로 묶었다 ForAllValues:StringEquals의 뜻 — “요청에 들어 있는 태그 키가 전부 목록 안에 있어야 한다”- 함정 — 키 0개는 참 — 키가 하나도 없으면 목록 밖의 키도 없다. 그래서 “전부 목록 안” 이 참이 된다. 손님이 0명이면 “전원 명단에 있음” 이 되는 것과 같다
- 0개가 위험한 이유 — EC2
DeleteTags는 키를 적지 않으면 그 리소스의 태그를 전부 지운다. 키 없는DeleteTags가 조건을 통과해, 규칙의 태그를 통째로 지울 수 있었다 - 조치 — 키가 꼭 있어야 한다 —
Null조건을 함께 걸었다."aws:TagKeys": "false"는 “태그 키가 비어 있지 않아야 한다” 는 뜻이다. AWS 문서도 이 조합을 권한다 - 이후 — 5.4 에서 규칙 태그를 한 번 더 좁혔다. 규칙을 만들 때만 태그를 허용하고, 규칙의
DeleteTags는 아예 뺐다.Null조건은 그대로 남았다
"Condition": {
"ForAllValues:StringEquals": { "aws:TagKeys": ["Project", "Environment", "ManagedBy"] },
"Null": { "aws:TagKeys": "false" }
}
5.3 Managed Master Password — 수정 권한으로 내려놓는 비밀번호
비밀번호 우회로와 조치
- 관리형 비밀번호 — Aurora master 비밀번호는 RDS 가 만들어 Secrets Manager 에 넣고 7일마다 바꾼다. 사람은 값을 모른다
- 막아 둔 것 — database 역할은 Secret 값을 읽지 못한다
- 우회로 —
rds:ModifyDBCluster에는 “RDS 관리를 끄고 새 비밀번호를 지정” 하는 옵션이 있다. 이 요청이 통과하면 관리 Secret 이 지워지고, 비밀번호는 요청을 보낸 쪽이 정한 값이 된다. Secret 을 읽지 않고도 비밀번호를 손에 넣는 길이다 - 조치 —
rds:ManageMasterUserPassword = false인ModifyDBCluster만 골라 거부했다(KeepManagedMasterPassword). AWS 문서도 생성 시점에 관리형을 강제하는 것만으로는 부족하니 수정도 막으라고 한다 - 정상 적용은 그대로 — database root 는 이 값을
ignore_changes로 두어 보내지 않는다. 용량이나 파라미터 같은 다른 수정은 그대로 허용된다
{
"Sid": "KeepManagedMasterPassword", "Effect": "Deny", "Action": "rds:ModifyDBCluster", "Resource": "*",
"Condition": { "Bool": { "rds:ManageMasterUserPassword": "false" } }
}
5.4 Security Group Rule Tags — 그룹을 물을 수 없는 리소스
규칙 태그 조건: 소속에서 시점으로
- 규칙 줄도 리소스 — 보안 그룹의 규칙 한 줄 한 줄이 따로 ARN 을 가진 리소스다. Terraform 은 기본 태그(
default_tags) 때문에 규칙을 만들 때 태그도 붙인다. 태그는 동작에 영향 없는 이름표지만, 붙이는 일(CreateTags)에는 권한이 필요하다 - 처음 쓴 조건 — 소속으로 묶기 —
ec2:SecurityGroupID로 “Aurora 보안 그룹의 규칙에만” 태그를 허용했다. 다른 방화벽 규칙의 태그는 건드리지 못하게 하려는 것이다 - 문제 — 소속을 물을 수 없다 — 규칙 리소스는 이 조건 키를 제공하지 않는다. 키가 없으면
StringEquals는 거짓이라 정상 태그까지 거부되고, 첫/apply가 태그 단계에서 멈췄을 것이다. 5.1~5.3 과 반대로, 너무 좁아서 생긴 문제다 - 조치 — 시점으로 묶기 — 규칙을 만드는 요청 안에서 함께 붙는 태그만 허용했다(
ec2:CreateAction이AuthorizeSecurityGroupIngress또는Egress). 규칙 만들기는 보안 그룹 권한을 따로 검사하고, 그 권한은 Aurora 보안 그룹 하나뿐이다. 그래서 태그도 자연히 Aurora 쪽 새 규칙에만 붙는다 - 여파 — 아웃바운드 규칙은 관리 제외 — 이미 있는 규칙에 따로 태그를 붙이는 요청은 거부된다. CloudFormation 이 만든 아웃바운드 규칙을 가져오면 첫 적용이 거기에 기본 태그를 붙이려다 막힌다.
ignore_changes로도 provider 기본 태그 차이는 숨겨지지 않았고(실제 plan 으로 확인), 기본 “모두 허용” 규칙이라 관리할 값도 없어 Terraform 관리에서 뺐다
5.5 그 밖의 지적
리뷰의 나머지 지적
- 가져온 리소스 보호에
ec2:Update*가 빠져 규칙 설명 수정이 통과했다(DeleteTags는ec2:Delete*에 이미 포함) - 규칙 수정(
ModifySecurityGroupRules)은 보안 그룹과 규칙 리소스를 둘 다 확인해, 규칙 리소스에도 허용이 필요했다 - 파라미터를 지우면 provider 가
ResetDBClusterParameterGroup을 따로 부른다 - 문서 지적 넷: 역할 표, state 키 목록, plan 개수, 상설 참조 절
6. Guard by ARN — 태그 대신 주소
6.1 Tag and ARN — 스택 삭제 전후
원래 규칙: 태그 기준 거부
- eks-platform 역할이 CloudFormation 리소스를 건드리지 못하게 한 원래 규칙은 태그 기준이었다
- CloudFormation 이 붙이는
aws:cloudformation:stack-name태그가 있으면 거부한다 aws:로 시작하는 태그는 사람이 지울 수 없다 → 믿을 만했다
스택 삭제 뒤의 불확실성
- 전환하면 이 스택을 지운다
- 스택을 지운 뒤에도 리소스에 그 태그가 남는지는 확실하지 않았다
- 태그가 사라지면
ec2:*를 가진 eks-platform 역할이 VPC 와 subnet 을 바꿀 수 있게 된다
보완: ARN 으로 한 번 더
- 같은 동작을 리소스 ARN 으로 한 번 더 막았다(
KeepAdoptedResources) - 주소는 스택이 사라져도 그대로다
6.2 Inline Policy Limit — 10,240자
한도 초과: inline 정책 합계
- 연결 거부 규칙까지 더하자 eks-platform 역할의 inline 정책이 한도를 넘었다
- IAM 은 역할 하나의 inline 정책 합계를 공백 제외 10,240자로 제한한다
정리: 겹친 규칙 빼기
- 정책을 다시 보니 같은 리소스를 두 번 막고 있었다
- 태그 규칙이 막는 EC2 리소스(VPC, subnet, IGW, 라우트 테이블, DB 보안 그룹)는 ARN 규칙이 전부 덮는다
- ARN 규칙은 동작도 하나 더 많다
- 역할에서는 태그 규칙을 뺐다
- 크기 여유가 있는 경계(permissions boundary)에는 둘 다 남겼다
6.3 Mutation Tests — 실패해야 할 때 실패하는 테스트
통과만으로 부족한 테스트
- 정책 테스트는 Terraform 모의 테스트로 정책 JSON 을 검사한다
- 테스트가 통과하는 것만으로는 부족하다 → 검사가 느슨하면 잘못된 정책도 통과한다
- 고친 곳마다 정책을 일부러 되돌리거나 넓힌 변이를 만들어 테스트가 실패하는지 확인했다
eks-platform 의 ARN 보호 제거,
ec2:DeleteVpc 허용, 라우트 테이블 삭제 거부 제거rds:DeleteDBCluster 허용, Secret 값 읽기 추가, ARN 보호에서 DB 보안 그룹 제외연결 동작 복원,
Null 조건 제거, 비밀번호 거부 조건 뒤집기, 규칙 태그에 DeleteTags 허용결과: 변이 스물하나 모두 실패
- 스물하나 모두 실패했다
- 하나라도 통과했다면 그 검사는 아무것도 지키지 않는 것이다
7. Stack Deletion — 적용 기록
7.1 Foundation — 명찰 먼저
foundation 적용 결과
- #51 을 병합한 뒤 관리자 자격증명으로 foundation 을 적용했다
- plan: 생성 4 · 변경 3 · 삭제 0 — 예상과 같았다
- 적용 뒤 다시 돌린 plan:
No changes - network · database 명찰이 이때 생겼다
7.2 Retain Update — 남기고 떠나라는 통보
Retain 업데이트 단계
- 템플릿 읽기 — 스택의 원래 템플릿을 조회해 14개 모두에
DeletionPolicy·UpdateReplacePolicy: Retain을 붙였다. Aurora 클러스터만 원래Snapshot이었다. DB 보안 그룹의 템플릿 인바운드 규칙은 비웠다. 수정본에는 관리자 IP 가 남지 않는다 - 변경 세트 먼저 — 바로 업데이트하지 않고 변경 세트를 만들어 읽었다. 14개 모두
Modify, 교체 없음, 추가 · 삭제 없음, 속성이 바뀌는 것은 DB 보안 그룹 하나였다 - 실행 — 이 단계는 세션의 자동 권한 검사가 공유 리소스 변경으로 막았다. 명령을 터미널에서 직접 실행했다.
UPDATE_COMPLETE뒤 템플릿을 다시 읽어 14개 모두 Retain 인 것을 확인했다 - 남은 규칙 하나 — 옛 관리자 규칙 둘 중 템플릿에 있던 하나만 회수됐다. 나머지 하나는 콘솔에서 따로 추가된 규칙이라 CloudFormation 이 몰랐다. 규칙 ID 로 직접 회수해 인바운드를 0 으로 만들었다. 남겨 두면 database 적용 때 같은 IP 의 새 규칙이 중복으로 거부될 수 있었다
7.3 Stack Deletion — 장부만 지우기
스택 삭제 뒤 남은 리소스
- 스택을 지웠다
- 모두 남았다: VPC, subnet, IGW, 라우팅, Aurora 클러스터와 writer, subnet group, 파라미터 그룹, DB 보안 그룹
- Aurora 는
available, 삭제 방지도 그대로였다
6.1 절 질문의 답: 태그는 남았다
- 스택을 지운 뒤에도
aws:cloudformation:stack-name·stack-id·logical-id태그는 리소스에 남아 있었다 - 그래도 사람이 지울 수 없는 이 태그가 언제까지 남는지는 보장되지 않는다
- 그래서 보호를 ARN 에 둔 판단은 그대로다
관리 공백
- 이때부터 14개는 어느 도구도 관리하지 않는다
7.4 Network Apply — 첫 가져오기
#50 plan 댓글: 빠진 가져오기 수
- 첫 PR plan 댓글은 생성 0 · 변경 5 · 삭제 0 이었다 → 가져오기 8 이 보이지 않았다
- 원인: 배포 스크립트가
change.actions만 세고no-op을 건너뛰었다- import 는
change.importing에 따로 표시된다
- import 는
- 리뷰가 이것을 짚었다
- 수정: 스크립트가 가져오기 수를 따로 세고 목록에
가져오기작업을 남기도록 고쳤다 - 다시 돌린 plan 댓글: 가져오기 8 · 생성 0 · 변경 5 · 삭제 0
network 적용 결과
- 병합 뒤 main 의 최종 plan 도 같은 숫자였다
/apply결과도 가져오기 8 · 변경 5 였다- S3 에
dev/network.tfstate가 생겼다 - VPC 에
ManagedBy = terraform태그가 붙었다 - 네트워크는 이제 Terraform 소유다
7.5 Database Apply — 클러스터에서 DB 로
#53 최종 plan
- 가져오기 5 · 생성 3 · 변경 5 · 삭제 0
- 생성 셋: 관리자 접속 규칙 둘과 클러스터 → DB 5432 규칙 하나
- 관리자 규칙은 주소에 IP 가 들어가 댓글에서 이름이 숨겨진다
병합 전 리뷰의 추가 지적 두 가지
prevent_destroy빈자리 — 가져온 다섯 중 subnet group 과 파라미터 그룹만 빠져 있었다. 둘 다 이름이나 설명을 바꾸면 교체되는 리소스라, 교체 plan 이 apply 도중KeepDatabase거부로 멈추는 대신 plan 단계에서 멈추도록 같은 보호를 붙였다- 엔진 버전과 자동 업그레이드 — 클러스터는
17.10으로 적었는데 writer 는 마이너 자동 업그레이드가 켜져 있다. AWS 가 17.11 로 올리면 다음 plan 이 다운그레이드 요청이 된다.engine_version을ignore_changes에 넣었다
database 적용 결과
/apply결과도 가져오기 5 · 생성 3 · 변경 5 · 삭제 0 이었다- S3 에
dev/database.tfstate가 생겼다 - Aurora 는
available, 삭제 방지와 엔진 버전도 그대로였다 - 보안 그룹 인바운드는 세 줄, 모두
ManagedBy = terraform이다
7.6 Verification — 숫자 말고 연결
연결 확인 — 적용 숫자가 맞아도 실제로 도는지는 따로 확인했다
- 관리자 PC → DB 5432 — 연결된다. 새 관리자 규칙과
KUKIE_ADMIN_CIDRS의 IP 가 맞다는 뜻이다 - 클러스터 Pod → DB 5432 — 임시 Pod 하나를 띄워 TCP 연결만 시도하고 지웠다. 연결된다. 이 한 줄이 전환을 시작한 이유였다
- 노드 → NAT → 인터넷 — 같은 임시 Pod 가 공개 이미지를 받아 왔으니 함께 확인됐다
- 후속 plan —
imports.tf를 지우는 정리 PR 의 plan 이 network · database 모두 생성 0 · 변경 0 · 삭제 0 이었다. 코드와 실물이 같다
전환 완료와 남은 일
- 이제 스택에 있던 열넷은 모두 Terraform 소유다
- 남은 것은 앱 배포다
- member 전용 DB 계정
- Secrets Manager 값
- ArgoCD 의 첫 동기화
8. Discussion
리뷰가 찾은 구멍의 공통 모양
- 리뷰가 찾은 구멍은 대부분 같은 모양이었다
- 정책은 “이 리소스만” 이라고 적었는데
- AWS 가 그 동작에서 그 리소스를 확인하지 않거나(라우트 테이블 연결, 규칙 태그)
- 조건이 키가 없을 때 참이거나(
ForAllValues) - 다른 동작이 같은 결과를 내는(
ModifyDBCluster로 비밀번호 관리 해제) 경우다
- 이번 PR 이 남긴 것: 정책에 적은 대상과 AWS 가 실제로 평가하는 대상이 다를 수 있다
순서: 코드 리뷰와 실측 plan
- 리뷰 지적 중 둘은 적용했다면 첫
/apply가 권한 거부로 멈췄을 것이었다 - 하나는 로컬 plan 을 다시 돌려서야 보였다(아웃바운드 규칙 태그)
- 코드 리뷰와 실측 plan 이 서로 다른 것을 잡았다
핵심 메시지
도구가 먼저 생긴 순서대로 두 개가 남아 있었고, 클러스터가 DB 에 붙어야 할 때 처음 맞닿았다. 옮기는 역할은 지우지 못하게, 기존 역할은 옮긴 것을 건드리지 못하게 만들었다. 스택은 사라지고 리소스와 태그는 남았다 — 장부를 옮겨 적은 뒤 클러스터에서 DB 까지 연결로 확인했다.
9. References
"It also returns true if there are no context keys in the request."
"However, you can still disable Secrets Manager integration and manually set a master password by modifying the instance."
"Role policy size can't exceed 10,240 characters."
"requires an import block that specifies the unique infrastructure resource ID to import"