TerraTidy is a single-binary quality platform for Terraform and Terragrunt that provides:
- Formatting -- Format
.tfand.hclfiles using the HCL formatter - Style Checking -- Custom style rules for layout, ordering, and conventions
- Linting -- 11 built-in AST rules plus optional TFLint integration for provider-specific checks
- Policy Enforcement -- OPA policy checks for compliance
- Single Binary -- No external dependencies for core functionality
- Library-first Architecture -- Uses Go libraries (hclwrite, OPA SDK) directly instead of shelling out
- Extensible -- Custom rules in Go, YAML, Rego, or Bash
- Modular Config -- Split large configs into organized files with glob imports
- Auto-fix -- Automatically fix formatting and style issues
- Suppression Annotations -- Inline comments to suppress specific findings per-line or per-block
- Multiple Output Formats -- Text, table, JSON, SARIF, HTML, JUnit, Markdown, GitHub Actions annotations
- Multi-platform -- Linux, macOS, Windows (amd64 and arm64)
brew tap santosr2/tap https://github.com/santosr2/TerraTidy
brew install santosr2/tap/terratidyDownload the latest release for your platform from GitHub Releases.
docker pull ghcr.io/santosr2/terratidy:latest
# Pin to a specific version in CI
docker pull ghcr.io/santosr2/terratidy:v0.2.0-alpha.4
docker run --rm -v $(pwd):/app ghcr.io/santosr2/terratidy check# Use explicit version until v0.2.0 stable (see note below)
go install github.com/santosr2/TerraTidy/cmd/terratidy@v0.2.0-alpha.4Note: Avoid
@latestuntil v0.2.0 is released. Due to a repository rename after v0.1.0,@latestresolves to v0.1.0 which has a broken module path. See the deprecation notice for details.
cd your-terraform-project
terratidy init --interactiveThis creates a .terratidy.yaml configuration file with recommended settings.
terratidy checkExample output (sequential mode, the default):
Checking 3 files...
1. Checking formatting...
Found 1 issue(s)
2. Checking style...
Found 2 issue(s)
3. Running linter...
Found 1 issue(s)
4. Running policy checks...
Found 0 issue(s)
✗ modules/networking/main.tf:0:0: File needs formatting (fmt.needs-formatting)
⚠ modules/networking/main.tf:12:1: Missing blank line between blocks (style.blank-line-between-blocks)
⚠ modules/networking/variables.tf:5:1: Missing blank line between blocks (style.blank-line-between-blocks)
⚠ modules/networking/main.tf:8:1: resource name 'public-subnet' should use snake_case (lint.terraform-naming-convention)
---
Summary: 4 total issue(s)
Errors: 1
Warnings: 3
Run individual commands for details:
terratidy fmt --check
terratidy style
terratidy lint
terratidy policy
With --parallel (-p), the output is more compact:
Checking 3 files...
Running checks in parallel mode...
fmt: 1 issue(s)
style: 2 issue(s)
lint: 1 issue(s)
✗ modules/networking/main.tf:0:0: File needs formatting (fmt.needs-formatting)
⚠ modules/networking/main.tf:12:1: Missing blank line between blocks (style.blank-line-between-blocks)
⚠ modules/networking/variables.tf:5:1: Missing blank line between blocks (style.blank-line-between-blocks)
⚠ modules/networking/main.tf:8:1: resource name 'public-subnet' should use snake_case (lint.terraform-naming-convention)
---
Summary: 4 total issue(s)
Errors: 1
Warnings: 3
Run individual commands for details:
terratidy fmt --check
terratidy style
terratidy lint
terratidy policy
terratidy fix| Command | Description |
|---|---|
terratidy check |
Run all checks (recommended for CI) |
terratidy fix |
Auto-fix all fixable issues |
terratidy fmt |
Format files |
terratidy style |
Check/fix style issues |
terratidy lint |
Run linting |
terratidy policy |
Run policy checks |
terratidy init |
Initialize configuration |
terratidy dev |
Development mode with file watching |
terratidy lsp |
Start the Language Server Protocol server |
terratidy init-rule |
Initialize a new custom rule |
terratidy test-rule |
Test a specific rule |
terratidy plugins |
Plugin management commands |
terratidy config |
Configuration management commands |
terratidy rules list |
List available rules |
terratidy rules docs |
Generate markdown documentation |
terratidy version |
Show version info |
These flags apply to all commands:
| Flag | Description |
|---|---|
--config |
Path to config file (default: .terratidy.yaml) |
--profile |
Configuration profile to use |
--format |
Output format: text, table, json, json-compact, sarif, html, junit, markdown, github |
--changed |
Only check files changed in git |
--no-recurse |
Disable recursive directory traversal |
--exclude |
Glob patterns to exclude (repeatable or comma-separated) |
--severity-threshold |
Minimum severity to fail: info, warning, error |
--color |
Enable colored output (default: true) |
--absolute-paths |
Output absolute file paths instead of relative |
These flags are specific to terratidy check:
| Flag | Description |
|---|---|
--parallel / -p |
Run engines in parallel |
--skip-fmt |
Skip formatting checks |
--skip-style |
Skip style checks |
--skip-lint |
Skip linting checks |
--skip-policy |
Skip policy checks |
# .terratidy.yaml
version: 1
engines:
fmt: { enabled: true }
style: { enabled: true }
lint: { enabled: true }
policy: { enabled: false }
severity_threshold: warningFor larger projects, split configuration into organized files:
# .terratidy.yaml
version: 1
imports:
- .terratidy/rules/*.yaml
- .terratidy/profiles/default.yaml
severity_threshold: warningSee the Configuration Guide for details.
| Method | When | Best For |
|---|---|---|
| CLI | Manual runs | Local development, scripting |
| Pre-commit | On git commit | Catching issues before push |
| GitHub Actions | On PR/push | CI/CD quality gates |
| LSP / VS Code | Real-time in editor | Instant feedback while coding |
| Docker | Isolated environments | CI pipelines without Go installed |
Add to .pre-commit-config.yaml:
repos:
- repo: https://github.com/santosr2/TerraTidy
rev: v0.2.0-alpha.4
hooks:
- id: terratidy-checkAvailable hook IDs: terratidy-fmt, terratidy-fmt-check, terratidy-style, terratidy-style-fix, terratidy-lint, terratidy-check, terratidy-fix, terratidy-policy.
- name: Run TerraTidy
uses: santosr2/terratidy@v0
with:
format: sarif
parallel: 'true'
github-token: ${{ secrets.GITHUB_TOKEN }}Pin to a specific release for reproducible builds: santosr2/terratidy@v0.2.0-alpha.4
Available inputs: version, config, profile, format, parallel, working-directory,
skip-fmt, skip-style, skip-lint, skip-policy, exclude, no-recurse, absolute-paths,
changed, severity-threshold, fail-on-error, fail-on-warning, github-token.
The TerraTidy VS Code extension provides real-time diagnostics via LSP. Install from the VS Code Marketplace or see vscode/README.md for full installation instructions.
Create custom rules in three formats:
package main
import (
"github.com/hashicorp/hcl/v2"
"github.com/santosr2/TerraTidy/pkg/sdk"
)
type MyRule struct{}
func (r *MyRule) Name() string { return "my-rule" }
func (r *MyRule) Description() string { return "Checks something" }
func (r *MyRule) Check(ctx *sdk.Context, file *hcl.File) ([]sdk.Finding, error) {
var findings []sdk.Finding
// Full HCL AST access via file.Body
return findings, nil
}name: require-description
description: Resources must have a description attribute
severity: warning
enabled: true
message: "Resource is missing a description attribute"
patterns:
resource_types:
- aws_instance
- aws_s3_bucket
required_attributes:
- description#!/usr/bin/env bash
# Output JSON findings to stdoutSee the Custom Rules Guide for details.
Full documentation is available at docs/site/docs/.
git clone https://github.com/santosr2/TerraTidy
cd terratidy
mise install # Install Go 1.26 and tools
mise run setup # Download and tidy Go modules
mise run build # Build binarymise run test # Unit tests
mise run test:integration # Integration tests
mise run lint # Run linters
mise run check # All quality checks (fmt, vet, lint, test)Contributions are welcome. Please read CONTRIBUTING.md for details.
MIT License -- see LICENSE for details.
Built with:
- HashiCorp HCL for parsing
- TFLint for optional provider-specific linting (invoked as subprocess)
- Open Policy Agent for policies
- Cobra for CLI