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

Enforcing Terraform Standards With TFLint and AI-Authored Rules

Use TFLint to enforce Terraform conventions and catch provider-specific errors, with AI drafting config and lint rules that a human reviews before they land.

  • #terraform
  • #tflint
  • #linting
  • #standards
  • #ai

The first time I inherited a Terraform monorepo from another team, terraform validate passed clean on every module — and the apply still blew up on a deprecated interpolation, an instance type that doesn’t exist in the target region, and a resource named thing2. Validate only checks that your HCL parses and that types line up. It has no opinion about correctness against the real AWS API, no opinion about whether your naming is consistent, and no idea that you left three variables declared but unused. That gap is exactly where TFLint lives, and it’s become the linter I reach for before any Terraform code merges.

What TFLint Is (and Isn’t)

TFLint is a pluggable linter for Terraform. It catches three classes of problems that terraform validate misses: possible errors (invalid instance types, references to nonexistent resources), deprecated syntax, and style/convention violations. With a provider plugin enabled, it also does deep checking — validating values against what the cloud provider actually accepts.

What it is not is a security scanner. This trips people up constantly, so let me be explicit: TFLint is about correctness and conventions. Checkov and tfsec (now Trivy) are about security posture — open security groups, unencrypted buckets, public access. They’re complementary, not competing. Run TFLint to keep your code consistent and free of provider errors; run a security scanner to keep your infrastructure safe. Wiring both into the same review gate gives you correctness and security on every pull request.

Installing TFLint

The fastest path on macOS or Linux:

# macOS
brew install tflint

# Linux / CI
curl -s https://raw.githubusercontent.com/terraform-linters/tflint/master/install_linux.sh | bash

# Verify
tflint --version

Pin the version in CI rather than tracking latest, so a new release can’t silently change which rules fire.

The .tflint.hcl Config

TFLint reads a .tflint.hcl file from the directory you run it in (or a parent). This is where you declare plugins and turn rules on and off. Here’s a realistic starting config:

tflint {
  required_version = ">= 0.50"
}

config {
  call_module_type = "local"
  force            = false
}

plugin "terraform" {
  enabled = true
  preset  = "recommended"
}

plugin "aws" {
  enabled = true
  version = "0.35.0"
  source  = "github.com/terraform-linters/tflint-ruleset-aws"
}

The bundled terraform plugin ships with the core ruleset (naming, deprecation, unused declarations). The aws plugin is a separate ruleset you pull from GitHub — that’s what unlocks deep checking against the AWS provider schema. There are equivalent rulesets for Azure (tflint-ruleset-azurerm) and Google Cloud.

Initialize and Run

Plugins declared in config aren’t downloaded automatically. Run init first:

tflint --init

Then lint. For a single directory, just tflint. For a tree of modules, use recursive mode:

tflint --recursive

When something’s wrong, the output is specific and actionable:

$ tflint --recursive

modules/ec2/main.tf:14:21: Error: "t2.mikro" is an invalid value as instance_type (aws_instance_invalid_type)
modules/ec2/variables.tf:8:1: Warning: variable "ami_id" is declared but not used (terraform_unused_declarations)
modules/ec2/main.tf:3:24: Warning: Interpolation-only expression is deprecated in Terraform v0.12.14+ (terraform_deprecated_interpolation)

3 issue(s) found:

That first one — t2.mikro — is the deep check doing its job. terraform validate would happily accept it as a string and only fail at plan or apply time against the API. TFLint catches it locally in milliseconds.

Naming Conventions and Useful Core Rules

Conventions are where teams drift hardest. The terraform_naming_convention rule enforces a consistent format across resources, variables, outputs, and more:

rule "terraform_naming_convention" {
  enabled = true
  format  = "snake_case"
}

You can go beyond a single format and set per-block-type patterns — for example, requiring resource names to match snake_case but enforcing a custom regex on variables. A few other core rules I always enable explicitly:

rule "terraform_deprecated_interpolation" {
  enabled = true
}

rule "terraform_unused_declarations" {
  enabled = true
}

rule "terraform_documented_variables" {
  enabled = true
}

rule "terraform_typed_variables" {
  enabled = true
}

terraform_deprecated_interpolation flags the old "${var.foo}" wrapping that should just be var.foo. terraform_unused_declarations catches the dead variables and locals that accumulate in every long-lived module.

Pro Tip: Run tflint --recursive in your pre-commit hook, not just CI. Catching a snake_case violation before it leaves your laptop is far cheaper than a failed pipeline and a “fix lint” commit.

Where AI Fits — Drafting Config and Rule Logic

Writing a comprehensive .tflint.hcl, tuning regex patterns for naming, and especially authoring a custom ruleset (TFLint supports custom rulesets written in Go, or simpler ones via the framework) is exactly the kind of fiddly, syntax-heavy work where an AI assistant earns its keep. I treat the model as a fast junior engineer: it drafts quickly and knows the syntax, but it does not get the last word.

A prompt I actually use, pasted into Claude or Cursor:

“Here’s our current .tflint.hcl and three example modules. Write naming-convention rules that require resources and variables in snake_case, enforce that every variable has a description, and enable the AWS ruleset’s deep checking. Output valid .tflint.hcl syntax only, and explain each rule you add.”

The model returns a config in seconds. Then the human work begins: I read every rule, confirm the HCL is actually valid (LLMs occasionally invent plausible-looking block names), run tflint --init && tflint --recursive against real modules, and check that nothing fires on code we consider correct. For custom-rule logic, the AI drafts the Go matcher; I review it like any other code before it ships. Reusable versions of these prompts live in our prompt library and the curated prompt packs.

Pro Tip: Have the AI generate a few intentionally-broken Terraform snippets alongside the rule, then confirm TFLint flags each one. A rule you haven’t seen fail is a rule you haven’t tested.

The hard boundary, every time: the AI drafts config and rule logic only. It never gets state-write access, never gets cloud credentials, and never auto-applies anything. A human reviews every plan and every rule before it lands. TFLint is read-only by design — it inspects HCL — which makes it a safe place to let an assistant move fast. Keep it that way.

Wiring It Into CI

Once the config is reviewed and committed, enforce it on every pull request. A GitHub Actions step:

- name: Setup TFLint
  uses: terraform-linters/setup-tflint@v4
  with:
    tflint_version: v0.52.0

- name: Init TFLint plugins
  run: tflint --init
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Run TFLint
  run: tflint --recursive --format compact

The GITHUB_TOKEN only raises the API rate limit for downloading the ruleset plugin — it grants no cloud access. With --recursive, every module in the repo gets linted, and a violation fails the check before review. Pair this gate with your code review dashboard so correctness, convention, and human judgment all sign off together.

Conclusion

TFLint closes the gap between “the HCL parses” and “this will actually work and matches our standards.” Let it own correctness and conventions; let a security scanner own posture. And when it comes to authoring the config or a custom ruleset, lean on AI to draft fast — then review every rule, test it against real modules, and keep credentials and state firmly out of the model’s reach. Fast junior engineer, human in the loop. That’s the whole pattern. Start by browsing more Terraform guides and pinning a .tflint.hcl into your next module today.

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.