Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for Terraform By James Joyner IV · · 12 min read

Building an AI Terraform PR Review Bot That Can't Touch Your Infra

Wire an AI reviewer into Terraform pull requests so it comments on every plan automatically — with an architecture that gives it zero ability to apply anything.

  • #terraform
  • #ai
  • #ci-cd
  • #automation
  • #review

After the third time I asked a teammate “did you notice this plan recreates the cache cluster?” I decided the first-pass review should happen automatically. Not the decision — a human still approves every apply — but the tedious first read: spotting replacements, over-broad IAM, open security groups, and missing moved blocks. That’s a fast junior reviewer’s job, and a fast junior reviewer is precisely what an AI is.

The entire design challenge of a Terraform review bot is one constraint: it must comment on plans without being able to apply them, read state, or hold cloud credentials. Get the architecture right and the bot is a pure read-and-comment function — even if it were fully compromised, it couldn’t touch your infrastructure. This is the build that respects that constraint.

The two-job architecture

The non-negotiable shape is two separated jobs with a one-way data flow between them:

  1. Plan job — has cloud credentials and backend access. It runs terraform plan, exports JSON, and uploads it as an artifact. It does not call any AI.
  2. Review job — has no credentials and no backend. It downloads the plan JSON, sends it to the AI, and posts the result as a PR comment.

The review job, where the model lives, literally cannot authenticate to your cloud or your state. There’s no secret to leak because none is present.

# .github/workflows/tf-review.yml
jobs:
  plan:
    runs-on: ubuntu-latest
    permissions: { contents: read }
    steps:
      - uses: actions/checkout@v4
      - run: terraform init && terraform plan -out=tfplan
        env:                       # creds ONLY in this job
          AWS_ROLE_ARN: ${{ secrets.PLAN_ROLE }}
      - run: terraform show -json tfplan > plan.json
      - uses: actions/upload-artifact@v4
        with: { name: plan, path: plan.json }

  review:
    needs: plan
    runs-on: ubuntu-latest
    permissions: { pull-requests: write }   # comment only
    steps:
      - uses: actions/download-artifact@v4
        with: { name: plan }
      - run: ./scripts/ai-review.sh plan.json   # no cloud creds here

Notice the permissions blocks: the plan job can read code, the review job can write PR comments. Neither grants the review job anything dangerous.

Pre-shrink the plan before the model sees it

Sending a raw 2,000-resource plan to a model is slow and expensive. Filter to the changes that matter first:

jq '[.resource_changes[]
  | select(.change.actions != ["no-op"])
  | {address, type, actions: .change.actions,
     replace: (.change.actions == ["delete","create"])}]' \
  plan.json > diff.json

diff.json is a tight changeset. The bot reasons over this, not the kitchen sink.

The review prompt

The bot’s prompt frames it as a triage commenter, never an approver:

“You are reviewing a Terraform plan changeset. Produce a Markdown comment with three sections: HIGH RISK (replacements of stateful resources, deletions, IAM or networking changes), REVIEW (in-place changes worth a look), and NOTED (tags, descriptions). For each HIGH RISK item, name the resource and explain the likely cause from the before/after values. End with: ‘A human must review and approve before apply.’ Never state that the change is safe to apply.”

That forced closing line is a small thing that sets the right expectation on every comment: the bot read it first, a person decides.

Pro Tip: Make the bot fail the check (non-zero exit) when it finds a stateful replacement, so the PR is blocked until a human explicitly acknowledges it. The AI surfaces the risk; the required-status-check enforces that someone looked.

Wire the comment back

The review script posts via the API the job is allowed to use — PR comments, nothing more:

#!/usr/bin/env bash
summary=$(call-ai-review < diff.json)   # your model call
gh pr comment "$PR_NUMBER" --body "$summary"

The call-ai-review step is the only place a model is involved, and it receives JSON and returns text. No terraform binary, no backend, no apply anywhere in this job.

Keep humans firmly in the loop

The bot is an accelerant, not an authority. The guardrails that keep it that way:

  • Apply stays gated. A separate, human-triggered job does terraform apply. The bot has no part in it and no path to it.
  • The bot can only comment. Its CI permissions grant PR-comment write and nothing else. This is enforced by the platform, not by trust.
  • Every comment ends by deferring to a human. No “looks good to merge.” The model triages; people approve.

I route the bot’s output into the code review dashboard so AI comments and human approvals live in one place, and a genuinely alarming finding (an active exposure, not a proposed one) can be escalated to incident response instead of sitting in a PR thread.

Add the diff context the plan JSON lacks

Plan JSON tells the bot what changes, but not what the author intended or what the HCL looks like. For higher-quality comments, give the review job the Git diff of the .tf files too — it’s already public to the bot and reveals intent the plan can’t:

  review:
    steps:
      - uses: actions/checkout@v4          # for the diff
        with: { fetch-depth: 0 }
      - run: git diff origin/main...HEAD -- '*.tf' > hcl.diff
      - run: ./scripts/ai-review.sh plan.json hcl.diff

Now the bot can say “this PR adds a prevent_destroy = false lifecycle change and the plan shows a replacement — that combination looks intentional but risky” rather than reasoning from the plan in isolation. The diff is read-only source code; handing it to the review job adds zero capability and a lot of context. Crucially, this still grants no credentials — git diff reads the checked-out repo, nothing more.

Make the bot’s findings actionable, not just descriptive

A wall of risk prose gets ignored after the third PR. Tune the bot to be specific and sparing: link each finding to the exact resource address, suggest the concrete fix where it can, and stay quiet on no-op changes entirely. A good comment reads like “aws_db_instance.main is being replaced because engine_version crossed a major boundary — pin it to 15.x to avoid the replacement, or confirm the maintenance window if intentional.” That’s something an author acts on. “There are 3 high-risk changes” is something they scroll past.

Keep the bot honest about its own limits too. Instruct it to say “I can’t determine X from the plan alone” rather than guessing — a review bot that occasionally admits uncertainty is one people keep trusting, and one that confidently hallucinates risk is one they mute within a week.

Tuning it over time

The first version will be noisy. Iterate the prompt — not the architecture — based on what it over-flags and what it misses. I keep the bot’s prompt versioned in the prompt library so changes are reviewable like code, and the Terraform prompt pack includes a PR-review-bot prompt with the three-section format and the human-approval footer already wired in. Whichever model you back it with — point your Claude AI or GitHub Copilot integration at the review step — the architecture is identical, because the model is just a function over JSON.

Conclusion

An AI Terraform review bot earns its place by doing the boring first read on every PR — catching the replacement on line 612 before a human even opens the diff. The design that makes it safe is dead simple: split plan from review, give credentials only to the credential-less-by-design plan job, and let the AI job do nothing but read JSON and write comments. The bot reviews tirelessly; humans approve every apply; and the architecture guarantees the model can’t touch your infrastructure even if it wanted to. More CI patterns are in the Terraform category.

Free download · 368-page PDF

Download the Free 500-Prompt DevOps AI Toolkit

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.