Last Updated on June 11, 2025 by Arnav Sharma
Infrastructure as Code (IaC) offers power, but with that comes risk. A typo in a Terraform file or a misconfigured resource can easily cause an outage, a security gap, or unexpected costs.
To avoid that, we need more than just terraform plan. We need a consistent way to validate our Terraform code—before it ever touches production.
In this post, I’ll cover three tools that can help:
- Terratest: run unit-like tests on actual infrastructure
- TFLint: catch issues through static code analysis
- Pre-commit hooks: enforce quality and style before any Git commit
1. Terratest – Infrastructure Testing with Go
Terratest is a testing framework written in Go that provisions real cloud infrastructure using your Terraform files, validates it programmatically, and destroys it once done.
When to Use It
You want to:
- Validate infrastructure logic
- Confirm naming conventions or resource outputs
- Automatically test infrastructure in CI/CD
Where to Place Terratest Files
Create a dedicated test folder inside your Terraform repo:
/terraform-azure-storage/
├── main.tf
├── variables.tf
├── outputs.tf
└── test/
└── storage_test.go
Best practice: keep test logic separate from your deployment modules.
Sample Terratest File – test/storage_test.go
package test
import (
"testing"
"github.com/gruntwork-io/terratest/modules/terraform"
"github.com/stretchr/testify/assert"
)
func TestTerraformAzureStorage(t *testing.T) {
terraformOptions := &terraform.Options{
TerraformDir: "../", // points to Terraform root
}
defer terraform.Destroy(t, terraformOptions)
terraform.InitAndApply(t, terraformOptions)
storageName := terraform.Output(t, terraformOptions, "storage_account_name")
assert.Contains(t, storageName, "arnav")
}
How to Run It
go test -v ./test/
Example Output
=== RUN TestTerraformAzureStorage
Terraform apply in ../
Validating output: storage_account_name = arnavstorageprod
--- PASS: TestTerraformAzureStorage (46.12s)
PASS
2. TFLint – Static Analysis for Terraform
TFLint is a linter designed specifically for Terraform. It scans .tf files and flags things like:
- Deprecated resource attributes
- Provider-specific issues
- Style inconsistencies
- Missing required tags or naming rules
When to Use It
Ideal for teams, pipelines, and code reviews. It helps enforce standards and best practices early.
Where to Place TFLint Configs
Place the TFLint config file in the root of your Terraform project:
/terraform-azure-storage/
├── main.tf
├── variables.tf
├── .tflint.hcl
You don’t need the config file unless customizing rules, but it’s recommended for consistency.
Running TFLint
tflint --init
tflint
Sample Output
Warning: azurerm_storage_account has deprecated attribute "enable_blob_encryption"
Error: Resource "azurerm_resource_group" is missing tag "owner"
Error: Unknown resource type "azurerm_storge_account"
3. Pre-Commit Hooks – Enforce Validation Before Commits
Pre-commit hooks are scripts that automatically run before a commit is accepted in Git. These are great for:
- Running
terraform fmt,validate,tflint - Preventing half-baked or inconsistent code from entering the repo
- Keeping things clean across team members
When to Use It
You’re working in a team or want automated quality enforcement—even if you’re working solo.
Where to Place Pre-Commit Configs
At the root of your repo:
/terraform-azure-storage/
├── main.tf
├── outputs.tf
├── .pre-commit-config.yaml
Sample .pre-commit-config.yaml
repos:
- repo: https://github.com/antonbabenko/pre-commit-terraform
rev: v1.78.0
hooks:
- id: terraform_fmt
- id: terraform_validate
- id: tflint
- id: terraform_docs
Installation & Usage
Install pre-commit (Python-based):
pip install pre-commit
pre-commit install
pre-commit run --all-files
Example Output
Terraform fmt.................................................Passed
Terraform validate............................................Passed
Terraform tflint..............................................Failed
- main.tf:5 - Missing required tags on azurerm_resource_group
Terraform docs................................................Updated
Bringing It All Together
| Tool | Purpose | Where to Use |
|---|---|---|
| Terratest | Validate real deployed infrastructure | In a /test/ folder using Go |
| TFLint | Catch static errors in .tf code | Root folder (optional .tflint.hcl) |
| Pre-commit | Automate checks before Git commits | Root folder (.pre-commit-config.yaml) |
I help organisations secure their cloud infrastructure and stay ahead of evolving cyber threats. Microsoft MVP and Certified Trainer, author of Mastering Azure Security, and founder of arnav.au — a platform for practical Cloud, Cybersecurity, DevOps and AI content.
Frequently Asked Questions
Terratest is a testing framework written in Go that provisions real cloud infrastructure using your Terraform files, validates it programmatically, and destroys it once done. You should use it when you want to validate infrastructure logic, confirm naming conventions or resource outputs, and automatically test infrastructure in CI/CD pipelines.
Create a dedicated test folder in your Terraform repo with Go test files (e.g., test/storage_test.go), then run the command `go test -v ./test/`. The test will initialize, apply your Terraform configuration, validate outputs, and automatically destroy the infrastructure when complete.
TFLint is a linter that scans .tf files and flags deprecated resource attributes, provider-specific issues, style inconsistencies, and missing required tags or naming rules. It's ideal for teams and CI/CD pipelines to enforce standards and best practices early in the development process.
Pre-commit hooks are scripts that automatically run before a commit is accepted in Git, allowing you to run terraform fmt, validate, tflint, and other checks. They prevent half-baked or inconsistent code from entering the repository and help keep standards consistent across team members.
Terratest files should go in a dedicated /test/ folder, TFLint configuration (.tflint.hcl) goes in the root of your Terraform project, and pre-commit configuration (.pre-commit-config.yaml) also goes in the root. This structure keeps test logic separate from deployment modules while keeping validation tools accessible.