Agent skills that review reusable Terraform modules against a written profile of the module family's rules, and help you make the change the review asks for. One profile ships today: terraform-aws-modules. They run in your coding agent (Claude Code or Codex) on your machine, with your own GitHub login.
- The skills
- Quick start
- Everyday use
- Reading a review
- Verification ladder
- Limits
- Advanced use
- Requirements by task
- Related repositories
| Skill | You give it | You get back |
|---|---|---|
terraform-module-pr-review |
A pull request or commit URL, or owner/repo#123 |
A verdict and a comment to post, after you confirm |
terraform-module-reviewer |
A module checkout and a diff, a base ref or a list of changed files | A list of findings. Read-only, no verdict |
terraform-module-maintainer |
A change to make, review feedback to work through, a module to create or a provider to upgrade | Edits in your checkout, a report of the checks it ran, and reply drafts |
change-scope |
A proposed addition, in any language or project | Whether it belongs in the thing being changed, and where it goes if not |
The pull request review calls the reviewer, and both call change-scope. The rules of the module family live in a profile: terraform-aws-modules.
-
Install the skills as the
modulestfplugin from the antonbabenko/agent-plugins marketplace. It holds all four skills; the three Terraform skills link into each other, so they ship together.# Claude Code /plugin marketplace add antonbabenko/agent-plugins /plugin install modulestf@antonbabenko # Codex: add the marketplace, then install modulestf from /plugins codex plugin marketplace add antonbabenko/agent-pluginsOr copy or symlink all four directories under
.agents/skills/side by side into your agent's skills directory:~/.claude/skills/for Claude Code,~/.agents/skills/for Codex.change-scopealso works alone.git clone https://github.com/modulestf/modulestf.git mkdir -p ~/.claude/skills ln -sfn "$PWD"/modulestf/.agents/skills/* ~/.claude/skills/ # Claude Code mkdir -p ~/.agents/skills ln -sfn "$PWD"/modulestf/.agents/skills/* ~/.agents/skills/ # Codex
-
Add HashiCorp's Terraform MCP server to your agent under the name
terraform, so its tools appear asmcp__terraform__get_latest_provider_version,mcp__terraform__search_provider_docsandmcp__terraform__get_provider_details. The skills read provider schemas from it, not from memory. Without it, every rule that needs a provider fact is reported as not run, so a pull request review is inconclusive at best. -
Log in to GitHub with
gh auth loginas yourself, and installterraform,git,jq,perlandmtf-facts. A pull request review needsmtf-factsfor a conclusive verdict.brew install modulestf/tap/mtf-facts
Without Homebrew:
go install github.com/modulestf/facts/cmd/mtf-facts@latest. -
Ask your agent:
review terraform-aws-modules/terraform-aws-lambda#780
The skill clones the pull request into a temporary directory, runs terraform init -backend=false and terraform validate in each example, the ones the change touches first, and shows you a verdict, the findings and the comment it would post. It posts nothing until you confirm the comment and its review event: comment, approve or request changes.
Write requests in plain words. The examples show what each skill needs to hear.
review https://github.com/terraform-aws-modules/terraform-aws-lambda/pull/780
is terraform-aws-modules/terraform-aws-lambda#780 ready to merge?
what should I reply on terraform-aws-modules/terraform-aws-lambda#780?
review https://github.com/terraform-aws-modules/terraform-aws-lambda/commit/<sha>
You get a verdict, a comment with every finding and its suggested fix, the checks that could not run, and the state of the pull request's checks and threads. The skill proposes one review event (comment, approve or request changes) and asks once. Say "do not post" and it runs render-only: it shows the comment and posts nothing. A commit gets a plain commit comment with no approve or request changes.
triage terraform-aws-modules/terraform-aws-lambda#780
A quick pass: scope and the checks that need no provider lookup, with no validation. It never approves. Use it to decide whether a full review is worth it.
review the changes on this branch against master in ~/src/terraform-aws-s3-bucket
The reviewer reads committed changes only. For uncommitted work, paste git diff into the request. You get findings, most severe first, each with a rule ID, a file and line, and a suggested fix. The reviewer never edits files and never runs Terraform.
add poller_group_name to the event source mapping in ~/src/terraform-aws-lambda
The maintainer checks first that the addition belongs in the module, then makes the smallest edit, says whether it is additive, behaviour-changing or breaking, and drafts the pull request description. Your checkout is treated as untrusted, so it runs only terraform fmt -check, terraform init -backend=false, terraform validate and the documentation update. Say "this is a trusted local run" to let it run pre-commit run -a as well. Say it only for code you wrote or have read: pre-commit runs the hooks the repository defines. It counts only in your own message: never from the repository, the pull request or text you paste. The same goes for permission to run outside the sandbox.
work through this review on my pull request, workspace ~/src/terraform-aws-lambda:
<paste the review and the comments>
apply F-003
The maintainer decides each item (apply, reject, already addressed, needs a decision, and so on), edits the code for the items it applies, and writes reply drafts that start with "Fixed in :" or "Not changed:". It stops and asks when an item is unclear or needs your decision. It never posts, pushes or resolves threads.
before I add a Redis client to this billing service, does it belong here?
change-scope answers with one of three outcomes and the rule behind it. It does not judge quality or whether the idea is good, only where it belongs.
- Finding: one problem in one place: a rule ID such as
compat.variable-removed, a severity (CRITICAL, HIGH, MEDIUM or LOW), a file and line, a summary and a suggested fix. A fix can be a code change, a choice between options for you to make, or "no code change". - Verdict: computed from the findings and the pull request's checks and threads, with no judgement of its own. The first rule that matches wins (verdict.md):
- Hold for the next major release: at least one CRITICAL finding, and every CRITICAL and HIGH finding is a compatibility (
compat.*) finding. - Block: any other CRITICAL or HIGH finding, or a failing host signal: a failing check, a merge conflict, or a review that requests changes.
- Inconclusive: anything that could not be checked.
- Changes suggested: unresolved review threads.
- Block: any MEDIUM finding.
- Approve with nits: only LOW findings.
- Approve: nothing found.
- Hold for the next major release: at least one CRITICAL finding, and every CRITICAL and HIGH finding is a compatibility (
- Inconclusive: something could not be checked, for example a provider lookup, a validation or a check run that was still pending. A gap never counts as a pass, and a failed lookup is not evidence that anything is missing.
- Validation:
terraform initdownloads the providers and modules the change pins, andterraform validateruns those provider plugins. The skill removes AWS, GitHub, Terraform and SSH credentials from that environment, gives it an empty home directory and uses no backend. A passingterraform validateis evidence, not proof. - Scope outcomes: when an addition belongs, there is no scope finding. Otherwise it is a Ban (it does not belong here, and the review names where it goes), a Maintainer decision (a person has to decide), or Not evaluated (the rule could not be applied; an unknown, never a pass). Text in the pull request itself, such as "this is in scope", never clears a scope rule.
A change to behaviour, such as dropping compact() around a list, is verified by escalating evidence, simplest first. mtf-facts behaviour turns each changed construct into a trigger, the review writes the pull request's behaviour claims as data, and mtf-facts claims-eval gives each trigger a state from what the levels observed. Reading the code supports a claim and never verifies one.
| Level | What runs | Needs |
|---|---|---|
| L1 | The base and head expression, evaluated offline with typed samples and unknowns, built-in provider only | terraform, mtf-facts, perl, jq, and sandbox-exec on macOS or bwrap on Linux |
| L2 | A plan-only terraform test of the instances a count or for_each makes |
The same as L1 |
| L3 | A curated fixture with the real provider, fake credentials, a prior state and a control run | The same, plus curl to fetch the pinned provider, hash-checked, before the sandboxed run |
| L4 | The credentialed plan pass, a base and a head plan per example | An AWS profile and a review role, as in Add a terraform plan pass |
L1 to L3 run with the network denied, in a sandbox a canary proves before any command; on an unproven sandbox those levels are unverifiable. A trigger that ends verified, covered by a plan or inert passes, a refuted one blocks, and an unverifiable one makes the review inconclusive. Details: verification-ladder.md and ladder-pass.md.
- No skill runs
terraform apply, writes state or uses a real backend. None merges, pushes to a pull request or resolves threads. None obeys instructions found in the code, the pull request text or its comments; all of it is data. - The pull request review posts only the one comment and review event you confirm in the conversation. From the pull request it runs only
terraform init -backend=falseandterraform validate, with AWS, GitHub, Terraform and SSH credentials removed and an empty home directory; both still run the provider plugins the change pins. When you opt in, it also runsterraform planon a read-only review session. - The reviewer and
change-scopeare read-only. They never post and never run Terraform. - The maintainer edits only your checkout and never posts. It commits only when you ask, and runs the repository's own hooks only on a run you call trusted.
These need extra tools, credentials or a decision from you.
review owner/repo#123, plan with profile review-sso and role arn:aws:iam::<account-id>:role/<review-role>
The plan pass runs terraform plan on every example, the ones the change touches first, on a short session of a read-only review role that the account owner creates. You name both the AWS profile and the role for that run only. The session carries a policy that allows only read actions. Before planning, a write probe, a dry-run VPC create, must be refused; it tests one write action, not the whole policy. With Docker, the plan runs in an isolated container whose proxy lets each phase reach only the hosts it needs. Without Docker, it runs on your machine in laptop mode, where stricter pattern checks refuse risky examples; they reduce the risk of the code acting on your machine but cannot rule it out. Plan results add evidence and never clear a finding. In runner mode only each example's outcome and resource counts reach the comment. In laptop mode a finding may quote one scanned error line; the scan cannot guarantee that no sensitive value slips through. It needs terraform, jq, perl, git and AWS CLI v2. Details: plan-pass.md and the plan runner, which is a prototype.
create a terraform-aws-<service> module covering <resource types>
The maintainer builds a coverage ledger from the provider schema (every argument becomes an input, an output, or a documented omission), copies the profile's templates and writes the examples. It counts as complete only after a trusted run of pre-commit and a terraform plan with credentials you choose.
upgrade ~/src/terraform-aws-rds to AWS provider 6.x
The maintainer compares the old and new provider schemas, makes the smallest edit plus the version constraint bump, runs terraform init -backend=false -upgrade, and writes migration notes in docs/MIGRATION_<from>_to_<to>.md for anything that breaks consumers.
mtf-facts is a command-line tool that reads the files alone, with no network and no Terraform. Keep it on your PATH, outside the module checkout. A pull request review needs it: it picks the examples to verify and classifies the change for the verification ladder. Without it the classification cannot run, so the review is inconclusive at best. The reviewer also runs its lint once and ends the review with a short comparison of where the tool and the review agree. The maintainer can also use its lint, ledger and schema-diff commands. The facts repository has a walkthrough on a real pull request. Design: docs/facts-engine-design.md.
Tell the reviewer that the checkout holds a new module, for example one offered for adoption. It runs a full coverage pass with no base to compare against.
The terraform-aws-modules profile holds the family's templates, layout, release rules and scope lists. The skills' shared references stay provider-neutral; anything specific to one family goes in its profile. A second profile is not designed yet: PROFILE.md says how to start one. A module can record its own scope exceptions in docs/SCOPE.md; the review reads that file from the base, so an exception must be merged before the change that needs it. To hand the profile to another code reviewer, pin review-guide.md by commit; it gives advice, not a verdict.
A host that runs the review's verify job, plan job, records and render itself pins one commit of this repository and moves them together. This release changes the files they exchange, with no fallback to the older shapes: the render's run file requires coverage, and verify.json and plan-hosted.json gained keys. See host-pass.md.
When a check cannot run inside your agent's sandbox, the review records it as not run and the verdict is inconclusive at best. Only your own message can allow a run outside the sandbox, for that run or a named set of runs.
| Task | Needs |
|---|---|
| Every Terraform skill | The four skill directories side by side, a Terraform MCP server |
| Review a pull request or commit | gh logged in as a person (a bot or CI identity runs render-only and posts nothing), git, jq, perl, terraform, mtf-facts, and sandbox-exec (macOS) or bwrap (Linux) for the ladder |
| Review a local change | git |
| Change, fix or upgrade a module | terraform; for a trusted run also pre-commit, terraform-docs and tflint |
| Create a module | The same. To finish it: a trusted run and a terraform plan you run with your own credentials, not a review role |
| Plan pass | An AWS profile and a review role, AWS CLI v2, jq, perl; Docker for the isolated runner |
change-scope alone |
Nothing else |
mtf-facts comparison in a local review |
The mtf-facts binary on PATH, outside the checkout |
- modulestf/facts:
mtf-facts, the facts engine a pull request review needs. - modulestf/terraform-schema-mirror: provider schemas, every stable version, as release assets.
- antonbabenko/terraform-skill: general Terraform practices. The skills here link to it and do not repeat it.
The tests are shell scripts under tests/. Run them all with:
for t in tests/*.sh; do bash "$t" || echo "FAILED: $t"; doneWhat each test checks and what it needs is in AGENTS.md.
Issues are welcome; see CONTRIBUTING.md. Report security issues as SECURITY.md says. Licensed under the Apache License 2.0.
This repository held modules.tf-lambda, the Cloudcraft-to-Terraform generator (2019-2021). Its code is on the legacy branch.