BXUP Track progress & test yourself — free
DevOps / SRE / Platform · Hands-on lab · ~5 min read
DEVOPS / SRE / PLATFORM · HANDS-ON LAB

Lab 01 · The IaC Verification Pipeline

Build the gate chain that makes AI-drafted Terraform safe to review: terraform validate → terraform plan → Checkov → Infracost → a human review checklist. You will run a deliberately AI-flawed module through the chain and catch three seeded defects — one per mechanical gate — plus one judgment call no scanner flags.

The January 2026 TerraFormer paper showed that even frontier models hallucinate HCL resource types and attributes; the skill this lab trains is not writing Terraform, it is never trusting generated Terraform that has not survived the chain.

Time & tier

~90 minutes hands-on · Core tier (maps to module M2).

Setup

Everything runs offline against a mock AWS provider — no cloud account, no credentials, $0. Install: Terraform ≥ 1.5 (or OpenTofu), pip install checkov, and the Infracost CLI (free tier; skippable — the lab notes what it would have caught).

Create a directory webstack/ with these four files. This is your "AI output" — it simulates a typical assistant draft of a 3-tier stack and contains three seeded defects you must not fix until the gates find them.

# provider.tf
terraform {
  required_version = ">= 1.5.0"
  required_providers {
    aws = { source = "hashicorp/aws", version = "~> 5.0" }
  }
}

provider "aws" {
  region                      = "us-east-1"
  access_key                  = "mock"
  secret_key                  = "mock"
  skip_credentials_validation = true
  skip_requesting_account_id  = true
  skip_metadata_api_check     = true
}
# network.tf
variable "vpc_cidr" {
  type    = string
  default = "10.42.0.0/24"
}

resource "aws_vpc" "main" {
  cidr_block           = var.vpc_cidr
  enable_dns_hostnames = true
  tags                 = { Name = "webstack-vpc" }
}

resource "aws_subnet" "app" {
  count             = 2
  vpc_id            = aws_vpc.main.id
  cidr_block        = cidrsubnet(var.vpc_cidr, 12, count.index)
  availability_zone = ["us-east-1a", "us-east-1b"][count.index]
  tags              = { Name = "webstack-app-${count.index}" }
}

resource "aws_security_group" "app" {
  name   = "webstack-app"
  vpc_id = aws_vpc.main.id

  ingress {
    description = "ssh for ops"
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    description = "app traffic"
    from_port   = 8080
    to_port     = 8080
    protocol    = "tcp"
    cidr_blocks = [var.vpc_cidr]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}
# compute.tf
resource "aws_instance" "app" {
  count              = 2
  ami                = "ami-0c02fb55956c7d316"
  instance_type      = "m5.4xlarge"
  subnet_id          = aws_subnet.app[count.index].id
  security_group_ids = [aws_security_group.app.id]
  tags               = { Name = "webstack-app-${count.index}" }
}
# storage.tf
resource "aws_s3_bucket" "assets" {
  bucket = "webstack-assets-bucket"
}

resource "aws_db_instance" "orders" {
  identifier          = "webstack-orders"
  engine              = "postgres"
  engine_version      = "16.3"
  instance_class      = "db.t3.medium"
  allocated_storage   = 20
  username            = "app"
  password            = "changeme123"
  skip_final_snapshot = true
  publicly_accessible = false
}
Note

The mock provider trick (skip_credentials_validation + fake keys) lets plan run fully offline because create-only plans never call AWS APIs. Avoid data sources and it works for any module.

Guided steps

  1. Start a defect log. Create defect-log.md with columns: defect · which gate caught it · why earlier gates missed it · fix. You will fill one row per gate.
  2. Gate 1 — validate. Run terraform init -input=false && terraform validate. It fails with Unsupported argument … "security_group_ids" is not expected here in compute.tf. This is a classic hallucination: the model blended aws_instance's real argument vpc_security_group_ids with the security_group_ids attribute other resources use. Ask your AI assistant to explain and fix it, then confirm the fix against the provider docs — not against the assistant's word. Log it. Re-run validate until green.
  3. Gate 2 — plan. Run terraform plan -input=false. Validate passed, but plan fails: Error in function call … cidrsubnet — carving 12 new bits out of a /24 would need a /36, which cannot exist. Validate missed it because expression evaluation only happens at plan. Fix (cidrsubnet(var.vpc_cidr, 4, count.index) gives two /28s), log it, re-plan until you see Plan: 8 to add.
  4. Gate 3 — policy scan. Run checkov -d . --quiet --compact. Expect roughly 30 findings across 6 resources. Now triage like a reviewer, not a checkbox-clicker: the seeded defect is CKV_AWS_24 — SSH open to 0.0.0.0/0. Also treat CKV_SECRET_6 (the hardcoded DB password) as must-fix: move it to variable "db_password" { sensitive = true }. Restrict SSH to a management CIDR. Log the defect.
  5. Triage the rest. Ask your assistant to group the remaining findings into fix-now / fix-later / suppress-with-reason and challenge its grouping. Cross-region replication for a static-assets bucket? Multi-AZ for a lab database? Suppress with # checkov:skip=CKV_AWS_144:single-region by design style annotations so the pipeline is green and auditable.
  6. Gate 4 — cost. Run infracost breakdown --path . (or, without an account, price it manually). Two m5.4xlarge instances is roughly $1,100/month for an app tier no requirement justifies — a number, not an error, which is exactly why no earlier gate fired. Right-size (e.g., t3.medium) and log it as the judgment catch.
  7. Gate 5 — human checklist. Write review-checklist.md with at least six questions a human must answer before apply (blast radius? state file location? deletion protection? tags for cost attribution? who approved the SG exceptions? rollback plan?). Then wire gates 1–4 into one verify.sh that exits non-zero on any failure.
Key Takeaway:

Each gate catches a class the previous gate structurally cannot: syntax/schema → expression evaluation → policy → economics → judgment. Order is cheap-to-expensive; never skip ahead.

Verification

  • terraform validate and terraform plan both succeed (plan shows 8 resources).
  • checkov -d . --quiet reports zero unaddressed failures (fixed or annotated-suppressed).
  • defect-log.md has ≥ 3 rows, each naming the gate that caught it and why earlier gates missed it.
  • verify.sh runs the full chain and exits 0; deliberately re-introduce the SSH rule and confirm it exits non-zero.

Completion artifact

The webstack/ repo containing the fixed module, verify.sh, review-checklist.md, and defect-log.md. Self-grading rubric (pass = 8/10):

  • Defect capture (0–3): all three seeded defects logged with correct gate attribution = 3.
  • Gate mechanics (0–2): chain runs as one script, fails closed = 2.
  • Triage quality (0–2): Checkov noise sorted with written reasons, suppressions annotated in-code = 2.
  • Cost judgment (0–2): oversize instance caught and right-sized with a stated assumption = 2.
  • Checklist realism (0–1): a colleague could run your human review without you present = 1.

Stuck?

  • init fails offline: provider download needs internet once; afterwards everything is local.
  • plan demands credentials: you removed one of the three skip_* flags or added a data source.
  • Checkov output overwhelms: add --check CKV_AWS_24,CKV_SECRET_6 first, then widen.
  • Infracost needs signup you can't do: price instances from the AWS pricing page and log the same conclusion.

Client-safe variant

On client engagements where IaC cannot enter public AI tools: run the same chain unchanged — validate, plan, Checkov and Infracost are all local OSS and see no LLM. For the AI-assist steps, use the client-approved tenancy (Bedrock/Azure OpenAI) or a local model via Ollama, and redact account IDs/CIDRs before prompting. The gate chain is precisely what makes AI assistance defensible under an MSA: every generated line passes deterministic verification you can show an auditor.

Answer key (don't read before attempting)

  1. Gate 1 (validate):security_group_ids on aws_instance — hallucinated attribute; real one is vpc_security_group_ids.
  2. Gate 2 (plan):cidrsubnet(var.vpc_cidr, 12, …) on a /24 — arithmetic impossible; only fails when expressions are evaluated.
  3. Gate 3 (Checkov): SSH ingress from 0.0.0.0/0 (CKV_AWS_24); the hardcoded password = "changeme123" (CKV_SECRET_6) is a legitimate fourth catch.
  4. Gate 4 (judgment):m5.4xlarge × 2 (~$1,100/mo) with no stated requirement — legal, secure, and wrong.
This is the free reading view. Open BXUP to track your progress, run the hands-on labs, and prove it with original, exam-pattern assessments.
© Anvi Innovate · BXUP — Be Exponentially Upskilled · BXUP is an independent learning platform, not affiliated with or endorsed by AWS, Microsoft, Google, NVIDIA, IBM, Salesforce, Databricks, Oracle or Anthropic. All trademarks belong to their respective owners. · bxup.in