Infrastructure code is the one AI output that machines can check before a human ever reads it. This module makes validate → plan → policy → cost a reflex, so every AI-drafted module arrives at review with the mechanical failure classes already caught and your attention free for the two that aren't.
Meera's assistant produced the S3 module in eleven seconds. Clean HCL, sensible variable names, a versioning block, a lifecycle rule, tags threaded through — the kind of output that makes even a skeptic quiet for a moment. It also contained aws_s3_bucket_lifecycle_rule, a resource that does not exist; an inline acl = "private" the AWS provider deprecated two major versions ago; and a force_destroy = true nobody asked for, sitting one typo away from erasing the bucket of checkout-api logs it was meant to hold.
None of this is visible by reading. It reads right — that is exactly the point, and exactly the danger. Meera, of all people, had almost pasted it in; she caught the lifecycle resource only because she'd fought that same error the week before.
"This is why I keep it away from prod," she told Divya. Divya, reading over her shoulder, drew the opposite lesson: not a reason to ban the tool, but a reason the tool needs gates the machine runs — because human eyes are precisely what these failure classes are optimized to slip past.
Here is the good news, and the reason IaC is where AI assistance earns its keep despite all of that. Infrastructure code is where AI assistance in ops gets real leverage first — and where the trust map from M1 earns its keep. Terraform modules, Kubernetes manifests, and CI pipelines are exactly the artifact class the Common Core called cheap to generate and expensive to be wrong about: declarative, convention-heavy, boilerplate-rich (AI's home turf), yet executed with credentials that can reshape an estate. The good news is structural: IaC is more gateable than almost any other AI output, because the ecosystem already ships deterministic verifiers — terraform validate and plan, schema validators, policy scanners, cost estimators — that catch AI failure modes mechanically, before human review even begins.
This module builds that gate chain as a reflex: you will draft infrastructure with AI at generate-only trust, run every draft through an always-on validate → plan → policy → cost pipeline, learn the specific ways models hallucinate HCL so you recognize them on sight, apply the same loop to Kubernetes manifests with kubeconform and kube-score, use AI for CI failure diagnosis with calibrated trust per failure class, and deploy AI review on infra PRs the only way the evidence supports: advisory, never blocking. Everything here runs on free OSS tooling — the gate chain costs nothing but discipline.
In AI-assisted IaC, the verification chain is the skill — anyone can prompt a module into existence; the engineer who wires validate → plan → policy → cost so that nothing unverified can reach review is the one who ships AI speed without shipping AI risk.
terraform validate → plan → Checkov → Infracost, wired as pre-commit and CI, with worked configsThe Common Core taught hallucinated APIs as a general failure mode. In HCL it has a specific, well-documented shape: research accompanying the TerraFormer work (January 2026) found that even state-of-the-art models hallucinate Terraform resource types and attributes — emitting blocks that read fluently and do not exist. This is worth internalizing as a taxonomy, because each failure mode has a different gate that catches it:
aws_s3_bucket_lifecycle_rule where the real resource is aws_s3_bucket_lifecycle_configuration — the exact ghost in Meera's eleven-second module. Caught by terraform validate — free and instant.validate — this is why validate runs first even though it feels trivial.acl on buckets, deprecated arguments) that v5 rejects or — worse — silently ignores. Sometimes caught by validate, sometimes only visible in plan output or deprecation warnings. Pin provider versions and tell the model which one you use.0.0.0.0/0, encryption omitted, force_destroy = true on a stateful resource, an instance type ten times your budget. Valid, plannable, and dangerous — this is what the policy and cost gates exist for.The ordering matters: each gate is cheaper than the next, and each failure mode falls to the earliest gate that can see it. The taxonomy also explains why "it looks right" is worth nothing in IaC — four of the five classes are specifically optimized, by the model's training objective, to look right.
Stale-syntax hallucinations are the class most likely to survive careless review, because the reviewer half-remembers the old syntax too — always pin provider versions in the prompt and in required_providers, and treat deprecation warnings in plan output as review findings, not noise.
The rule this module exists to install: AI-drafted IaC never reaches human review without passing the full chain. Not because humans are careless, but because human attention is the scarcest gate — spending it on typo-class errors that validate catches for free is waste, and arriving at review with mechanical checks green means review can focus on the two failure classes only humans catch.
The chain, as a Makefile you can drop into any Terraform repo today:
# Gate chain for AI-assisted Terraform. Run: make gate
.PHONY: gate validate plan policy cost
gate: validate plan policy cost
@echo "All gates passed - ready for human review."
validate:
terraform fmt -check -recursive
terraform init -backend=false -input=false
terraform validate
plan:
terraform plan -input=false -out=tfplan.bin
terraform show -json tfplan.bin > tfplan.json
policy:
checkov -d . --quiet --compact
checkov -f tfplan.json --quiet --compact # plan-aware checks
cost:
infracost breakdown --path . --format table
infracost diff --path . --compare-to infracost-base.json || true
And the same chain as CI, where it belongs permanently (GitHub Actions shown; the GitLab translation is mechanical):
name: iac-gates
on:
pull_request:
paths: ["infra/**"]
jobs:
gates:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3
- name: Validate
working-directory: infra
run: |
terraform fmt -check -recursive
terraform init -backend=false -input=false
terraform validate
- name: Plan
working-directory: infra
run: terraform plan -input=false -out=tfplan.bin
- name: Policy scan (Checkov)
uses: bridgecrewio/checkov-action@v12
with:
directory: infra
quiet: true
- name: Cost estimate (Infracost)
uses: infracost/actions/setup@v3
with:
api-key: ${{ secrets.INFRACOST_API_KEY }}
- name: Post cost diff to PR
run: |
infracost breakdown --path infra --format json --out-file /tmp/infracost.json
infracost comment github --path /tmp/infracost.json \
--repo $GITHUB_REPOSITORY --pull-request ${{ github.event.pull_request.number }} \
--github-token ${{ secrets.GITHUB_TOKEN }} --behavior update
Three design notes carry the chain:
checkov:skip) require a written justification in the code.db.r6g.4xlarge" from a month-end surprise into a review-time conversation.Keep a defect log the first month you run the chain — one line per AI error each gate catches, classified against the taxonomy above; after twenty entries you will know your assistant's personal failure signature and your prompts will improve to match.
The whole chain is free — Terraform/OpenTofu, Checkov, and kubeconform are OSS, and Infracost's CLI is free for this use — so "we don't have budget for AI safety tooling" is not a real objection in IaC.
With gates in place, generation can be fast and loose — but a disciplined prompt still saves cycles, because every gate failure is a round trip. The pattern that works for Terraform is contract-first prompting: state versions, module shape, and constraints; let the model fill the interior.
Draft a Terraform module for an S3 bucket for application logs.
Constraints:
- Terraform >= 1.7, AWS provider ~> 5.0 (use current v5 resource
patterns - separate versioning/encryption/lifecycle resources,
no deprecated inline arguments)
- Inputs: bucket_name_prefix, retention_days (default 90), tags
- Requirements: versioning on, SSE-KMS with a key created in the
module, all public access blocked, lifecycle rule to expire
objects after retention_days, no force_destroy anywhere
- Outputs: bucket_arn, kms_key_arn
Emit only files: main.tf, variables.tf, outputs.tf. I will run
validate, plan, Checkov, and Infracost on the result - prefer
being explicit over being clever.
Then run make gate and feed failures back verbatim — "Checkov flags CKV_AWS_145 on the bucket; fix without suppressing" — rather than hand-patching, so the conversation keeps the full context. Two or three loops typically converge. The same contract-first shape applies to pipeline authoring ("GitHub Actions workflow, ubuntu-latest, needs id-token: write for OIDC to AWS, no long-lived keys") and to pipeline migration, this module's other high-frequency services-company task. For Jenkins-to-Actions migration, the discipline is a parity harness, not trust: run both pipelines on the same commits, diff artifacts, test reports, and versions programmatically, and cut over only when the diff is empty or every delta is explained in writing. AI does the transpilation in minutes; the harness is what makes the minutes safe — the exact generate-fast/verify-mechanically trade this module keeps making.
AI-generated Kubernetes YAML fails in the same taxonomy as HCL — invented fields, stale apiVersions, valid-but-unwise defaults — but Kubernetes gives you an even cheaper gate: the API schema itself. The loop is kubeconform for "is this a valid object?" and kube-score for "is this a good object?", wired so generated YAML cannot be applied, or even committed, unvalidated:
# .pre-commit-config.yaml
repos:
- repo: local
hooks:
- id: kubeconform
name: kubeconform (schema validation)
entry: kubeconform -strict -summary
-kubernetes-version 1.30.0
language: system
files: ^k8s/.*\.ya?ml$
- id: kube-score
name: kube-score (best-practice lint)
entry: kube-score score --output-format ci
language: system
files: ^k8s/.*\.ya?ml$
Pin -kubernetes-version to your real cluster version — stale-apiVersion hallucinations (the extensions/v1beta1 ghosts of training data) are invisible against "latest" but caught instantly against your actual version. kube-score then catches the valid-but-unwise class: no resource limits, no probes, single replica behind a PDB, latest image tags. Expect AI-drafted manifests to fail kube-score on first pass almost every time — not because models are bad at YAML, but because minimal examples dominate training data and production hygiene (probes, limits, securityContext, PDBs) is exactly what minimal examples omit. That makes the fix prompt formulaic: paste the kube-score output and require a clean re-emit. As in Terraform, the loop converges in two or three rounds, and the pre-commit wiring means the converged state is the only state that can enter the repo. For clusters you control, the same pair runs in CI against kind for a full dry-run apply — the free path this track's labs use.
kubeconform answers "will the API server accept this?"; kube-score answers "will you regret it at 3 a.m.?" — AI-generated manifests routinely pass the first and fail the second, which is precisely why you run both.
Diagnosing broken pipelines is a Level 1–2 task on your trust map and one of the highest-toil-value AI uses in this module — CI-native assistants (GitLab Duo's root-cause analysis, Copilot in Actions) and paste-the-log workflows both work. But calibration varies sharply by failure class, and knowing the pattern is the skill:
A diagnosis prompt that enforces the discipline across all classes:
Here is a failed CI log (attached) and the diff of the triggering
commit. Produce: (1) top 3 ranked hypotheses with a confidence
each, (2) for each, the exact log line(s) supporting it, (3) the
cheapest command or check that would confirm or kill each
hypothesis. Do not propose fixes yet.
Requiring cited log lines and a disconfirming check per hypothesis converts the model from oracle to hypothesis generator — the same shape M3 uses for incident investigation, learned here where the blast radius is only a red pipeline.
The last gate is the most tempting to over-build. Stack Overflow 2025 found a majority of developers reject fully-AI-driven code review — and for infra PRs the rejection is correct as a matter of design, not sentiment: review is an accountability transfer, and accountability needs a human name on it (the same reason Coinbase's aggressive AI mandate still requires human review on every line). The placement that works is advisory — AI reviews every infra PR and posts findings as comments; a human decides what matters; nothing AI-only ever blocks a merge.
What makes ops-flavored AI review genuinely useful is the rubric. Generic "review this code" produces style noise; an ops rubric aims the model at what infra reviewers actually miss when tired:
Review this Terraform/K8s diff as an infra reviewer. Score ONLY:
1. BLAST RADIUS - resources whose change forces replacement or
touches shared/stateful infrastructure; flag every "destroy"
or "replace" implication.
2. SECURITY - IAM widening, public exposure, encryption changes,
secret handling, security-group deltas.
3. COST - new resources or sizing changes with material spend;
estimate direction, not exact figures.
4. REVERSIBILITY - can this change be cleanly rolled back? Name
anything that cannot.
Output: one finding per line as {severity: HIGH|MED|LOW, area,
file:line, one-sentence rationale}. Max 8 findings, no style
comments. If nothing qualifies in an area, print "AREA: clean".
Capping findings and banning style commentary is load-bearing: an AI reviewer that posts twenty nitpicks trains humans to scroll past it, and the one HIGH that mattered scrolls past too. Measure your bot like any alert source — track precision (findings a human acted on / findings posted) monthly, and tune the rubric when precision sags; lab-05 does exactly this against PRs with known seeded issues. Position in the chain matters too: the advisory review runs after the mechanical gates, so it never wastes its findings budget on what Checkov already caught. And the finding categories are not accidental — blast radius, security, cost, reversibility are your M1 map axes, applied per-diff. The AI reviewer is, in the end, a trust-map query answered automatically for every PR: what here would change this change's placement?
Assessment scenarios probe the advisory-versus-blocking distinction directly — if a design makes an LLM's judgment the sole thing standing between a merge and production, the design is wrong regardless of the model's quality; deterministic gates block, AI advises, humans decide.
Your first 15 minutes: the next time an assistant hands you a Terraform module, run make gate before you read a single line — let validate → plan → policy → cost speak first, so your scarce attention lands only on the two failure classes the machine cannot see. That reflex is the whole chapter; Meera's near-paste is what it prevents.
Check yourself: Your assistant emits a Terraform module that terraform validate and terraform plan both pass cleanly. Why is "the mechanical gates are green" still not enough to approve it — and which two failure classes are now your personal responsibility?
make gate, and log every failure against the five-class hallucination taxonomy. Deliverable: the passing repo plus your classified defect log.This module's companion playbooks cover the gate-chain setup for Terraform/OpenTofu and Kubernetes (with GitLab CI and Jenkins variants), contract-first prompting templates for modules, manifests, and workflows, the pipeline-migration parity harness, CI-diagnosis prompt patterns per failure class, and the advisory-review rubric with client-safe redaction rules for services engagements. Do this at work this week → pb-08 Terraform Module Drafting with the Full Verification Chain (the gate chain, hands-on), pb-11 Kubernetes Manifests and Helm Charts with a Schema-Validation Loop (the kubeconform + kube-score loop), and pb-04 An Advisory AI Review Gate for Infra PRs (advisory, never blocking). Five labs anchor the module and run together in the guided journey's Build stage: the IaC verification pipeline with seeded AI errors (lab-01), the K8s validation loop (lab-02), the CI failure autopsy across five failure classes (lab-03), the Jenkins-to-Actions migration with parity harness (lab-04), and the advisory review bot scored for precision against known issues (lab-05). All five run at zero cost on OSS tooling and kind/minikube.
apiVersion hallucination class.