This is a custom TFLint ruleset focused on readable, consistent Terraform code, targeted for Stegra Terraform projects. It currently provides:
stegra_newline_after_keywords: Enforces a blank line after configured attributes (e.g.,count,for_each,source) only when another item follows in the same block. Auto-fix inserts the missing blank line.stegra_depends_on_last: Requiresdepends_onto be the last item (attribute or block) in resource/data/module blocks and to have a blank line above when there are prior items. Auto-fix movesdepends_onto the end and inserts the blank line as needed.stegra_no_type_in_name: Prevents repeating type tokens from the resource/data type in the name (e.g.,aws_security_group_ruleshould not be namedmy_security_group_rule). Allows the tokenmainin both type and name.stegra_provider_configuration_locations: Allows provider configuration blocks only in specified directories.stegra_no_multiple_blank_lines: Disallows multiple consecutive blank lines between content. Auto-fix removes extras and keeps a single blank line.stegra_no_leading_trailing_blank_lines: Disallows leading blank lines and trailing blank lines at EOF. Auto-fix removes leading blanks and trims trailing blanks while preserving exactly one final newline.stegra_no_block_edge_blank_lines: Disallows leading and trailing blank lines inside any HCL block (resource, data, module, provider, nested blocks). Auto-fix removes the interior edge blank lines.stegra_blank_line_between_blocks: Requires a blank line between any consecutive top-levelresource/data/moduleblocks. If comments appear immediately before the next block, the blank line is inserted before the first comment so the comments remain attached to that block. Auto-fix inserts missing blank lines.stegra_keywords_first: Ensures configured attributes appear first in the order listed inkeywords(supportsresource,data, andmoduleblocks). Auto-fix reorders items.stegra_no_this_resource_name: Forbids using the resource namethis. Auto-fix renames tomainand updates<type>.thistraversals in expressions to<type>.main(strings/comments are left untouched).stegra_empty_block_one_line: Enforces that empty blocks use single-line form{}. Auto-fix collapses two-line empty blocks.stegra_no_blank_lines_in_required_providers: Disallows blank lines anywhere insideterraform→required_providers. Auto-fix removes only the empty lines (keeps comments).stegra_provider_version_ownership: Requires reusable modules to omit provider versions and runnable roots to declare exact versions for their complete local-module dependency graph, keeping provider-version ownership next to each root lockfile.
- TFLint v0.46+
- Go v1.25 (only needed for local development)
Install via GitHub releases (recommended):
- In your Terraform project, create
.tflint.hcl:
plugin "stegra" {
enabled = true
source = "github.com/stegraab/tflint-ruleset-stegra"
version = "0.1.0"
}- Download the plugin binary referenced by your version:
tflint --init
- Run TFLint:
tflint
| Name | Description | Severity | Enabled | Auto-fix |
|---|---|---|---|---|
| stegra_newline_after_keywords | Enforces a blank line after selected attributes when followed by more items | ERROR | ✔ | Insert blank line |
| stegra_depends_on_last | Requires depends_on last with a blank line above when needed | ERROR | ✔ | Move depends_on to end + insert |
| stegra_no_type_in_name | Prevents repeating type tokens in resource/data names (allows token main) |
ERROR | ✔ | N/A |
| stegra_provider_configuration_locations | Allows provider blocks only in specified directories | ERROR | ✔ | N/A |
| stegra_no_multiple_blank_lines | Disallows multiple consecutive blank lines between content | ERROR | ✔ | Remove extras (collapse to one) |
| stegra_no_leading_trailing_blank_lines | Disallows leading/trailing blank lines | ERROR | ✔ | Remove leading/trailing; keep 1 EOF newline |
| stegra_no_block_edge_blank_lines | Disallows leading/trailing blank lines inside any block | ERROR | ✔ | Remove interior edge blanks |
| stegra_blank_line_between_blocks | Requires a blank line between top-level resource/data/module blocks | ERROR | ✔ | Insert blank line |
| stegra_keywords_first | Configured attributes must appear first in the order listed | ERROR | ✔ | Reorder items |
| stegra_no_this_resource_name | Forbids resource name this |
ERROR | ✔ | Rename to main + update expression refs |
| stegra_empty_block_one_line | Enforces single-line {} for empty blocks |
ERROR | ✔ | Collapse to {} |
| stegra_no_blank_lines_in_required_providers | Disallows blank lines anywhere in required_providers | ERROR | ✔ | Remove blank lines |
| stegra_provider_version_ownership | Keeps module providers versionless and requires exact transitive versions in roots | ERROR | Configurable | N/A |
These rules support tflint's auto-fixer. Run with:
tflint --fix
-
stegra_no_multiple_blank_lines
- Bad:
resource "aws_vpc" "a" {} resource "aws_vpc" "b" {}
- Fixed:
resource "aws_vpc" "a" {} resource "aws_vpc" "b" {}
- Bad:
-
stegra_no_leading_trailing_blank_lines
- Leading blank line (bad):
resource "aws_vpc" "a" {}
- Fixed:
resource "aws_vpc" "a" {}
- Trailing blanks (bad):
resource "aws_vpc" "a" {}
- Fixed (keeps a single EOF newline):
resource "aws_vpc" "a" {}
- Leading blank line (bad):
-
stegra_no_block_edge_blank_lines
- Bad:
module "vpc" { source = "./vpc" }
- Fixed:
module "vpc" { source = "./vpc" }
- Bad:
-
stegra_blank_line_between_blocks
- Bad:
resource "null_resource" "a" {} resource "null_resource" "b" {}
- Fixed:
resource "null_resource" "a" {} resource "null_resource" "b" {}
- With comments: blank line is inserted before the comment so comments stay attached to the next block
Fixed to:
resource "null_resource" "a" {} # comment about next block resource "null_resource" "b" {}
resource "null_resource" "a" {} # comment about next block resource "null_resource" "b" {}
- Note: Applies only to top-level blocks, not nested blocks inside a resource/module
- Bad:
-
stegra_keywords_first
- Bad (non-target before targets):
resource "aws_vpc" "a" { name = "a" for_each = [] }
- Fixed (targets first in configured order):
resource "aws_vpc" "a" { for_each = [] name = "a" }
- Bad (non-target before targets):
-
stegra_newline_after_keywords (only if more items follow)
- Bad (keyword not followed by a blank line and more items follow):
module "mod" { source = "./module" name = "value" }
- Fixed:
module "mod" { source = "./module" name = "value" }
- No issue when keyword is last:
module "mod" { source = "./module" }
- Exception in module blocks:
sourcefollowed immediately byversiondoes not require a blank linemodule "mod" { source = "./module" version = "~> 1.0" }
- Bad (keyword not followed by a blank line and more items follow):
-
stegra_no_this_resource_name
- Bad:
resource "aws_s3_bucket" "this" {} resource "aws_iam_role" "r" { name = aws_s3_bucket.this.id }
- Fixed (resource renamed and expression reference updated):
resource "aws_s3_bucket" "main" {} resource "aws_iam_role" "r" { name = aws_s3_bucket.main.id }
- Note: References are updated only in expressions (not in plain strings/comments).
- Bad:
-
stegra_no_blank_lines_in_required_providers
- Bad:
terraform { required_providers { aws = { source = "hashicorp/aws" version = "6.20.0" } gitlab = { source = "gitlabhq/gitlab" version = "18.5.0" } } }
- Fixed (blank line removed, comments would be preserved):
terraform { required_providers { aws = { source = "hashicorp/aws" version = "6.20.0" } gitlab = { source = "gitlabhq/gitlab" version = "18.5.0" } } }
- Bad:
-
stegra_empty_block_one_line
- Bad:
resource "random_uuid" "x" { }
- Fixed:
resource "random_uuid" "x" {}
- Bad:
You must configure some rules using .tflint.hcl rule blocks.
- stegra_newline_after_keywords
- Required option:
keywords(list of strings) - If
keywordsis not set, the rule returns an error. - Example:
- Required option:
rule "stegra_newline_after_keywords" {
enabled = false
keywords = ["for_each", "count", "source"]
}- stegra_provider_configuration_locations
- Required option:
allowed_directories(list of path prefixes relative to repo root) - Example:
- Required option:
rule "stegra_provider_configuration_locations" {
enabled = true
allowed_directories = ["environments"]
}- stegra_provider_version_ownership
- Required options:
root_directoriesandmodule_directories(path prefixes relative to the repository root) - Runnable roots must use one exact provider version, while reusable modules must omit the
versionattribute. - The rule follows nested local module sources (
./and../) and requires the root to declare every provider used transitively by those modules. - Lockfile selection is outside TFLint's configuration model, so a separate CI check is still needed to verify that
.terraform.lock.hclselects the declared versions. - Example:
- Required options:
rule "stegra_provider_version_ownership" {
enabled = true
root_directories = ["environments"]
module_directories = ["modules"]
}Failing reusable module:
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "6.51.0"
}
}
}Passing reusable module and root workspace:
# modules/example/provider.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
}
}
}
# environments/prod/example/provider.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "6.51.0"
}
}
}- stegra_keywords_first
- Required option:
keywords(ordered list of attribute names to appear first) - The rule enforces the exact order listed in
keywordswhen those attributes are present in a block - Applies to
resource,data, andmoduleblocks - Example:
- Required option:
rule "stegra_keywords_first" {
enabled = true
keywords = ["provider", "for_each", "count", "source"]
}-
Run tests
make test -
Build the plugin
make build -
Install locally into
~/.tflint.d/plugins/make install
Then use .tflint.hcl as shown in Installation and run tflint. The Makefile uses a local GOCACHE for tests to work in restricted environments.