hclalign normalizes Terraform and other HCL files with a two‑phase pipeline that mirrors terraform fmt before reordering attributes for consistent alignment.
- fmt – detects the Terraform CLI with
exec.LookPath; if found it runsterraform fmt, otherwise a pure Go formatter is used. Newline and BOM hints are carried through and applied only when writing the result. - align – reorders attributes to match a configurable schema.
terraform fmt is run again after alignment to ensure canonical layout. This process is idempotent: running the tool multiple times yields the same result.
The Go formatter emits the same spacing, alignment, and comment layout as terraform fmt. Parity tests exercise fixtures covering comments, heredocs, CRLF line endings, and UTF-8 BOM files. When the Terraform CLI is unavailable, hclalign logs a warning and falls back to this Go formatter.
hclalign aligns attributes inside Terraform blocks. By default it processes only variable blocks and targets files matching the glob pattern **/*.tf while excluding .terraform/** and vendor/**.
Attributes are reordered inside these block types using canonical schemas:
- variable:
description,type,default,sensitive,nullable, then any other attributes followed byvalidationblocks - output:
description,value,sensitive,ephemeral,depends_on, then other attributes - locals: no reordering
- module:
source,version,providers,count,for_each,depends_on, then input variables alphabetically and other attributes - provider:
aliasfollowed by remaining attributes sorted alphabetically, then nested blocks in their original order - terraform:
required_version,required_providers(entries sorted alphabetically),backend,cloud, then other attributes and blocks - resource/data:
provider,count,for_each,depends_on,lifecycle,provisioner, then provider schema attributes grouped as required → optional → computed (each alphabetical), followed by any other attributes
Validation blocks are placed immediately after canonical attributes. Attributes not covered by a canonical list or provider schema keep their original order, except in provider blocks where they are sorted alphabetically after alias. Entries within required_providers are sorted alphabetically by provider name.
# before
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
}
aws = {
source = "hashicorp/aws"
}
}
}
# after
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
}
azurerm = {
source = "hashicorp/azurerm"
}
}
}Use --types to select which block types to align. --order customizes the variable schema and has no effect on other block types:
# align module and output blocks using their default order
hclalign . --types module,output
# override variable attribute order while still aligning modules with defaults
hclalign . --types variable,module --order value,description,type
# --order is ignored when variable blocks are not selected
hclalign . --types module --order value,description,typeResource and data blocks can be ordered according to provider schemas. Supply a
schema file via --providers-schema or let hclalign invoke terraform providers schema -json by passing --use-terraform-schema. When Terraform is
invoked, schemas are cached under --schema-cache (default
.terraform/schema-cache) using a key derived from the Terraform version,
provider versions, and module path. Disable caching with
--no-schema-cache. Unknown attributes keep their original order.
By default hclalign rewrites files in place. The following flags adjust this behavior:
--write: write result to files (default true)--check: exit with non‑zero status if changes are required--diff: print unified diff instead of writing files--follow-symlinks: follow symbolic links when searching for files--stdin,--stdout: read from stdin and/or write to stdout--include,--exclude: glob patterns controlling which files are processed (defaults: include**/*.tf; exclude.terraform/**,vendor/**)--order: control variable attribute order--concurrency: maximum parallel file processing--providers-schema: path to a provider schema JSON file--use-terraform-schema: derive schema viaterraform providers schema -json--schema-cache: directory for Terraform schema cache--no-schema-cache: disable Terraform schema caching--types: comma-separated list of block types to align (defaults tovariable)--all: align all supported block types (mutually exclusive with--types)
0: success1: files need formatting when run with--checkor--diff2: invalid CLI usage or configuration3: processing error during formatting or alignment
Files are written atomically via a temporary file rename and the original newline style and optional UTF‑8 byte‑order mark (BOM) are preserved.
git clone https://github.com/oferchen/hclalign.git
cd hclalign
make init tidy buildThe binary is created at .build/hclalign.
hclalign [path] [flags]Format all .tf files under the current directory and write the result back:
hclalign . --include "**/*.tf"Check whether files are already formatted:
hclalign . --checkPreview the diff of required changes:
hclalign . --diffProcess a single file from STDIN and write to STDOUT:
cat variables.tf | hclalign --stdin --stdout| Target | Description |
|---|---|
make init |
download and verify Go modules |
make tidy |
tidy module dependencies |
make fmt |
run gofumpt (v0.6.0); regenerate golden files; run terraform fmt on test cases if available |
make strip |
remove comments and enforce the single-line comment policy |
make lint |
execute golangci-lint |
make test |
run tests with coverage |
make cover |
verify coverage ≥95% |
make build |
build the hclalign binary into .build/ |
make clean |
remove build artifacts |
Terraform CLI is optional. If installed, make fmt runs terraform fmt on tests/cases and regenerates golden test files.
make fmt uses go run mvdan.cc/[email protected] so contributors do not need to install gofumpt manually.
Use hclalign . --check in CI to fail builds when formatting is needed. The provided GitHub Actions workflow runs make tidy, make fmt, make lint, make test, and make cover on Linux and macOS with multiple Go versions.
hclalign is released under the Apache-2.0 License.