Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .coderabbit.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
language: ko-KR

reviews:
auto_review:
enabled: false

path_instructions:
- path: "**/*.tf"
instructions: |
이 PR의 Terraform 코드 변경을 리뷰합니다.
PR 댓글에 올라온 각 환경의 "Terraform Plan" 결과를 반드시 확인하고, 코드 변경과 plan 결과가 일치하는지 검토하세요.

다음 항목을 중점적으로 검토하세요:
- plan에 예상치 못한 resource destroy 또는 replace가 포함된 경우
- 보안 그룹(Security Group) 인바운드/아웃바운드 규칙 변경
- IAM 권한이 과도하게 부여된 경우 (최소 권한 원칙)
- 민감한 값(비밀번호, 키 등)이 코드에 하드코딩된 경우
- `lifecycle.ignore_changes` 설정이 의도에 맞게 사용되었는지
259 changes: 259 additions & 0 deletions .github/workflows/terraform-apply.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
name: Terraform Apply

on:
push:
branches: [main]

permissions:
id-token: write
contents: read

env:
TF_VERSION: "1.10.5"
SSM_TUNNEL_TIMEOUT: "60"

jobs:
detect-changes:
runs-on: ubuntu-latest
outputs:
bootstrap: ${{ steps.filter.outputs.bootstrap }}
global: ${{ steps.filter.outputs.global }}
prod: ${{ steps.filter.outputs.prod }}
stage: ${{ steps.filter.outputs.stage }}
monitoring: ${{ steps.filter.outputs.monitoring }}
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v3
id: filter
with:
filters: |
bootstrap:
- 'bootstrap/**'
global:
- 'environment/global/**'
- 'modules/shared_resources/**'
prod:
- 'environment/prod/**'
- 'modules/app_stack/**'
- 'modules/common/**'
stage:
- 'environment/stage/**'
- 'modules/app_stack/**'
- 'modules/common/**'
monitoring:
- 'environment/monitoring/**'
- 'modules/monitoring_stack/**'
- 'modules/common/**'

apply-bootstrap:
needs: detect-changes
if: needs.detect-changes.outputs.bootstrap == 'true'
runs-on: ubuntu-latest
Comment thread
coderabbitai[bot] marked this conversation as resolved.
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
token: ${{ secrets.GH_PAT }}
persist-credentials: false
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: ap-northeast-2
- uses: hashicorp/setup-terraform@v3
with:
terraform_version: ${{ env.TF_VERSION }}
terraform_wrapper: false
- name: Terraform Init
working-directory: bootstrap
run: terraform init
- name: Terraform Apply
working-directory: bootstrap
run: terraform apply -auto-approve

apply-global:
needs: [detect-changes, apply-bootstrap]
if: |
always() &&
needs.detect-changes.outputs.global == 'true' &&
(needs.apply-bootstrap.result == 'success' || needs.apply-bootstrap.result == 'skipped')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
token: ${{ secrets.GH_PAT }}
persist-credentials: false
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: ap-northeast-2
- uses: hashicorp/setup-terraform@v3
with:
terraform_version: ${{ env.TF_VERSION }}
terraform_wrapper: false
- name: Terraform Init
working-directory: environment/global
run: terraform init
- name: Terraform Apply
working-directory: environment/global
run: |
terraform apply -auto-approve \
-var-file="../../config/secrets/shared_resources.tfvars"

apply-prod:
needs: [detect-changes, apply-bootstrap]
if: |
always() &&
needs.detect-changes.outputs.prod == 'true' &&
(needs.apply-bootstrap.result == 'success' || needs.apply-bootstrap.result == 'skipped')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
token: ${{ secrets.GH_PAT }}
persist-credentials: false
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: ap-northeast-2
- uses: hashicorp/setup-terraform@v3
with:
terraform_version: ${{ env.TF_VERSION }}
terraform_wrapper: false
- name: Install Session Manager Plugin
run: |
curl -sL "https://s3.amazonaws.com/session-manager-downloads/plugin/latest/ubuntu_64bit/session-manager-plugin.deb" \
-o /tmp/session-manager-plugin.deb
sudo dpkg -i /tmp/session-manager-plugin.deb
echo "/usr/local/sessionmanagerplugin/bin" >> $GITHUB_PATH
/usr/local/sessionmanagerplugin/bin/session-manager-plugin --version
- name: Start SSM Tunnel to RDS
run: |
echo "=== session-manager-plugin 진단 ==="
which session-manager-plugin || echo "NOT IN PATH"
session-manager-plugin --version || echo "VERSION CHECK FAILED"
echo "===================================="

EC2_ID=$(aws ec2 describe-instances \
--filters "Name=tag:Name,Values=solid-connection-server-prod" "Name=instance-state-name,Values=running" \
--query 'Reservations[0].Instances[0].InstanceId' \
--output text)

RDS_HOST=$(aws rds describe-db-instances \
--query 'DBInstances[?contains(DBInstanceIdentifier, `prod`)].Endpoint.Address | [0]' \
--output text)

if [ -z "$EC2_ID" ] || [ "$EC2_ID" = "None" ]; then
echo "::error::prod EC2 인스턴스를 찾을 수 없습니다"
exit 1
fi
if [ -z "$RDS_HOST" ] || [ "$RDS_HOST" = "None" ]; then
echo "::error::prod RDS 엔드포인트를 찾을 수 없습니다"
exit 1
fi

echo "Tunneling via $EC2_ID -> $RDS_HOST:3306"

aws ssm start-session \
--target "$EC2_ID" \
--document-name AWS-StartPortForwardingSessionToRemoteHost \
--parameters "{\"host\":[\"$RDS_HOST\"],\"portNumber\":[\"3306\"],\"localPortNumber\":[\"3306\"]}" &
SSM_PID=$!
echo "SSM_PID=$SSM_PID" >> $GITHUB_ENV

for i in $(seq 1 $SSM_TUNNEL_TIMEOUT); do
if ! kill -0 $SSM_PID 2>/dev/null; then
echo "::error::SSM 세션이 터널 준비 전에 종료되었습니다"
exit 1
fi
if nc -z 127.0.0.1 3306 2>/dev/null; then
echo "SSM tunnel ready (${i}s)"
break
fi
sleep 1
done

if ! nc -z 127.0.0.1 3306 2>/dev/null; then
echo "::error::${SSM_TUNNEL_TIMEOUT}초 내에 터널이 준비되지 않았습니다"
kill $SSM_PID 2>/dev/null || true
exit 1
fi
if ! kill -0 $SSM_PID 2>/dev/null; then
echo "::error::포트는 열렸으나 SSM 세션이 이미 종료되었습니다"
exit 1
fi
- name: Terraform Init
working-directory: environment/prod
run: terraform init
- name: Terraform Apply
working-directory: environment/prod
run: |
terraform apply -auto-approve \
-var-file="../../config/secrets/prod.tfvars" \
-var-file="../../config/secrets/app_stack.tfvars"
- name: Stop SSM Tunnel
if: always()
run: kill $SSM_PID 2>/dev/null || true

apply-stage:
needs: [detect-changes, apply-bootstrap]
if: |
always() &&
needs.detect-changes.outputs.stage == 'true' &&
(needs.apply-bootstrap.result == 'success' || needs.apply-bootstrap.result == 'skipped')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
token: ${{ secrets.GH_PAT }}
persist-credentials: false
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: ap-northeast-2
- uses: hashicorp/setup-terraform@v3
with:
terraform_version: ${{ env.TF_VERSION }}
terraform_wrapper: false
- name: Terraform Init
working-directory: environment/stage
run: terraform init
- name: Terraform Apply
working-directory: environment/stage
run: |
terraform apply -auto-approve \
-var-file="../../config/secrets/stage.tfvars" \
-var-file="../../config/secrets/app_stack.tfvars"

apply-monitoring:
needs: [detect-changes, apply-bootstrap]
if: |
always() &&
needs.detect-changes.outputs.monitoring == 'true' &&
(needs.apply-bootstrap.result == 'success' || needs.apply-bootstrap.result == 'skipped')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
token: ${{ secrets.GH_PAT }}
persist-credentials: false
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: ap-northeast-2
- uses: hashicorp/setup-terraform@v3
with:
terraform_version: ${{ env.TF_VERSION }}
terraform_wrapper: false
- name: Terraform Init
working-directory: environment/monitoring
run: terraform init
- name: Terraform Apply
working-directory: environment/monitoring
run: |
terraform apply -auto-approve \
-var-file="../../config/secrets/monitoring.tfvars" \
-var-file="../../config/secrets/monitoring_stack.tfvars"
Loading
Loading