โšก ~/naveed Labs
โšก Portfolio Home โœ๏ธ Engineering Blog Deep Dives ๐ŸŽฏ Interview Hub 1,000+ Scenarios โ˜ธ๏ธ Kubernetes Mastery Hub 24 Modules ๐ŸŽฎ DevOps Arcade & Quizzes Subnet Blitz โšก ๐Ÿ—บ๏ธ DevOps Roadmaps PDFs & Guides ๐Ÿค– Morpheus Analysis AI Quant โ†— ๐Ÿ› ๏ธ Developer Tools Utilities ๐Ÿงช Labs & Experiments ๐Ÿ“„ Interactive CV & Certs ๐Ÿ”— All Links & Socials โšก Join The Dispatch (Weekly SRE Newsletter) →
โšก DevOps Lab RUNBOOK
DevOps Lab / Tutorials / Automated Terraform Plan Guardrails
๐Ÿ›ก๏ธ Production Architecture Runbook

Automated Terraform Plan Guardrails & Pre-Apply Disaster Prevention

Target: Terraform 1.1+ · AWS Provider v5+ · Author: Naveed Ahmed
Interactive Simulator โšก

Test Your Review Reflexes in the Arcade

Practice catching RDS replacement traps and count wipes under 30-second timer pressure.

๐Ÿ’ฃ Play Terraform Plan or Destroy →
1
Enforce Code-Level Safeguards with prevent_destroy

Attach explicit lifecycle { prevent_destroy = true } locks to persistent cloud assets (databases, KMS keys, storage buckets). This ensures Terraform errors out locally during the plan phase if an engineer accidentally modifies an immutable attribute:

# main.tf - Critical Database Safeguard
resource "aws_db_instance" "core_production" {
  identifier        = "core-postgres-prod"
  engine            = "postgres"
  instance_class    = "db.r6g.2xlarge"
  allocated_storage = 500

  lifecycle {
    prevent_destroy = true
  }
}
2
Zero-Downtime Refactoring with Declarative moved {} Blocks

When refactoring resource collections from integer count to map-based for_each, declare moved blocks in your root module to reindex state addresses without destroying running compute:

# migrations.tf - Declarative State Re-indexing
moved {
  from = aws_instance.worker[0]
  to   = aws_instance.worker["api"]
}

moved {
  from = aws_instance.worker[1]
  to   = aws_instance.worker["auth"]
}
3
Generate Machine-Readable Plan Output for Policy Auditing

In your build automation, export the execution plan to an encrypted binary artifact, then convert it to normalized JSON for automated policy engines:

# Generate binary plan
terraform plan -out=tfplan.binary

# Convert binary plan to JSON AST
terraform show -json tfplan.binary > tfplan.json
4
Author Conftest Open Policy Agent (OPA) Guardrails

Write a Conftest policy in Rego (policy/disaster_prevention.rego) that inspects the resource_changes array in tfplan.json and denies any PR attempting to delete or replace critical resources:

package terraform.analysis

import future.keywords.in

default allow = false

# Rule: Deny any destruction of RDS databases in production
deny[msg] {
    some resource in input.resource_changes
    resource.type == "aws_db_instance"
    "delete" in resource.change.actions
    msg := sprintf("BLOCKED: Pull request attempts to DESTROY database: %v", [resource.address])
}

# Rule: Block immutable replacement traps on S3 buckets
deny[msg] {
    some resource in input.resource_changes
    resource.type == "aws_s3_bucket"
    resource.change.actions == ["delete", "create"]
    msg := sprintf("BLOCKED: Pull request forces REPLACEMENT of S3 bucket: %v", [resource.address])
}
5
Automate Guardrail Enforcement in GitHub Actions

Embed Conftest evaluation directly into your Pull Request CI workflow (.github/workflows/terraform-pr.yml):

name: Terraform Plan & Security Guardrails
on:
  pull_request:
    branches: [main]

jobs:
  validate-plan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: hashicorp/setup-terraform@v3
      - uses: open-policy-agent/conftest-action@v1
        with:
          version: '0.45.0'

      - name: Terraform Plan
        run: |
          terraform init
          terraform plan -out=tfplan.binary
          terraform show -json tfplan.binary > tfplan.json

      - name: Conftest Policy Check
        run: conftest test tfplan.json -p policy/
6
Run Intentional Trap Smoke Test

Test your defense layer by pushing a deliberate database renaming branch. Observe the automated GitHub Actions pipeline failing instantly with an actionable error before any human can execute apply:

FAIL - tfplan.json - terraform.analysis - BLOCKED: Pull request attempts to DESTROY database: aws_db_instance.core_production
1 test, 0 passed, 0 warnings, 1 failure, 0 exceptions