modules.tf

In testing. modulestf is early and may contain errors or bugs. Found one, or disagree with a finding? Open an issue on GitHub. We want to hear it.

Unknown is never a pass

modulestf reviews code your agents write against your project's profile and scope.

It reviews Terraform modules. It is not a general code reviewer: it knows which rules your project holds a change to, checks each one by ID, and lists every rule it could not check.

Read this reviewInstall the skills

Profiles shipped: 1 Apache-2.0

  1. RequestPull request open, review asked for
  2. Scope checkscope.md decides what belongs
  3. FindingsSeverity, rule, location, and a fix
  4. CommentOne verdict you read and act on
  5. RevisionYou push and ask again
  6. Updated listFixed, still open, new, and a new verdict

One review, start to finish

The findings below come from a real run on an AWS Backup module, reviewed against the terraform-aws-modules profile. That run covered a whole repository; a pull request review has the same shape and is usually shorter.

Request

Reads: the pull request reference. Never: the pull request text as instructions.

A pull request is open and you ask your agent for a review.

Review owner/repo#123 with the terraform-module-pr-review skill.

It runs as agent skills in your own coding agent, with your own GitHub login.

Scope check

Reads: scope.md from the profile. Decides, for each addition, whether it belongs in the module at all.

Does each addition belong in the module? Open a row to see why and what to do.

  • Out of scope.checkov.yml, .trivyignore

    New scanner configuration. The profile carries no scanner beyond its pre-commit hooks and five workflows.

    Fix Remove both files, or keep them in your own CI repository.

  • Out of scopetests/e2e/*_test.go

    New Go test files. The profile carries no Go tests.

    Fix Remove them from the module, or propose the test layer to the maintainers first.

  • In scopeiam.tf adds aws_iam_role

    An IAM role is an adjunct type on the profile's closed list, so it may sit beside the service. It still needs something that uses it.

  • Maintainer decisionan IAM role that nothing uses

    An adjunct is exempt from the boundary, not from the edge rule. Whether the role stays is for a maintainer to call.

    Fix Reference the role from a resource, or remove it. Either closes the question.

  • Not evaluateda check the review could not run

    A rule that cannot be checked is listed, with the reason. It is an unknown and never counts as a pass.

Out of scope is correctable. Each finding says what to change, and the next round confirms it.

Findings

Reads: the profile's rules, each with a stable ID. Every finding carries a severity, a location, and a fix.

Findings come back grouped by severity, the way a code review does.

  • HIGHscope.tooling2

    .checkov.yml, .trivyignore - new scanner configuration on the profile's closed list.

    Fix Delete both files from the module.

  • HIGHscope.layout28

    .github/CODEOWNERS, .tflint.hcl, tests/setup/*/main.tf and 25 more - files outside the profile's layout table.

    Fix Remove the files the layout table does not admit. The table is in the profile.

  • HIGHprofile.release-config-changed1

    .github/workflows/release.yml:20 - the release job runs only for the previous repository owner, so no release is cut once the module moves.

    Fix Restore the owner check from the template: terraform-aws-modules.

  • HIGHprofile.region-not-allowed1

    examples/cross-region-copy/main.tf:7 - region_secondary = "eu-west-3" chooses a region the profile does not allow.

    Fix Set region_secondary to "us-east-1", the one secondary region the profile allows.

  • MEDIUMprofile.required-file-missing1

    examples/basic - the profile's minimal example is missing; examples/simple fills its role under another name.

    Fix Rename examples/simple to examples/basic.

  • MEDIUMstructure.comment-unsourced5

    plans.tf:21 and four more - comments assert service behavior with no link to an official source and no date.

    Fix Add the documentation URL and the date to each comment, or remove the claim.

  • LOWprofile.template-drift2

    .gitignore - diverges from the profile template with no stated reason.

    Fix Start from the template and keep only the additions you can explain.

Excerpt of a real run. Fix text on this page is written for the page from each finding.

Comment

Writes: one verdict, computed by fixed rules from the findings and the pull request's checks and threads. Nothing is posted until you confirm.

You get one verdict, in plain words.

Changes requested before merge.

HIGH findings are open. 4 HIGH, 2 MEDIUM, 1 LOW shown here; the full run listed 84.

The verdict names the rule that produced it. A check that could not run makes it Inconclusive, unless a CRITICAL or HIGH finding, a failing check, a merge conflict or a review that requests changes already decides it.

Revision

Reads: the new head. Runs the same rules again and compares with the last list.

You fix what it found, push, and ask for the review again.

  • HIGHscope.toolingfixed
  • HIGHscope.layoutfixed
  • HIGHprofile.release-config-changedfixed
  • HIGHprofile.region-not-allowedfixed
  • MEDIUMprofile.required-file-missingfixed
  • MEDIUMstructure.comment-unsourcedfixed
  • LOWprofile.template-driftopen
  • LOWstructure.argument-ordernew

Illustrative second round. A finding can be fixed, stay open, or appear because of the new code.

Updated list

Writes: a new verdict on the new head.

The list shrinks, and the verdict follows it.

Good to merge, with nits.

6 fixed, 1 new, 2 left at LOW. Merging is still your decision.

Not a generic code reviewer

A general-purpose review tool brings its own idea of good code. modulestf brings yours, written down.

QuestionGeneral-purpose review tool (typical)modulestf
Where does the standard come from?The model's own sense of qualityRules in your profile and scope.md, each with an ID
Does the addition belong at all?Reviews what is in the diffChecks each addition: in scope, out of scope, or a maintainer decision
A rule it could not checkSilence reads as fineListed as not evaluated; it never counts as a pass
What do you get?Free-form commentsOne verdict, rule ID, location, a fix, evidence
Text inside the changeRead as contextUntrusted data; it never clears a rule

The middle column describes the category, not a named product.

What it checks against

Every finding cites a rule. Some of them:

RuleChecksFrom
scope.layoutFiles and folders belong in a module of this familyscope.md
scope.testsTest additions the profile does not acceptscope.md
profile.region-not-allowedExamples use only regions the profile allowsprofile
structure.comment-unsourcedComments that assert service behavior link a source and a dateprofile
coverage.attribute-not-outputResource attributes surface as outputsprofile

Install

The skills are plain files that live side by side. Copy them where your agent loads skills.

git clone https://github.com/modulestf/modulestf

# Claude Code
mkdir -p ~/.claude/skills
ln -sfn "$PWD"/modulestf/.agents/skills/* ~/.claude/skills/

# Codex
mkdir -p ~/.agents/skills
ln -sfn "$PWD"/modulestf/.agents/skills/* ~/.agents/skills/

They also need HashiCorp's Terraform MCP server, added to your agent under the name terraform, plus gh logged in as you, git, jq and the terraform command line. The README has the details and what each task needs.

Apache-2.0. Runs entirely without compliance.tf; profiles are plain files in your repository.

What modulestf will never do

Profiles

Profiles shipped: 1

A profile is the written-down standard a review checks against. The one that ships is terraform-aws-modules, and it is made of the actual rules we use to review pull requests for terraform-aws-modules.

It is the maintainer's review practice, written down. It is not a policy document published by the terraform-aws-modules organization, and it changes as the review practice does. Rules in it are not guesses by the model; the model applies them.

A profile is plain files. There are no other ready profiles yet. To review your own modules to your own rules, start from the shape of this one, as its PROFILE.md describes.

Read the terraform-aws-modules profile

Limits today

Disagree with a finding? Open an issue with the rule ID and your reason.

FAQ

Does it block merges?
Only if you post its review as a request for changes and your branch protection requires reviews. It never merges, and it posts nothing until you confirm.
Does it need my cloud credentials?
No. Validation runs without credentials. A plan pass runs only when you name an AWS profile and a read-only review role.
Who pays for the model?
You do: the skills run in the coding agent you already use.
Can I run it without a hosted service?
There is no hosted service. The skills run in your agent on your machine.
What happens when it is wrong?
Open an issue on GitHub with the rule ID. It is in testing and will be wrong sometimes. Nothing is posted until you confirm, so a review you disagree with never has to reach the pull request.
Does it follow instructions in pull request text?
No. Pull request text is data under review. It never clears a rule.

Coming

Some of these may come, and some may not. There are no guarantees and no dates: a hosted GitHub App, a CLI, an MCP server, SARIF export, organization standards, more profiles. Treat none of them as a promise.

Follow the discussion if you want to know when one of them exists. Nothing else is collected.

Who makes this

modulestf is created and maintained by Anton Babenko, maintainer of terraform-aws-modules and terraform-skill, and creator of compliance.tf.

terraform-skill helps agents write modules; modulestf reviews what they write.

Tell us what is wrong

modulestf is in testing. It may produce wrong findings, miss things, or fail in ways we have not seen. Every report helps.