테라폼 운영 강좌 #1 협업 워크플로: PR에서 plan 리뷰, GitHub Actions로 apply
실전 강좌 열 편으로 인프라는 완성됐지만, 운영 방식은 아직 “각자 노트북에서 apply"입니다. 이 방식은 팀에서 오래 못 갑니다. 강한 AWS 권한이 개인 노트북마다 깔려 있어야 하고, 리뷰 없이 적용이 일어나고, 누가 무엇을 언제 apply 했는지 남지 않습니다. 운영 강좌 일곱 편의 첫 주제는 이 구조를 바꾸는 것입니다. 목표는 한 문장으로 요약됩니다. 사람은 코드와 plan을 리뷰하고, apply는 CI만 한다.
워크플로의 모양: PR에 plan, 병합에 apply #
Git 협업의 관문이 PR이므로, 테라폼의 관문도 PR에 맞춥니다.
브랜치에서 코드 수정 → PR 생성
→ CI: fmt 검사, validate, plan 실행
→ plan 결과가 PR 코멘트로 등록
→ 리뷰어가 코드와 plan을 함께 승인
main에 병합
→ CI: 병합된 코드로 apply핵심 관점의 전환은 리뷰 대상이 코드만이 아니라 plan이라는 것입니다. 코드 diff는 의도를 보여 주고, plan은 결과를 보여 줍니다. 기초 #2에서 -/+(교체) 기호를 반드시 확인하라고 했는데, 그 확인이 개인의 습관이 아니라 팀의 리뷰 절차가 되는 순간입니다.
자격 증명: OIDC 역할 재사용 #
CI가 AWS를 만지려면 자격 증명이 필요한데, 실전 #7에서 만들어 둔 GitHub OIDC 역할이 바로 이 용도를 위한 것이었습니다. 그 역할에 배포 권한(관리 대상 리소스와 state 버킷 접근)을 붙이고, 워크플로에서는 액세스 키 없이 역할을 맡습니다. 저장된 시크릿이 없으니 유출될 시크릿도 없습니다.
GitHub Actions 워크플로 #
두 파일로 구성합니다. 먼저 PR 쪽입니다.
# .github/workflows/terraform-plan.yml
name: terraform plan
on:
pull_request:
paths: ["envs/**", "modules/**"]
permissions:
id-token: write # OIDC 토큰 발급
contents: read
pull-requests: write # plan 코멘트 등록
jobs:
plan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ vars.TF_PLAN_ROLE_ARN }}
aws-region: ap-northeast-2
- uses: hashicorp/setup-terraform@v3
- run: terraform fmt -check -recursive
- run: terraform -chdir=envs/dev init
- run: terraform -chdir=envs/dev validate
- run: terraform -chdir=envs/dev plan -no-color -out=tfplan
# 이어서 plan 출력을 PR 코멘트로 등록하는 스텝plan 출력을 PR 코멘트로 올리는 부분은 공개 액션이나 gh pr comment를 쓰는 짧은 스크립트로 처리합니다. 다음은 병합 쪽입니다.
# .github/workflows/terraform-apply.yml
name: terraform apply
on:
push:
branches: [main]
paths: ["envs/**", "modules/**"]
permissions:
id-token: write
contents: read
concurrency:
group: terraform-dev
cancel-in-progress: false
jobs:
apply:
runs-on: ubuntu-latest
environment: dev
steps:
- uses: actions/checkout@v4
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ vars.TF_APPLY_ROLE_ARN }}
aws-region: ap-northeast-2
- uses: hashicorp/setup-terraform@v3
- run: terraform -chdir=envs/dev init
- run: terraform -chdir=envs/dev apply -auto-approve설정 하나하나가 운영 장치입니다.
- concurrency: 병합이 연달아 일어나도 apply가 한 번에 하나만 돕니다. 기초 #9의 state 잠금이 마지막 방어선이라면 이것은 첫 방어선이고,
cancel-in-progress: false로 진행 중인 apply를 중단하지 않습니다(apply 중단은 리소스를 어중간한 상태로 남깁니다). - environment: dev: GitHub 환경 기능에 승인자를 설정하면 apply 직전에 사람의 승인 단계를 넣을 수 있습니다. prod 워크플로에서 특히 유용합니다.
- paths 필터: 인프라 코드가 바뀔 때만 돌아 문서 수정 같은 커밋에 CI를 낭비하지 않습니다.
- 역할 분리: plan용 역할은 읽기 위주, apply용 역할만 쓰기 권한을 갖게 나누면 PR 단계의 권한이 최소화됩니다.
신뢰 경계가 이렇게 바뀝니다 #
이 워크플로가 자리 잡으면 권한 구조가 달라집니다. 사람에게는 읽기와 plan 권한만 남기고, 쓰기 권한은 CI의 역할에만 있습니다. 긴급 상황에 로컬 apply가 필요하면 할 수는 있지만 그것은 예외 절차이고, 예외가 일어났다는 사실 자체가 감사 기록에 남습니다. “누가 언제 무엇을 적용했나"의 답이 Git 이력과 Actions 로그로 완결되는 구조입니다.
prod까지 확장하면 구조는 같고 값만 다릅니다. envs/prod용 워크플로에 prod 역할과 승인 필수의 environment를 걸면, 실전 #9에서 만든 “dev 먼저, 검증 후 prod” 흐름이 CI 위에서 굴러갑니다.
정리 #
이번 글에서 다룬 내용입니다.
- 로컬 apply는 권한 집중, 리뷰 부재, 기록 부재의 문제를 갖습니다. 원칙은 “사람은 리뷰, apply는 CI"입니다
- PR에는 fmt·validate·plan이 돌고 plan 출력이 코멘트로 남아, 코드와 결과를 함께 리뷰합니다
- CI 자격 증명은 실전 #7의 OIDC 역할입니다. plan용과 apply용 역할을 나눠 PR 단계 권한을 최소화합니다
- concurrency로 동시 apply를 막고, environment 승인으로 prod 앞에 사람의 관문을 둡니다
- 예외적 로컬 apply는 가능하되 기록에 남는 예외가 됩니다
다음 글(#2 drift와 state 수술)에서는 이 워크플로 밖에서 일어나는 변경을 다룹니다. 콘솔 수동 변경의 감지, import와 removed 블록으로 하는 state 편입과 제외입니다.