81.5 Project 81: Terraform plan on pull request
Friends, apply without a reviewed plan is gambling. Today we run terraform plan on every PR, post the plan as a comment tip, and apply only on main after approve.
मित्रांनो, reviewed plan नसताना apply = जुगार. आज प्रत्येक PR वर terraform plan, plan comment tip, आणि approve नंतरच main वर apply.
दोस्तों, बिना reviewed plan apply = जुआ. आज हर PR पर terraform plan, plan comment tip, और approve के बाद ही main पर apply.
What you will build in this project
- Workflow: plan on pull_request.
- OIDC to the Terraform role.
- Save plan artifact.
- Apply only on main.
1. Start from a real company story
At ShareChat in Bengaluru, someone applied from a laptop and deleted a prod subnet by mistake.
Rough cost: about ₹35 lakh in downtime that afternoon.
Ritu forced plan-on-PR and apply-on-main with OIDC.
Today we build that gate.
Ravindra Bagale's Tip
Students run apply on every branch. Limit apply to main or a release tag. Keep that habit.
Students प्रत्येक branch वर apply चालवतात. Apply फक्त main किंवा release tag वर. सवय ठेवा.
Students हर branch पर apply चलाते हैं. Apply सिर्फ main या release tag पर. आदत रखिए.
2. Start and prepare
Step 1: Why plan on PR
Why: Catch destroy before merge. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: echo why. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 1: a1
Type this:
echo 'terraform plan on PR shows adds changes destroys before merge'
You will see:
terraform plan on PR shows adds changes destroys before merge
What it means: Why.
Example 2: a2
Type this:
mkdir -p /tmp/gha81/infra/.github/workflows && echo 'Today: plan on PR, apply on main, OIDC'
You will see:
Today: plan on PR, apply on main, OIDC
What it means: Scope.
Example 3: a3
Type this:
echo 'Never apply unreviewed plans to prod'
You will see:
Never apply unreviewed plans to prod
What it means: Rule.
Step 2: Tiny module
Why: Sample tf. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: printf main.tf. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 4: b1
Type this:
printf '%s\n' 'terraform {' ' required_version = ">= 1.5.0"' ' required_providers {' ' aws = { source = "hashicorp/aws", version = "~> 5.0" }' ' }' '}' 'provider "aws" { region = "ap-south-1" }' 'resource "aws_s3_bucket" "web" { bucket = "shop-web-lab-unique" }' > /tmp/gha81/infra/main.tf && head -5 /tmp/gha81/infra/main.tf
You will see:
terraform {
What it means: main.tf.
Example 5: b2
Type this:
echo 'Remote state from Terraform projects 52 stays required in real life'
You will see:
Remote state from Terraform projects 52 stays required in real life
What it means: State.
Example 6: b3
Type this:
echo 'Use a unique bucket name or a random suffix in labs'
You will see:
Use a unique bucket name or a random suffix in labs
What it means: Unique.
Step 3: Plan job
Why: PR workflow. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: YAML tip. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 7: c1
Type this:
printf '%s\n' 'name: tf' 'on:' ' pull_request:' ' paths: [ "infra/**" ]' ' push:' ' branches: [ main ]' ' paths: [ "infra/**" ]' 'jobs:' ' plan:' ' if: github.event_name == \'pull_request\'' ' runs-on: ubuntu-latest' ' permissions: { id-token: write, contents: read }' ' defaults: { run: { working-directory: infra } }' ' steps:' ' - uses: actions/checkout@v4' ' - uses: hashicorp/setup-terraform@v3' ' - uses: aws-actions/configure-aws-credentials@v4' ' with: { role-to-assume: arn:aws:iam::123456789012:role/tf-plan, aws-region: ap-south-1 }' ' - run: terraform init -input=false' ' - run: terraform plan -input=false -out=tfplan' ' - uses: actions/upload-artifact@v4' ' with: { name: tfplan, path: infra/tfplan }' > /tmp/gha81/infra/.github/workflows/tf.yml && grep -E 'plan:|terraform plan' /tmp/gha81/infra/.github/workflows/tf.yml
You will see:
plan:\n - run: terraform plan -input=false -out=tfplan
What it means: Plan job.
Example 8: c2
Type this:
echo 'paths filter avoids running tf on docs-only PRs'
You will see:
paths filter avoids running tf on docs-only PRs
What it means: Filter.
Example 9: c3
Type this:
echo 'Plan role needs read IAM; apply role is separate and tighter'
You will see:
Plan role needs read IAM; apply role is separate and tighter
What it means: Roles.
3. Build the main pieces
Step 4: Apply job
Why: Main only. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: apply tip. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 10: d1
Type this:
printf '%s\n' ' apply:' ' if: github.ref == \'refs/heads/main\' && github.event_name == \'push\'' ' runs-on: ubuntu-latest' ' permissions: { id-token: write, contents: read }' ' environment: production' ' defaults: { run: { working-directory: infra } }' ' steps:' ' - uses: actions/checkout@v4' ' - uses: hashicorp/setup-terraform@v3' ' - uses: aws-actions/configure-aws-credentials@v4' ' with: { role-to-assume: arn:aws:iam::123456789012:role/tf-apply, aws-region: ap-south-1 }' ' - run: terraform init -input=false' ' - run: terraform apply -input=false -auto-approve' >> /tmp/gha81/infra/.github/workflows/tf.yml && grep -A2 'apply:' /tmp/gha81/infra/.github/workflows/tf.yml
You will see:
apply:\n if: github.ref == 'refs/heads/main' && github.event_name == 'push'\n runs-on: ubuntu-latest
What it means: Apply.
Example 11: d2
Type this:
echo 'environment: production can require a forge reviewer'
You will see:
environment: production can require a forge reviewer
What it means: Env.
Example 12: d3
Type this:
echo 'Prefer apply saved plan file in stricter teams'
You will see:
Prefer apply saved plan file in stricter teams
What it means: Saved plan.
Step 5: fmt validate
Why: Fast gates. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: steps. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 13: e1
Type this:
printf '%s\n' '- run: terraform fmt -check -recursive' '- run: terraform validate' > /tmp/gha81/FMT.txt && cat /tmp/gha81/FMT.txt
You will see:
- run: terraform fmt -check -recursive
What it means: fmt.
Example 14: e2
Type this:
echo 'Put fmt+validate before plan to fail fast'
You will see:
Put fmt+validate before plan to fail fast
What it means: Order.
Example 15: e3
Type this:
echo 'tflint can join later for style rules'
You will see:
tflint can join later for style rules
What it means: tflint.
Step 6: Comment tip
Why: Human readable plan. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: PR comment. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 16: f1
Type this:
printf '%s\n' 'Optional: terraform show -no-color tfplan > plan.txt' 'Upload plan.txt or post via a comment action' > /tmp/gha81/COMMENT.txt && cat /tmp/gha81/COMMENT.txt
You will see:
Optional: terraform show -no-color tfplan > plan.txt
What it means: Comment.
Example 17: f2
Type this:
echo 'Reviewers must read destroys carefully'
You will see:
Reviewers must read destroys carefully
What it means: Review.
Example 18: f3
Type this:
printf '%s\n' 'Acceptance: PR shows plan; main apply succeeds after merge' > /tmp/gha81/ACCEPT.txt && cat /tmp/gha81/ACCEPT.txt
You will see:
Acceptance: PR shows plan; main apply succeeds after merge
What it means: Gate.
4. Automate and verify
Step 7: State locking
Why: Still required. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: backend tip. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 19: g1
Type this:
echo 'S3 backend + DynamoDB lock from project 52'
You will see:
S3 backend + DynamoDB lock from project 52
What it means: Lock.
Example 20: g2
Type this:
echo 'Two applies at once without a lock corrupt state'
You will see:
Two applies at once without a lock corrupt state
What it means: Why.
Example 21: g3
Type this:
echo 'Plan also takes a lock — do not cancel mid-plan casually'
You will see:
Plan also takes a lock — do not cancel mid-plan casually
What it means: Cancel.
Step 8: Cost tip
Why: Infracost on PR. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: bridge. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 22: h1
Type this:
echo 'Add Infracost from project 60 as a PR check'
You will see:
Add Infracost from project 60 as a PR check
What it means: Infracost.
Example 23: h2
Type this:
echo 'Fail when monthly delta exceeds a team budget'
You will see:
Fail when monthly delta exceeds a team budget
What it means: Budget.
Example 24: h3
Type this:
echo 'Cost and plan together make better reviews'
You will see:
Cost and plan together make better reviews
What it means: Together.
Step 9: Secrets
Why: No cloud keys. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: OIDC. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 25: i1
Type this:
echo 'Reuse OIDC from project 80 for tf-plan and tf-apply roles'
You will see:
Reuse OIDC from project 80 for tf-plan and tf-apply roles
What it means: OIDC.
Example 26: i2
Type this:
echo 'Separate plan and apply roles — plan cannot create most resources'
You will see:
Separate plan and apply roles — plan cannot create most resources
What it means: Split.
Example 27: i3
Type this:
echo 'Never echo terraform.tfvars with secrets in logs'
You will see:
Never echo terraform.tfvars with secrets in logs
What it means: Logs.
5. Harden and accept
Step 10: Story ShareChat
Why: Laptop apply. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: story. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 28: j1
Type this:
printf '%s\n' 'At ShareChat in Bengaluru, a laptop apply deleted a prod subnet.' 'Ritu forced plan-on-PR and apply-on-main with OIDC.' > /tmp/gha81/STORY.txt && wc -l /tmp/gha81/STORY.txt
You will see:
2 /tmp/gha81/STORY.txt
What it means: Story.
Example 29: j2
Type this:
echo 'Rough cost: about ₹35 lakh in downtime that afternoon'
You will see:
Rough cost: about ₹35 lakh in downtime that afternoon
What it means: Cost.
Example 30: j3
Type this:
echo 'Ritu: PASS'
You will see:
Ritu: PASS
What it means: Sign-off.
Step 11: Proof
Why: Workflow. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: grep. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 31: k1
Type this:
grep -E 'terraform plan|terraform apply' /tmp/gha81/infra/.github/workflows/tf.yml
You will see:
- run: terraform plan -input=false -out=tfplan\n - run: terraform apply -input=false -auto-approve
What it means: Commands.
Example 32: k2
Type this:
test -f /tmp/gha81/ACCEPT.txt && echo 'accept ok'
You will see:
accept ok
What it means: accept.
Example 33: k3
Type this:
echo 'Next: GitLab CI pipeline with stages project 82'
You will see:
Next: GitLab CI pipeline with stages project 82
What it means: Bridge.
Step 12: Acceptance
Why: Plan on PR apply on main. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy.
What: final. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool.
Example 34: z1
Type this:
echo 'PR runs terraform plan'
You will see:
PR runs terraform plan
What it means: Plan.
Example 35: z2
Type this:
echo 'main runs terraform apply'
You will see:
main runs terraform apply
What it means: Apply.
Example 36: z3
Type this:
echo 'OIDC roles separate'
You will see:
OIDC roles separate
What it means: OIDC.
Check yourself
Q1. Who fixed ShareChat laptop apply?
Ritu
Q2. PR runs?
plan
Q3. Main runs?
apply
Q4. Auth?
OIDC
Q5. Artifact?
plan file
Q6. Apply on feature branch?
no
Friends, you planned Terraform on every PR. Did you understand? Let us try it once more. Add a fmt and validate job that fails on bad formatting.
मित्रांनो, तुम्ही प्रत्येक PR वर Terraform plan केला. fmt आणि validate job जोडा जी चुकीच्या formatting वर fail होईल.
दोस्तों, आपने हर PR पर Terraform plan किया. fmt और validate job जोड़ें जो गलत formatting पर fail हो.
Lab: try at home
- Plan workflow.
- OIDC role.
- Artifact.
- Main apply.
Recap
- Plan before apply.
- PR only plans.
- Main applies.
- OIDC no keys.
- fmt+validate first.