Introduction
Every Terraform project needs consistent formatting and syntax validation. terraform fmt auto-formats your HCL files to the canonical style, while terraform validate checks your configuration for errors before you run plan or apply.
This guide covers both commands with all their flags, practical examples, and CI/CD integration.
terraform fmt — Auto-Format HCL Files
terraform fmt rewrites Terraform configuration files to the canonical format and style. It applies a subset of the Terraform language style conventions.
Basic Usage
# Format all .tf files in the current directory
terraform fmt
# Format and show what changed
terraform fmt -diff
# Format recursively (all subdirectories)
terraform fmt -recursive
What terraform fmt Changes
- Indentation: Normalizes to 2 spaces
- Alignment: Aligns
=signs in attribute blocks - Whitespace: Removes trailing whitespace, normalizes blank lines
- Quotes: Standardizes string quoting
Before:
resource "aws_instance" "web" {
ami = "ami-12345678"
instance_type="t3.micro"
tags={
Name = "my-instance"
Environment="production"
}
}
After terraform fmt:
resource "aws_instance" "web" {
ami = "ami-12345678"
instance_type = "t3.micro"
tags = {
Name = "my-instance"
Environment = "production"
}
}
All terraform fmt Flags
| Flag | Description | Example |
|---|---|---|
-check | Check if files are formatted (exit 0 if yes, 3 if no) | terraform fmt -check |
-diff | Show the formatting differences | terraform fmt -diff |
-recursive | Process subdirectories recursively | terraform fmt -recursive |
-list=false | Don’t list formatted files | terraform fmt -list=false |
-write=false | Don’t write changes (dry run) | terraform fmt -write=false -diff |
-no-color | Disable color output | terraform fmt -no-color |
terraform fmt -check (CI/CD)
The -check flag is designed for CI/CD pipelines. It returns exit code 0 if all files are properly formatted, or exit code 3 if any files need formatting:
# Check without modifying files
terraform fmt -check -recursive
# In a CI pipeline — fail the build if not formatted
terraform fmt -check -recursive -diff
terraform fmt -recursive
By default, terraform fmt only processes the current directory. Use -recursive to format your entire project:
# Format everything including modules
terraform fmt -recursive
# Check everything in CI
terraform fmt -check -recursive
Combining Flags
# Dry run: show what would change across all directories
terraform fmt -check -diff -recursive
# Format everything, show changes, suppress file list
terraform fmt -recursive -diff -list=false
terraform validate — Check Configuration Syntax
terraform validate checks whether a configuration is syntactically valid and internally consistent. It checks types, required attributes, and references without accessing any remote services.
Basic Usage
# Must run init first (validates provider schemas)
terraform init
terraform validate
Successful output:
Success! The configuration is valid.
Error output:
╷
│ Error: Missing required argument
│
│ on main.tf line 3, in resource "aws_instance" "web":
│ 3: resource "aws_instance" "web" {
│
│ The argument "ami" is required, but no definition was found.
╵
What terraform validate Checks
- Syntax errors: Missing braces, commas, quotes
- Type errors: Wrong attribute types (string where number expected)
- Required attributes: Missing mandatory fields
- Reference errors: Referencing resources or variables that don’t exist
- Block structure: Invalid nested blocks
What terraform validate Does NOT Check
- Whether resources actually exist in the cloud
- Whether values are valid (e.g., valid AMI IDs)
- Whether you have proper credentials
- Runtime errors that only appear during apply
JSON Output
For programmatic use:
terraform validate -json
{
"valid": false,
"error_count": 1,
"warning_count": 0,
"diagnostics": [
{
"severity": "error",
"summary": "Missing required argument",
"detail": "The argument \"ami\" is required..."
}
]
}
terraform fmt vs terraform validate
| Feature | terraform fmt | terraform validate |
|---|---|---|
| What it does | Formats HCL code style | Checks syntax and logic |
| Needs init? | No | Yes (needs provider schemas) |
| Modifies files? | Yes (unless -check) | No |
| Catches syntax errors? | Only formatting issues | Yes |
| Catches logic errors? | No | Yes (missing refs, wrong types) |
| CI/CD usage | fmt -check (exit 3 = unformatted) | validate (exit 1 = invalid) |
Use both — they catch different things:
# Full validation pipeline
terraform fmt -check -recursive # Style check
terraform init -backend=false # Init without backend
terraform validate # Syntax/logic check
terraform plan # Plan against real infra
CI/CD Integration
GitLab CI
validate:
stage: test
script:
- terraform fmt -check -recursive -diff
- terraform init -backend=false
- terraform validate
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
GitHub Actions
- name: Terraform Format Check
run: terraform fmt -check -recursive -diff
- name: Terraform Validate
run: |
terraform init -backend=false
terraform validate
Pre-commit Hook
Create .pre-commit-config.yaml:
repos:
- repo: https://github.com/antonbabenko/pre-commit-terraform
rev: v1.86.0
hooks:
- id: terraform_fmt
- id: terraform_validate
Or a simple git hook in .git/hooks/pre-commit:
#!/bin/sh
terraform fmt -check -recursive
if [ $? -ne 0 ]; then
echo "Run 'terraform fmt -recursive' to fix formatting"
exit 1
fi
Related Tools
| Tool | Purpose |
|---|---|
terraform fmt | Official formatter (style only) |
terraform validate | Official validator (syntax + logic) |
| TFLint | Linter for best practices + cloud-specific rules |
| Checkov | Security and compliance scanning |
| tfsec | Static security analysis |
Hands-On Courses
Learn by doing with interactive courses on CopyPasteLearn:
Conclusion
terraform fmt and terraform validate are the foundation of Terraform code quality. Use terraform fmt -check -recursive in CI to enforce consistent formatting, and terraform validate after init to catch syntax and reference errors before they reach terraform plan. Together with linters like TFLint, they form a complete validation pipeline.




