Skip to main content

Deployment Workflow

Workflow patterns สำหรับ deploy Terraform อย่างปลอดภัย — ใน team ใหญ่/หลาย environment

Workflow Components

Standard Workflow

1. Local Development

# Branch
git checkout -b feature/add-monitoring

# Edit
vim main.tf

# Format + Validate
terraform fmt
terraform validate

# Plan locally
terraform plan

2. Open PR

git add .
git commit -m "feat: add CloudWatch monitoring"
git push origin feature/add-monitoring
gh pr create

3. CI Runs

  • ✅ Format check
  • ✅ Validate
  • ✅ Lint (TFLint)
  • ✅ Security scan (Checkov)
  • ✅ Plan → comment on PR

4. Code Review

Reviewer ดู:

  • Code quality
  • Plan output (most important!)
  • Security implications
  • Cost impact (Infracost)

5. Merge → Auto Apply Dev

.github/workflows/deploy.yml
on:
push:
branches: [main]

jobs:
apply-dev:
runs-on: ubuntu-latest
environment: dev
steps:
- run: terraform apply -auto-approve

6. Smoke Tests Dev

# Auto run after apply
curl -f https://dev-app.example.com/health

7. Manual Approval → Prod

apply-prod:
needs: apply-dev
environment: production # ← require approval
steps:
- run: terraform apply -auto-approve

GitHub: Settings → Environments → production → Required reviewers

Promotion Strategy

Strategy 1: Same Code, Different Vars

infra/
├── main.tf # shared
├── envs/
│ ├── dev.tfvars
│ ├── staging.tfvars
│ └── prod.tfvars
# Same plan logic, different config
terraform apply -var-file=envs/dev.tfvars
terraform apply -var-file=envs/staging.tfvars
terraform apply -var-file=envs/prod.tfvars

Strategy 2: Same Module, Different Caller

infra/
├── modules/
│ └── web-app/
└── envs/
├── dev/main.tf # uses module
├── staging/main.tf # uses module
└── prod/main.tf # uses module

→ More flexibility per env

Strategy 3: Workspaces (Same Code)

terraform workspace select dev
terraform apply

terraform workspace select prod
terraform apply

Pull Request Patterns

Atlantis (Open-Source TF Pull Request Bot)

Atlantis = bot ที่ run plan/apply ตาม PR comment

atlantis.yaml
version: 3
projects:
- name: prod-network
dir: prod/network/
workflow: prod
- name: dev-network
dir: dev/network/
workflow: dev

workflows:
prod:
plan:
steps: [init, plan]
apply:
steps: [apply]

ใน PR comment:

atlantis plan -p prod-network
atlantis apply -p prod-network

Terraform Cloud

UI-based workflow:

  • VCS connection
  • Auto plan on PR
  • Manual apply
  • State management built-in

Locking & Coordination

State Lock (DynamoDB)

ป้องกัน concurrent apply (ดูเพิ่ม)

Branch Protection

GitHub: Settings → Branches

  • Require PR before merge
  • Require status checks (CI must pass)
  • Require approval count (1+ reviewers)
  • Restrict who can push to main

Environment Protection

  • Require manual approval for prod
  • Restrict deploy to specific reviewers
  • Wait timer before deploy

Rollback Strategy

Terraform ไม่มี built-in rollback — กลยุทธ์:

Option 1: Git Revert + Apply

git revert <commit>
git push
# CI: terraform apply (กลับสู่ state ก่อนหน้า)

Option 2: Apply Old Plan

# Save plan ทุก apply
terraform plan -out=plan-$(date +%s).tfplan

# ถ้าจะ rollback — apply plan เก่า
terraform apply plan-1234567890.tfplan

Option 3: Restore State Version

# S3 versioning
aws s3api list-object-versions --bucket my-tfstate
aws s3api copy-object \
--copy-source "my-tfstate/prod.tfstate?versionId=abc" \
--bucket my-tfstate \
--key prod.tfstate
terraform apply
Restore = Drift Risk

Restore state เก่า ≠ rollback resources ต้อง re-apply เพื่อให้ reality ตรง state เก่า

Blue/Green Deployment

resource "aws_alb_target_group" "blue" {
name = "app-blue"
}

resource "aws_alb_target_group" "green" {
name = "app-green"
}

resource "aws_alb_listener" "main" {
default_action {
type = "forward"
target_group_arn = var.active_color == "blue" ? aws_alb_target_group.blue.arn : aws_alb_target_group.green.arn
}
}

Switch traffic:

terraform apply -var="active_color=green"

Canary Deployment

resource "aws_alb_listener_rule" "canary" {
listener_arn = aws_alb_listener.main.arn

action {
type = "forward"
forward {
target_group {
arn = aws_alb_target_group.stable.arn
weight = var.canary_percentage == 0 ? 100 : 100 - var.canary_percentage
}
target_group {
arn = aws_alb_target_group.canary.arn
weight = var.canary_percentage
}
}
}
}
terraform apply -var="canary_percentage=10"   # 10% to canary
# Monitor metrics
terraform apply -var="canary_percentage=50" # promote 50%
terraform apply -var="canary_percentage=100" # full rollout

Pipelines for Multiple Stacks

.github/workflows/multi-stack.yml
jobs:
network:
runs-on: ubuntu-latest
steps:
- run: |
cd network
terraform apply -auto-approve

data:
needs: network
runs-on: ubuntu-latest
steps:
- run: |
cd data
terraform apply -auto-approve

compute:
needs: data
runs-on: ubuntu-latest
steps:
- run: |
cd compute
terraform apply -auto-approve

apps:
needs: compute
runs-on: ubuntu-latest
steps:
- run: |
cd apps
terraform apply -auto-approve

Drift Detection

on:
schedule:
- cron: '0 9 * * *' # daily at 9 AM

jobs:
drift:
runs-on: ubuntu-latest
steps:
- run: terraform plan -refresh-only -detailed-exitcode

- name: Notify if drift
if: failure()
run: |
curl -X POST $SLACK_WEBHOOK \
-d '{"text": "⚠️ Drift detected in prod"}'

Cost Estimation

ใช้ Infracost ใน PR:

- name: Generate Cost Estimate
uses: infracost/actions/setup@v3

- run: |
infracost breakdown --path . --format json --out-file infracost.json
infracost comment github --path infracost.json \
--pull-request ${{ github.event.pull_request.number }}

→ PR แสดงค่าใช้จ่ายต่อเดือน

Best Practices

✅ DO:
- Plan ใน PR, apply on merge
- Manual approval สำหรับ prod
- Smoke tests หลัง deploy
- Drift detection daily
- Cost estimation in PR
- Document deploy order
- State lock + branch protection

❌ DON'T:
- ห้าม direct apply ใน prod จาก laptop
- ห้าม skip plan review
- ห้าม -auto-approve ใน prod terminal
- ห้าม commit credentials
- ห้าม destructive ops โดยไม่ approval

ตัวอย่าง Mature Workflow

.github/workflows/terraform.yml
name: Terraform

on:
pull_request:
paths: ["**.tf"]
push:
branches: [main]
paths: ["**.tf"]

jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3
- run: terraform fmt -check -recursive
- run: terraform init -backend=false
- run: terraform validate

lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: terraform-linters/setup-tflint@v4
- run: tflint --init && tflint --recursive

security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: bridgecrewio/checkov-action@master

cost:
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v4
- uses: infracost/actions/setup@v3
- run: |
infracost breakdown --path .
infracost comment github --pull-request ${{ github.event.pull_request.number }}

plan:
runs-on: ubuntu-latest
needs: [validate, lint, security]
steps:
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3
- run: terraform init
- run: terraform plan -out=tfplan
- run: terraform show -json tfplan > plan.json
- uses: actions/upload-artifact@v4
with:
name: plan
path: tfplan

apply-dev:
runs-on: ubuntu-latest
needs: plan
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
environment: dev
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with: { name: plan }
- uses: hashicorp/setup-terraform@v3
- run: terraform apply -auto-approve tfplan

smoke-dev:
runs-on: ubuntu-latest
needs: apply-dev
steps:
- run: curl -f https://dev-app.example.com/health

apply-prod:
runs-on: ubuntu-latest
needs: smoke-dev
environment: production # require manual approval
steps:
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3
- run: terraform init
- run: terraform apply -auto-approve

smoke-prod:
runs-on: ubuntu-latest
needs: apply-prod
steps:
- run: curl -f https://app.example.com/health

สรุป

  • Workflow: dev (auto) → smoke → prod (manual approval) → smoke
  • ใช้ Atlantis หรือ Terraform Cloud สำหรับ PR-driven workflow
  • Branch protection + state lock + environment approval
  • Rollback: git revert + apply (Terraform ไม่มี built-in rollback)
  • Drift detection scheduled
  • Cost estimation in PR

ต่อไป → Version Management