Every plan, read fully — including the Friday 5pm ones.

Every plan, read fully — including the Friday 5pm ones.

Pull up a chair, grab some chai, and let us be honest with each other for a second. Every platform engineer knows the open secret of infrastructure reviews: the Terraform plan review—the step where someone is supposed to check the preview of cloud resources before anything gets created or destroyed—is usually just whoever happens to be free, squinting at terminal logs at 5:15 PM on a Friday.

We do all the classic hygiene rituals. We pin module versions so external code doesn’t drift beneath our feet, and we isolate remote state files—the centralized storage where Terraform tracks the real-world IDs of your cloud setup—so the blast radius, or the potential wreckage area if something goes south, stays small. But the review itself? That careful, line-by-line inspection of “wait, should this really delete that subnet?” does not magically scale when your engineering team doubles. In fact, it breaks down precisely when you need it most: late in the day, right before a release, when everybody is in a hurry to shut their laptop and head home.

Think of this post as a practical blueprint for the reviewer who never gets tired, never rushes through a diff because dinner is getting cold, and never blindly stamps an approval: a lightweight large language model running right inside your CI (Continuous Integration, the automated pipeline that runs tests on code changes) pipeline. It reads every single Terraform plan against your written team policy and posts its review directly as a pull request comment. No hype, no hand-waving product sales pitch—just the architecture, the policy rules you actually need, the safety boundaries that stop it from going rogue, and the actual budget math before you stand it up.

The architecture: plan in, findings out

The entire pipeline flows through five stages, and honestly, every single stage is deliberately boring. A developer opens a pull request; the CI pipeline runs terraform plan and exports the execution plan as clean JSON; a prompt builder packages that JSON diff alongside your team’s plain-English policy rules; the model evaluates the changes and ranks its findings by severity; and those findings land straight in the PR conversation. A human engineer reads the notes and clicks merge. The automated agent never, ever applies infrastructure.

Every PR flows left to right. The only write path a human owns is the merge button.

Every PR flows left to right. The only write path a human owns is the merge button.

Two simple architectural boundaries do almost all the heavy lifting here:

First, the reviewer’s only input is the plan diff itself—clean, machine-readable JSON. It gets zero cloud credentials, no AWS or GCP IAM roles, and no shell access to poke around your VPCs. It parses text; it never touches live metal.

Second, the trust boundary stays firmly locked inside your CI runner. The model’s API secret lives inside GitHub Actions secrets or GitLab CI variables, nowhere else. And most importantly: the only write path that can trigger a production deployment belongs exclusively to a human being clicking that green merge button.

The CI wiring

The wiring is three steps. Whenever someone opens or updates a pull request that touches infrastructure definitions (.tf files) or input variables (.tfvars files), the workflow fires:

# .github/workflows/tf-review.yml name: terraform-review on: pull_request: paths: ['**.tf', '**.tfvars'] jobs: review: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: hashicorp/setup-terraform@v3 - run: terraform init -backend=false - run: terraform plan -out=tfplan - run: terraform show -json tfplan > plan.json - name: AI plan review env: MODEL_API_KEY: ${{ secrets.MODEL_API_KEY }} run: ./reviewer/review.sh plan.json policy/review-policy.yaml 

Notice what happens here. We initialize Terraform without pulling remote state backends (-backend=false) if we just want module syntax, or run against state in a read-only role, generate an execution file named tfplan, and immediately convert it with terraform show -json into raw, structured data. Instead of feeding messy CLI terminal colors into an LLM, we give it a clean breakdown showing exactly what values exist before and after:

{ "resource_changes": [ { "address": "google_storage_bucket.logs", "change": { "actions": ["update"], "before": { "uniform_bucket_level_access": true }, "after": { "uniform_bucket_level_access": false } } } ] } 

The prompt we feed to the model is purposefully plain. You do not need fancy prompt engineering gymnastics here. Just set crystal-clear constraints and an unambiguous output contract:

You are a Terraform plan reviewer. Review the plan JSON against each rule in the policy. For every violation, return: rule id, severity, resource address, what changed, and a one-paragraph recommendation. Do not approve, merge, or apply anything. If a rule’s intent is unclear from the plan, say so instead of guessing.

Simple, boring prompts perform far better in production pipelines. The real brains of this setup do not belong in clever prompt tricks; they live in your policy file and the model’s ability to read changes in context.

The policy document itself is the actual crown jewel. It sits right in the Git repository, versioned right alongside the HCL (HashiCorp Configuration Language, the syntax used to write Terraform) code it audits, written in straightforward rules that your team can debate and update together:

# policy/review-policy.yaml rules: - id: bucket-no-public severity: high check: "no storage bucket may allow public access" - id: modules-pinned severity: medium check: "every module source must pin an exact version" - id: stateful-lifecycle-guard severity: high check: "databases and stateful resources need prevent_destroy" - id: tag-hygiene severity: low check: "every resource carries owner and environment tags" 

Why bother with an LLM here instead of traditional static analysis tools we have used for years? Because an LLM can understand human intent. A classic linter will happily flag an open CIDR block like 0.0.0.0/0 (meaning wide-open traffic from any IP on the public internet). But what if a pull request says “Internal metric scraper update” in the title, yet the diff quietly opens a database port to the whole VPC? A deterministic linter sees valid syntax and stays quiet. A model spots that the code diff contradicts what the author claimed in the PR description.

This works best as a partner to deterministic engines, not a replacement. Tools like Open Policy Agent (OPA) or Conftest should still handle your hard, non-negotiable boolean rules (like “no public S3 or Cloud Storage buckets, period”) with zero ambiguity. Then, let the LLM handle the nuanced gray areas where intent and context matter. Let the fast, cheap rules run first, and let the model spend its attention where genuine engineering judgment is needed.

Advisory first; the human merges.

Advisory first; the human merges.

What the reviewer should check

If you want engineers to actually read automated comments, you must be ruthless about alert fatigue. The fastest way to kill this tool is dumping twenty low-priority style nitpicks on every PR. That is why severity tiers matter so much:

Severity tiers: block on HIGH, warn on MEDIUM, stay quiet on LOW unless asked.

Here is how the tiers split up the work:

  • HIGH: Blocks the pull request from me low-rging. Think accidental public internet exposure, unencrypted object storage, plain-text API secrets showing up in state diffs, or missing prevent_destroy lifecycle guards on production databases.

  • MEDIUM: Posts a visible warning without failing the build. Think floating module versions (like pointing to a generic main branch instead of a tagged release), mixed pinning conventions, or changes that contradict the PR summary.

  • LOW: Stays completely quiet unless someone specifically triggers an audit comment. Think naming convention drift, minor tag hygiene, or cosmetic ordering of blocks.

Start small. Write a dozen or so rules that the entire team genuinely agrees on, rather than copying fifty idealistic security benchmarks from the internet. An automated reviewer burdened with fifty nitpicky rules is just an obnoxious bot that engineers will mute within a week. A lean set of razor-sharp checks keeps your signal-to-noise ratio high, and that trust is the only reason people keep paying attention.

Anatomy of one finding

A good review comment never just barks an error; it shows its receipts. The reviewer inspects each change through three lenses: the diff (what is physically changing in the JSON), the intent (what the author wrote in the pull request description), and the policy (which rule fired and how critical it is).

Diff first, intent second, policy third — the whole trick.

Diff first, intent second, policy third — the whole trick.

Here is what real-world comments look like when the system spots trouble on a PR:

HIGH — google_storage_bucket.logs sets uniform_bucket_level_access = false with no retention policy block. The PR description says “add log storage for the API”; application logs imply sensitive data. Enable uniform bucket-level access and add a retention rule before merge.

MEDIUM — Module vpc pins ~> 4.2 while subnet references the registry latest tag. Mixed pinning makes this PR non-reproducible across environments. Pin subnet to an exact version.

LOW — The database instance has no prevent_destroy lifecycle guard. This is the resource you least want recreated by accident during a state drift reconciliation; consider adding the guard.

Notice the pattern? The reviewer catches the exact things senior engineers intend to verify during code reviews, but accidentally skip when they are under crunch time. Module version drift, missing database safety latches, forgotten access policies—the classic greatest hits of “we will fix that in technical debt sprint next quarter.”

Human review vs. agent review

Let us look at how both approaches compare across a normal sprint:

  • Review Speed: Humans review whenever someone finally gets a break between meetings; the automated agent runs consistently on every single PR.

  • Consistency: Humans depend heavily on how tired they are; the agent runs through the exact same policy checklist every single time.

  • Friday 5:00 PM plans: Humans skim the terminal output and look for green checkmarks; the agent reads every line of the JSON diff thoroughly.

  • Intent mismatches: Experienced seniors catch them if they are paying close attention; the agent flags diff-versus-description mismatches automatically.

  • Broader business context: Humans understand organizational nuance; the agent does not—and our prompt explicitly tells it to admit lack of context rather than hallucinating answers.

  • Fatigue and boredom: Humans get exhausted after scanning reams of generated infrastructure; the agent never gets tired.

The honest reality: this agent is a fantastic first reviewer, but a terrible final reviewer. It never skims, never cuts corners, and never hits “Approve” just because it wants to log off for the weekend. However, it has no clue that a “risky” database drop is actually totally safe because that microservice is being decommissioned next quarter anyway. That is precisely why the human stays in the driver’s seat and holds the merge rights.

Guardrails and rollout

Before you ever let an LLM read infrastructure diffs in production CI, you need strict guardrails in place. These seven safety rules should be treated as non-negotiable prerequisites:

  • [ ] Read-only by construction — the reviewer only parses JSON diff files; it must never possess cloud credentials that could run terraform apply.

  • [ ] No auto-merge, no auto-apply — the model advises and suggests; human engineers make the final call, every single time.

  • [ ] Redact secrets before transit — ensure environment variables marked sensitive (or ephemeral values introduced in recent Terraform versions) are stripped out so database passwords never leave your CI runner.

  • [ ] Strict severity gating — HIGH findings can fail the PR status check; MEDIUM findings warn gently; LOW findings stay completely silent unless explicitly requested.

  • [ ] Dismissals with required reasons — let engineers dismiss a finding with a quick written reason; treat recurring dismissals as policy bugs to refine your rule set.

  • [ ] Policy in the repo — keep policies version-controlled in plain YAML right next to your infrastructure manifests, so changes go through standard PR review.

  • [ ] Boring prompt, small sharp rules — remember that your policy file is the real product, not complex prompt engineering hacks.

When rolling this out across your engineering organization, take it in deliberate phases. Start in advisory mode: let it post PR comments without failing builds or blocking anything. Let your team get used to it, debate its feedback, and point out mistakes. Once the hit rate proves itself reliable, promote HIGH severity findings to block merge checks. MEDIUM rules can follow later if the team agrees. LOW should stay strictly advisory—its job is to help when asked, not to spam people.

An automated reviewer earns engineering trust the exact same way a senior staff engineer does: by being consistently right about the things that actually matter, and staying quiet about the rest.

Cost and latency: what to budget for

When setting this up, expect two operational bills, both of which are straightforward to forecast. The LLM API bill depends on three levers: your average plan JSON size, the model’s cost per token (the basic units of text processed by language models), and how many pull requests your team opens each day. Massive plans managing hundreds of cloud resources can blow up token counts quickly. Keep this cheap by filtering out unchanged resources from the JSON diff before sending it, using smaller and faster models for the initial review pass, and configuring GitHub Actions path triggers so the job only runs when .tf files are actually modified.

Your second bill is standard CI runner minutes for the plan, JSON conversion, and script execution. Compared to the LLM API, this cost is minimal, but it still ticks up with repository scale.

The real cost that can derail this project is not compute money—it is noise. If your reviewer comments five times on every PR about indentation and missing descriptions, engineers will quickly ignore it, and you will have built an expensive CI step that nobody looks at. Plan time for tuning your policies, not just paying for API tokens: start with a tiny set of high-value rules, enforce strict severity limits, and treat every ignored or unhelpful comment as a bug in your policy.

The takeaway

Every engineering team has a mental checklist of safety standards they swear they follow whenever there is enough time. But in reality, there is rarely enough time. An automated review workflow like this does not magically solve company culture—but it guarantees that your baseline safety checklist gets applied to every single pull request, especially the rushed Friday afternoon ones when mistakes love to hide.

Start in advisory mode. Keep your policy rules crisp and practical. Keep human engineers firmly on merge duty. The point was never to replace human reviewers—it is to make sure that before any code touches production, every plan gets scrutinized by something that actually reads every single line.

Disclaimer

  • This post describes my personal experience with my own systems and accounts, in my own words. It is a first-person account of what I observed and what I do in my own work — not a statement of any company's intentions, policies, or internal decisions. Where I do not know something, the post says so.
  • I am not affiliated with, endorsed by, or speaking for any company, product, or vendor named in this post. All product and company names are trademarks of their respective owners and are mentioned only to describe the experience accurately.
  • Any error messages, documentation excerpts, or figures quoted here are reproduced from my own records or from the public sources linked in the post, believed accurate at the time of writing. Product behavior changes over time; verify against current official documentation before relying on any detail.
  • Nothing in this post is professional, financial, legal, or security advice for your specific situation. Examples are illustrative — validate in your own environment before running anything in production.
  • Corrections are welcome. If any company named here publishes an official statement, or if any detail is wrong, I will update this post and note the correction. The goal is an accurate public record, not a verdict.

© 2026 Honey Kiran. All rights reserved.

Honey Kiran · DevOps & Platform Engineering

Practical guides on monitoring, SRE, Terraform, and platform engineering for high-scale systems.

Your turn: Send this to the teammate who rubber-stamps Terraform plans on Fridays.

Examples are illustrative — validate in your own environment before running in production.