Terraform Developer Productivity Master Guide

DevOps

YOUR COSMETIC CARE STARTS HERE

Find the Best Cosmetic Hospitals

Trusted โ€ข Curated โ€ข Easy

Looking for the right place for a cosmetic procedure? Explore top cosmetic hospitals in one place and choose with confidence.

โ€œSmall steps lead to big changes โ€” today is a perfect day to begin.โ€

Explore Cosmetic Hospitals Compare hospitals, services & options quickly.

โœ“ Shortlist providers โ€ข โœ“ Review options โ€ข โœ“ Take the next step with confidence

Tools, Workflow, Security, Testing, CI/CD, Automation, AI Assistance and Production Best Practices

The End-to-End Practical Handbook for Modern Terraform Engineering

Edition: September 2026
Audience: Terraform Developers, DevOps Engineers, SREs, Platform Engineers, Cloud Engineers, DevSecOps Engineers, Module Authors, Infrastructure Architects, Engineering Managers


Executive Standard

A professional Terraform workflow should never be:

Write Terraform
      โ†“
terraform apply

The production engineering workflow should instead look like:

Developer
   โ†“
IDE + Language Intelligence
   โ†“
AI / Registry Assistance
   โ†“
Terraform Code
   โ†“
Format
   โ†“
Validate
   โ†“
Lint
   โ†“
Security Scan
   โ†“
Test
   โ†“
Documentation
   โ†“
Cost Analysis
   โ†“
Pre-Commit
   โ†“
Git
   โ†“
CI
   โ†“
Terraform Plan
   โ†“
Policy Validation
   โ†“
Human Review
   โ†“
Approval
   โ†“
Remote Apply
   โ†“
Verification
   โ†“
Monitoring / Drift Detection

That difference is essentially the difference between using Terraform and operating a Terraform engineering platform.

As of September 2026, Terraform 1.16.0 is the current stable release, while 1.17 builds remain pre-release. Production organizations should normally stay on stable versions rather than automatically consuming alpha or RC builds.


PART 1 โ€” TERRAFORM DEVELOPMENT ECOSYSTEM

The Complete Developer Toolchain

Developer
    โ†“
VS Code / Cursor / Claude Code
    โ†“
HashiCorp Terraform Extension
    โ†“
terraform-ls
    โ†“
Terraform MCP Server
    โ†“
tenv
    โ†“
Terraform CLI
    โ†“
terraform fmt
terraform validate
terraform test
    โ†“
TFLint
    โ†“
Trivy / Checkov
    โ†“
terraform-docs
    โ†“
Infracost
    โ†“
pre-commit-terraform
    โ†“
Git
    โ†“
GitHub / GitLab
    โ†“
GitHub Actions / GitLab CI
    โ†“
HCP Terraform / Terraform Enterprise
    โ†“
Sentinel / OPA
    โ†“
Cloud Infrastructure

Responsibilities of Each Layer

LayerResponsibility
IDEDeveloper editing environment
Terraform ExtensionTerraform-aware editing
terraform-lsLanguage intelligence
Terraform MCPCurrent Registry/provider/module knowledge for AI
tenvTerraform/tool version management
Terraform CLICore Terraform execution engine
terraform fmtCanonical formatting
terraform validateTerraform configuration validation
terraform testNative Terraform testing
TFLintStatic linting and provider-aware checks
TrivySecurity/misconfiguration scanning
CheckovAdditional policy/compliance scanning
terraform-docsDocumentation generation
InfracostCost and FinOps feedback
pre-commitLocal automation
GitVersion control
GitHub/GitLabCollaboration and pull requests
CI/CDAutomated quality gates
AtlantisPR-driven Terraform execution
HCP TerraformState, remote execution and governance
Terraform EnterpriseSelf-hosted Terraform platform
Sentinel / OPAPolicy-as-code
RenovateDependency/version automation
MonitoringVerification and drift awareness

A mature platform deliberately assigns one primary responsibility to each tool. Adding multiple tools that solve the exact same problem generally increases maintenance and alert fatigue rather than quality.


PART 2 โ€” CORE TERRAFORM DEVELOPMENT TOOLS

1. Terraform CLI

What

Terraform CLI is the primary Terraform execution engine.

It:

  • Parses HCL.
  • Downloads providers/modules.
  • Builds dependency graphs.
  • Reads and writes Terraform state.
  • Generates execution plans.
  • Executes infrastructure changes.
  • Performs imports and state operations.
  • Runs native tests.

Core Commands

terraform init

Initializes a Terraform working directory.

terraform init

Use after:

git clone
new provider
new module
backend change
provider version change
Code language: JavaScript (javascript)

For validation-only automation:

terraform init -backend=false
Code language: JavaScript (javascript)

terraform init installs providers/modules and initializes the backend, and HashiCorp documents it as safe to rerun as configuration evolves.


terraform fmt

Canonical Terraform formatting:

terraform fmt
terraform fmt -recursive
terraform fmt -check
terraform fmt -check -recursive

Recommended:

Developer machine โ†’ terraform fmt -recursive
CI                โ†’ terraform fmt -check -recursive

CI should fail rather than modify code.


terraform validate

terraform validate

Checks:

  • HCL syntax
  • Attribute structures
  • References
  • Internal Terraform consistency
  • Provider/module schema compatibility where available

It does not prove that:

  • IAM permissions are sufficient.
  • An AWS region supports a resource.
  • A subnet actually has capacity.
  • Security configuration is acceptable.
  • Infrastructure deployment will succeed.

HashiCorp describes validate as checking syntax and internal consistency rather than remote APIs.


terraform plan

terraform plan

Save the plan:

terraform plan -out=tfplan

Machine-readable representation:

terraform show -json tfplan > tfplan.json
Code language: CSS (css)

Production rule:

Review the actual execution plan before applying infrastructure.

Particularly inspect:

+ create
~ update
- destroy
-/+ replace

-/+ is one of the most important symbols in Terraform review because it indicates replacement.


terraform apply

Interactive:

terraform apply

Approved saved plan:

terraform apply tfplan

For production, prefer:

CI / HCP Terraform
       โ†“
authoritative plan
       โ†“
approval
       โ†“
apply exact approved plan

Avoid developers casually applying production infrastructure from laptops.


terraform destroy

terraform destroy

This should usually be heavily restricted for production environments.

For temporary integration environments it can be legitimate:

terraform apply -auto-approve
run-tests
terraform destroy -auto-approve

terraform console

terraform console
Code language: JavaScript (javascript)

Excellent for evaluating:

  • expressions
  • locals
  • functions
  • CIDR calculations
  • maps
  • lists
  • transformations

More in Part 17.


terraform output

terraform output
terraform output vpc_id
terraform output -json

Useful for:

  • CI integrations
  • debugging
  • scripts
  • downstream automation

Be careful with sensitive outputs.


terraform show

terraform show
terraform show tfplan
terraform show -json tfplan

Machine-readable plan JSON is particularly useful for:

OPA
Checkov
Infracost
custom CI analysis

terraform providers

terraform providers
terraform providers schema -json
terraform providers lock

terraform providers lock is useful when producing lock information for multiple platforms or provider mirrors.


terraform state

Examples:

terraform state list
terraform state show aws_instance.app
terraform state mv OLD NEW
terraform state rm ADDRESS
Code language: PHP (php)

These commands are powerful and dangerous.

Prefer configuration-based approaches such as:

moved {
  from = aws_instance.old
  to   = aws_instance.new
}
Code language: JavaScript (javascript)

over manual state manipulation whenever possible.


terraform import

Modern preferred pattern:

import {
  to = aws_s3_bucket.logs
  id = "company-production-logs"
}
Code language: JavaScript (javascript)

Then:

terraform plan
terraform apply

CLI import remains useful:

terraform import aws_s3_bucket.logs company-production-logs
Code language: JavaScript (javascript)

terraform test

terraform test

Native Terraform testing is now a first-class part of a professional Terraform workflow.

Test files:

*.tftest.hcl
*.tftest.json
Code language: CSS (css)

HashiCorp warns that tests containing apply operations can create real infrastructure and therefore incur costs.


2. VS Code + HashiCorp Terraform Extension

Why

A developer should receive feedback while writing code, not after pushing a pull request.

The official HashiCorp Terraform extension provides:

  • syntax highlighting
  • IntelliSense
  • completion
  • diagnostics
  • code navigation
  • formatting
  • module explorer
  • provider awareness
  • Terraform test syntax
  • Terraform Stacks support
  • Terraform Policy support

It uses Terraform Language Server underneath.

Recommended VS Code Settings

.vscode/settings.json

{
  "[terraform]": {
    "editor.defaultFormatter": "hashicorp.terraform",
    "editor.formatOnSave": true,
    "editor.formatOnSaveMode": "file"
  },

  "[terraform-vars]": {
    "editor.defaultFormatter": "hashicorp.terraform",
    "editor.formatOnSave": true,
    "editor.formatOnSaveMode": "file"
  },

  "[terraform-test]": {
    "editor.defaultFormatter": "hashicorp.terraform",
    "editor.formatOnSave": true,
    "editor.formatOnSaveMode": "file"
  },

  "terraform.languageServer.enable": true
}
Code language: JSON / JSON with Comments (json)

The official extension recommends file-level format-on-save because terraform fmt formats complete files rather than arbitrary changed ranges.

Useful optional settings:

{
  "terraform.validation.enableEnhancedValidation": true,
  "terraform.experimentalFeatures.prefillRequiredFields": true,
  "terraform.codelens.referenceCount": true
}
Code language: JSON / JSON with Comments (json)

For large repositories:

{
  "terraform.languageServer.rootModules": [
    "/environments/development",
    "/environments/staging",
    "/environments/production"
  ],

  "terraform.languageServer.ignoreDirectoryNames": [
    ".terraform",
    ".terragrunt-cache"
  ]
}
Code language: JSON / JSON with Comments (json)

Recommended Extensions

Core:

HashiCorp Terraform
GitLens
YAML
EditorConfig

Depending on workflow:

GitHub Pull Requests
GitLab Workflow
Trivy
Infracost
Docker
Kubernetes

Do not install three competing Terraform formatters or Terraform language extensions simultaneously.


3. terraform-ls

What

terraform-ls is HashiCorp’s Terraform Language Server implementation.

It implements the Language Server Protocol so editors can understand Terraform structure.

Capabilities include:

Completion
Diagnostics
Navigation
Hover information
References
Modules
Providers
Terraform schema awareness

It is actively maintained by HashiCorp.

Do VS Code Users Need to Install It Manually?

Usually:

NO

The official HashiCorp VS Code Terraform extension includes/manages the language server.

Manual installation is mainly useful when:

  • another LSP-compatible editor is used
  • centralized custom tooling requires it
  • debugging language-server behavior
  • an editor integration requires an explicit binary

Architecture:

VS Code
  โ†“
HashiCorp Terraform Extension
  โ†“
terraform-ls
  โ†“
Terraform CLI + Provider Schemas

4. Terraform MCP Server

Why MCP Matters

Traditional AI coding:

Developer
   โ†“
AI Model
   โ†“
Model training knowledge
   โ†“
Terraform code

Problem:

The AI may invent:

  • obsolete arguments
  • removed resources
  • incorrect module versions
  • invalid provider syntax

Modern approach:

Developer Request
      โ†“
AI Coding Agent
      โ†“
Terraform MCP Server
      โ†“
Terraform Registry
      โ†“
Current Provider / Module / Policy Information
      โ†“
Generated Terraform

HashiCorp’s Terraform MCP Server can expose current Terraform Registry provider, module and policy information to AI clients and can optionally interact with HCP Terraform/Terraform Enterprise.

Strong Security Recommendation

For developer AI agents, default to:

Registry lookup       โ†’ ENABLED
Documentation lookup  โ†’ ENABLED
Module discovery      โ†’ ENABLED
Provider lookup       โ†’ ENABLED

Workspace changes     โ†’ DISABLED unless specifically required
Terraform operations  โ†’ DISABLED unless specifically required
Production apply      โ†’ NEVER casually delegated

The Terraform MCP Server explicitly gates Terraform operational capabilities and supports secure local deployment patterns.

Docker Setup

docker run -i --rm hashicorp/terraform-mcp-server

Example VS Code MCP configuration:

{
  "mcp": {
    "servers": {
      "terraform": {
        "command": "docker",
        "args": [
          "run",
          "-i",
          "--rm",
          "hashicorp/terraform-mcp-server"
        ]
      }
    }
  }
}
Code language: JSON / JSON with Comments (json)

Public Registry queries do not require an HCP token; private registry/HCP/TFE access requires appropriate authentication.

Interestingly, the current official Terraform VS Code extension itself also exposes:

{
  "terraform.mcp.server.enable": true
}
Code language: JSON / JSON with Comments (json)

as an MCP integration option.

Useful Terraform AI Prompts

Using the Terraform Registry through Terraform MCP,
find the current schema for aws_eks_cluster and generate
the smallest production-safe example compatible with the
provider version in this repository.
Code language: JavaScript (javascript)
Inspect the provider and module versions used by this repository.
Do not invent attributes. Verify every unfamiliar argument using
Terraform MCP before generating code.
Code language: JavaScript (javascript)
Review this Terraform module for deprecated provider attributes.
Return only changes supported by the provider version locked in
.terraform.lock.hcl.
Code language: CSS (css)
Generate terraform test plan-only tests for this module.
Do not create real cloud resources.
Code language: JavaScript (javascript)

AI Is Excellent For

Boilerplate
Variables
Outputs
Tests
Documentation
Refactoring
Module scaffolding
Explaining plans
Registry research

AI Must Not Be Blindly Trusted For

IAM
security policies
network exposure
state operations
resource destruction
provider upgrades
production applies
blast-radius decisions

AI accelerates Terraform engineering.

It does not replace Terraform engineering judgment.


PART 3 โ€” TERRAFORM VERSION MANAGEMENT

5. tenv

Problem

This is fragile:

Laptop Terraform = 1.16
CI Terraform     = 1.14
Engineer B       = 1.15
Production agent = 1.13

Different Terraform versions may:

  • interpret features differently
  • modify lock files
  • reject syntax
  • produce unexpected workflows

tenv

tenv manages:

  • Terraform
  • OpenTofu
  • Terragrunt
  • Terramate
  • related IaC tooling

It is also positioned as the successor to tfenv/tofuenv for broader version management.

macOS

brew install tenv

Windows

winget install Tofuutils.Tenv
Code language: CSS (css)

or:

choco install tenv

Usage

tenv tf install 1.16.0
tenv tf use 1.16.0
terraform version
Code language: CSS (css)

Project file:

.terraform-version
Code language: CSS (css)
1.16.0
Code language: CSS (css)

Terraform itself should also declare:

terraform {
  required_version = "~> 1.16.0"
}
Code language: JavaScript (javascript)

The version manager gives the developer the correct binary.

required_version prevents incompatible binaries from being used.

You want both.

tenv vs tfenv vs asdf vs mise

ToolBest Fit
tenvTerraform/OpenTofu/Terragrunt-focused teams
tfenvSimple Terraform-only version switching
asdfOrganizations already standardizing many runtimes
miseModern multi-language/tool version management

GOLD Recommendation

For a Terraform-centric engineering platform:

tenv

If the company already standardizes all development tooling through mise/asdf:

Use the organizational standard instead of introducing another manager.
Code language: PHP (php)

PART 4 โ€” TERRAFORM CODE QUALITY

6. terraform fmt

Terraform code formatting must be deterministic.

terraform fmt
terraform fmt -recursive
terraform fmt -check
terraform fmt -check -recursive

Recommended:

IDE     โ†’ format on save
Commit  โ†’ terraform_fmt hook
CI      โ†’ terraform fmt -check -recursive

Formatting should never consume meaningful code-review time.


7. terraform validate

Run:

terraform init -backend=false
terraform validate
Code language: JavaScript (javascript)

Think of validate as:

"Is this internally valid Terraform configuration?"
Code language: JSON / JSON with Comments (json)

Not:

"Is this secure?"
"Will AWS accept it?"
"Is this architecture good?"
Code language: JSON / JSON with Comments (json)

That is why the following progression exists:

terraform validate
        โ†“
TFLint
        โ†“
Trivy
        โ†“
terraform test
        โ†“
terraform plan

8. TFLint

What

TFLint is a pluggable Terraform static-analysis framework.

It can detect:

  • deprecated syntax
  • unused declarations
  • naming problems
  • best-practice violations
  • provider-specific mistakes
  • invalid AWS/Azure/GCP values
  • incorrect instance types

Installation

macOS:

brew install terraform-linters/tap/tflint

Windows:

winget install -e --id TerraformLinters.tflint
Code language: CSS (css)

Production .tflint.hcl

Example for AWS:

tflint {
  required_version = ">= 0.64.0"
}

config {
  format              = "compact"
  call_module_type    = "local"
  force               = false
  disabled_by_default = false
}

plugin "terraform" {
  enabled = true
  preset  = "recommended"
}

plugin "aws" {
  enabled = true
  version = "0.48.0"
  source  = "github.com/terraform-linters/tflint-ruleset-aws"
}
Code language: JavaScript (javascript)

At the time of this guide, TFLint 0.64.x is current and the AWS plugin line is 0.48.x.

Use the cloud plugin applicable to your repository.

Do not blindly load:

AWS plugin
Azure plugin
Google plugin

into a project that only uses AWS.

Run

tflint --init
tflint

Monorepository:

tflint --recursive --init
tflint --recursive

terraform validate vs TFLint

terraform validate
        โ†“
Terraform correctness

TFLint
        โ†“
Terraform quality + provider-aware static analysis
Code language: JavaScript (javascript)

Both provide value.


PART 5 โ€” TERRAFORM SECURITY

9. Trivy

What

Trivy is the recommended default security scanner for this stack because it can scan far more than Terraform alone.

It covers:

  • IaC misconfigurations
  • exposed infrastructure
  • encryption configuration
  • IAM risks
  • cloud resources
  • Kubernetes
  • secrets
  • containers
  • dependencies

Terraform HCL and Terraform plan scanning are supported.

Installation

macOS:

brew install trivy

Terraform Scan

trivy config .

Fail CI on serious findings:

trivy config \
  --exit-code 1 \
  --severity HIGH,CRITICAL \
  .
Code language: PHP (php)

Plan scan:

terraform plan -out=tfplan
trivy config tfplan

JSON:

terraform show -json tfplan > tfplan.json
trivy config tfplan.json
Code language: CSS (css)

What It Should Catch

Examples:

S3 bucket public access
unencrypted storage
0.0.0.0/0 administrative ports
weak IAM
public databases
insecure Kubernetes settings
missing encryption
embedded secrets
Code language: PHP (php)

10. Checkov

Checkov is another mature IaC security and policy scanner.

Capabilities include:

  • Terraform HCL scanning
  • plan scanning
  • compliance rules
  • graph-based checks
  • custom policies
  • multi-IaC support

Installation

pipx install checkov

or:

pip install checkov

Terraform

checkov -d .

Plan:

terraform plan -out=tfplan
terraform show -json tfplan > tfplan.json
checkov -f tfplan.json

Plan JSON can contain sensitive data. Treat it like a sensitive CI artifact.

Trivy or Checkov?

Use Trivy Only

Good default when you want:

One scanner
Terraform
Kubernetes
containers
dependencies
secrets
simple CI

Use Checkov Only

Consider it when:

Checkov-specific policies are already standardized
Prisma Cloud integration matters
graph-aware policies are important

Use Both

Only when:

There is demonstrably different policy coverage
AND
the organization has ownership for suppressions and alert tuning.

Bad strategy:

Trivy + Checkov + tfsec + another scanner

because "more scanners = more security"
Code language: JavaScript (javascript)

That usually creates duplicate alerts.

Also note: the current pre-commit-terraform project explicitly treats its tfsec hook as deprecated and recommends Trivy instead.


PART 6 โ€” TERRAFORM TESTING

11. terraform test

A Terraform module should increasingly be treated like software.

Directory:

modules/vpc/
โ”œโ”€โ”€ main.tf
โ”œโ”€โ”€ variables.tf
โ”œโ”€โ”€ outputs.tf
โ””โ”€โ”€ tests/
    โ””โ”€โ”€ main.tftest.hcl

Example:

variables {
  environment = "test"
  vpc_cidr    = "10.20.0.0/16"
}

run "plan_vpc" {
  command = plan

  assert {
    condition     = aws_vpc.main.cidr_block == "10.20.0.0/16"
    error_message = "VPC CIDR does not match the requested value."
  }

  assert {
    condition     = aws_vpc.main.enable_dns_support
    error_message = "DNS support must be enabled."
  }
}
Code language: JavaScript (javascript)

Run:

terraform test

Specific test:

terraform test -filter=tests/main.tftest.hcl

Testing Layers

terraform validate
    โ†“
Structural correctness

TFLint
    โ†“
Static quality

terraform test
    โ†“
Module behavior / assertions

Integration Test
    โ†“
Real infrastructure behavior
Code language: PHP (php)

Plan Tests

Preferred for most PR checks:

run "validate_configuration" {
  command = plan
}
Code language: JavaScript (javascript)

Fast and low-risk.

Apply Tests

run "deploy_test" {
  command = apply
}
Code language: JavaScript (javascript)

Apply tests may create infrastructure.

Use:

isolated test account
short-lived credentials
tight quotas
automatic cleanup
cost controls

Terraform supports provider mocking, which can further reduce dependence on live APIs in appropriate tests.

Terratest

Terratest uses Go to:

  1. create infrastructure
  2. query the deployed system
  3. assert behavior
  4. destroy infrastructure

Excellent when you need to prove:

Load balancer responds
EC2 boots correctly
DNS resolves
database connects
Kubernetes service becomes healthy

Kitchen-Terraform

Useful historically and in organizations already standardized around Kitchen ecosystems.

For new general Terraform development, native tests and Terratest tend to be easier defaults.

GOLD Standard

Native terraform test
        โ†“
default

Terratest
        โ†“
when real infrastructure behavior must be verified
Code language: JavaScript (javascript)

PART 7 โ€” TERRAFORM DOCUMENTATION

12. terraform-docs

Documentation that depends on humans eventually becomes stale.

terraform-docs extracts:

  • requirements
  • providers
  • modules
  • resources
  • inputs
  • outputs

and generates documentation automatically.

Installation

brew install terraform-docs

README Before

# VPC Module

Creates a VPC.
Code language: PHP (php)

README Template

# VPC Module

Creates the organization's standard VPC.

<!-- BEGIN_TF_DOCS -->
<!-- END_TF_DOCS -->

## Usage

Example usage goes here.
Code language: HTML, XML (xml)

Run:

terraform-docs markdown table \
  --output-file README.md \
  --output-mode inject \
  .
Code language: CSS (css)

The inject mode replaces content between its documentation markers while preserving manually written README sections.

.terraform-docs.yml

formatter: "markdown table"

sections:
  show:
    - requirements
    - providers
    - modules
    - resources
    - inputs
    - outputs

sort:
  enabled: true
  by: name

output:
  file: README.md
  mode: inject
  template: |-
    <!-- BEGIN_TF_DOCS -->
    {{ .Content }}
    <!-- END_TF_DOCS -->
Code language: HTML, XML (xml)

GOLD Rule

Humans maintain:

Purpose
architecture
examples
operational guidance

terraform-docs maintains:

Inputs
outputs
providers
requirements
resources
modules

PART 8 โ€” TERRAFORM COST MANAGEMENT

13. Infracost

Infrastructure code has financial consequences.

Traditional review:

PR changes RDS instance
      โ†“
Looks technically correct
      โ†“
Merge
      โ†“
Cloud bill surprise

FinOps-aware review:

Terraform Change
      โ†“
Terraform / IaC Analysis
      โ†“
Infracost
      โ†“
Monthly Cost Difference
      โ†“
PR Review

Installation

brew install infracost

Setup:

infracost auth login

Current CLI workflow:

infracost scan

Plan:

terraform plan -out=tfplan
terraform show -json tfplan > tfplan.json

infracost scan tfplan.json

Current Infracost tooling also includes:

infracost inspect
infracost ci setup
infracost ci setup --ci-pipeline

The newer workflow centers on scan and current VCS integrations rather than only the older breakdown workflow.

GitHub

Infracost currently recommends its GitHub App when permitted.

For GitHub Actions:

- uses: infracost/actions/diff@v4
  with:
    api-key: ${{ secrets.INFRACOST_API_KEY }}
    base-path: base
    head-path: head

The older setup action remains available but is considered the legacy integration path for new deployments.

Good Cost Policies

Examples:

Block >$5,000 monthly increase without FinOps approval
Warn >$500
Flag untagged billable resources
Flag expensive instance-family changes
Require owners for high-cost resources
Code language: PHP (php)

Infracost should provide feedback.

It should not automatically decide architecture.


PART 9 โ€” GIT AUTOMATION

14. pre-commit-terraform

One of the highest-value productivity improvements is moving failures from:

CI after 10 minutes

to:

developer laptop before commit

Workflow

git commit
    โ†“
terraform fmt
    โ†“
terraform validate
    โ†“
TFLint
    โ†“
Trivy
    โ†“
terraform-docs
    โ†“
Commit Accepted

Installation

brew install pre-commit

Install hooks:

pre-commit install

Run manually:

pre-commit run --all-files

Production .pre-commit-config.yaml

pre-commit-terraform v1.108.1 is the current release as of this guide.

repos:
  - repo: https://github.com/antonbabenko/pre-commit-terraform
    rev: v1.108.1
    hooks:
      - id: terraform_fmt

      - id: terraform_validate

      - id: terraform_tflint
        args:
          - --args=--config=__GIT_WORKING_DIR__/.tflint.hcl

      - id: terraform_trivy
        args:
          - --args=--severity=HIGH,CRITICAL
          - --args=--exit-code=1

      - id: terraform_docs
        args:
          - --hook-config=--path-to-file=README.md
          - --hook-config=--add-to-existing-file=true
Code language: PHP (php)

The project provides hooks for formatting, validation, TFLint, Trivy and terraform-docs.

What Runs Locally?

Fast checks:

fmt
validate
TFLint
targeted Trivy
terraform-docs

What Runs in CI?

Everything local plus:

terraform test
full security scan
terraform plan
plan security analysis
Infracost
policy
integration tests

Important principle:

Pre-commit improves developer feedback. CI remains authoritative.

Never trust local hooks as the only gate because they can be skipped.


PART 10 โ€” TERRAFORM PROJECT STRUCTURE

A practical repository:

terraform/
โ”œโ”€โ”€ modules/
โ”‚   โ”œโ”€โ”€ vpc/
โ”‚   โ”‚   โ”œโ”€โ”€ main.tf
โ”‚   โ”‚   โ”œโ”€โ”€ variables.tf
โ”‚   โ”‚   โ”œโ”€โ”€ outputs.tf
โ”‚   โ”‚   โ”œโ”€โ”€ versions.tf
โ”‚   โ”‚   โ”œโ”€โ”€ README.md
โ”‚   โ”‚   โ””โ”€โ”€ tests/
โ”‚   โ”‚       โ””โ”€โ”€ main.tftest.hcl
โ”‚   โ”‚
โ”‚   โ”œโ”€โ”€ eks/
โ”‚   โ”œโ”€โ”€ rds/
โ”‚   โ””โ”€โ”€ iam/
โ”‚
โ”œโ”€โ”€ environments/
โ”‚   โ”œโ”€โ”€ development/
โ”‚   โ”‚   โ”œโ”€โ”€ main.tf
โ”‚   โ”‚   โ”œโ”€โ”€ providers.tf
โ”‚   โ”‚   โ”œโ”€โ”€ versions.tf
โ”‚   โ”‚   โ”œโ”€โ”€ backend.tf
โ”‚   โ”‚   โ””โ”€โ”€ terraform.tfvars
โ”‚   โ”‚
โ”‚   โ”œโ”€โ”€ staging/
โ”‚   โ””โ”€โ”€ production/
โ”‚
โ”œโ”€โ”€ .github/
โ”‚   โ””โ”€โ”€ workflows/
โ”‚       โ””โ”€โ”€ terraform.yml
โ”‚
โ”œโ”€โ”€ .pre-commit-config.yaml
โ”œโ”€โ”€ .tflint.hcl
โ”œโ”€โ”€ .terraform-docs.yml
โ”œโ”€โ”€ .terraform-version
โ”œโ”€โ”€ .gitignore
โ””โ”€โ”€ README.md

Root Module

A deployable Terraform configuration.

Example:

environments/production

It owns a state.

Child Module

Reusable implementation:

modules/vpc
modules/rds
modules/eks

A child module should not normally own backend state.


Environment Isolation

Prefer:

development root/state/account
staging root/state/account
production root/state/account

over relying on Terraform CLI workspaces as the main isolation mechanism for substantially different production environments.

The strongest boundary is usually:

separate cloud account/subscription/project
            +
separate Terraform state
            +
separate permissions

.gitignore

.terraform/
*.tfstate
*.tfstate.*
crash.log
crash.*.log
*.tfplan
tfplan*
.terragrunt-cache/

# Secrets/local variable files
*.auto.tfvars
*.auto.tfvars.json

# Keep examples
!*.tfvars.example
Code language: PHP (php)

Do not ignore:

.terraform.lock.hcl
Code language: CSS (css)

Commit it for root modules.

HashiCorp recommends committing the provider dependency lock file so provider selections/checksums are reproducible. Modules themselves are not locked there, so module versions still need explicit constraints.


Repository Strategies

Monorepo

one repo
โ”œโ”€โ”€ networking
โ”œโ”€โ”€ security
โ”œโ”€โ”€ data
โ””โ”€โ”€ application-platform

Advantages:

  • atomic changes
  • easier shared standards
  • centralized tooling

Limitations:

  • CI complexity
  • broad permissions
  • potentially large blast radius

Multiple Infrastructure Repositories

Advantages:

  • stronger ownership boundaries
  • simpler permissions
  • smaller CI scope

Limitations:

  • duplicated tooling
  • cross-repository dependency coordination

One Repository Per Module

Excellent for externally reusable/platform modules.

Advantages:

  • independent release lifecycle
  • semantic versions
  • clean module ownership

One Repository Per Environment

Provides strong separation but often duplicates code excessively.

GOLD Pattern for Larger Organizations

Reusable modules
      โ†“
versioned module repositories or registry
      โ†“
Live infrastructure repositories
      โ†“
small root modules
      โ†“
separate environment state
Code language: JavaScript (javascript)

PART 11 โ€” TERRAGRUNT

15. Terragrunt

Why It Exists

Terraform modules solve reusable infrastructure logic.

Terragrunt primarily helps solve repeated live configuration and orchestration.

Example repeated across 100 roots:

backend configuration
provider configuration
account settings
region settings
module source versions
dependency wiring
Code language: JavaScript (javascript)

Terragrunt lets these be centralized.

Example

live/
โ”œโ”€โ”€ root.hcl
โ”‚
โ”œโ”€โ”€ development/
โ”‚   โ”œโ”€โ”€ account.hcl
โ”‚   โ”œโ”€โ”€ us-east-1/
โ”‚   โ”‚   โ”œโ”€โ”€ vpc/
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ terragrunt.hcl
โ”‚   โ”‚   โ””โ”€โ”€ eks/
โ”‚   โ”‚       โ””โ”€โ”€ terragrunt.hcl
โ”‚
โ”œโ”€โ”€ staging/
โ””โ”€โ”€ production/

Root:

remote_state {
  backend = "s3"

  config = {
    bucket       = "company-terraform-state"
    key          = "${path_relative_to_include()}/terraform.tfstate"
    region       = "us-east-1"
    encrypt      = true
    use_lockfile = true
  }
}

generate "provider" {
  path      = "provider.tf"
  if_exists = "overwrite_terragrunt"

  contents = <<EOF
provider "aws" {
  region = "us-east-1"
}
EOF
}
Code language: JavaScript (javascript)

Unit:

include "root" {
  path = find_in_parent_folders("root.hcl")
}

terraform {
  source = "../../../../modules/vpc"
}
Code language: PHP (php)

Modern Terragrunt uses workflows such as:

terragrunt plan
terragrunt run --all plan
terragrunt run --all apply

The modern run --all model is reflected in current Terragrunt documentation and examples.

Terragrunt reached 1.0 in March 2026, introducing stronger stability guarantees for the 1.x line.

Terraform vs Terraform + Terragrunt

Terraform
   โ†“
Best when:
small number of root modules
simple environments
HCP Terraform handles orchestration
little duplicated live configuration
Terraform + Terragrunt
   โ†“
Best when:
many accounts
many regions
many roots
repeated environment configuration
complex dependencies

When Terragrunt Is Unnecessary

Do not introduce it simply because the infrastructure uses Terraform.

For:

3 root modules
1 AWS account
1 region
simple CI

plain Terraform is generally easier.

Terragrunt is an abstraction layer.

Abstractions must pay rent.


PART 12 โ€” CI/CD

16. GitHub Actions

Pipeline

Checkout
   โ†“
Setup Terraform
   โ†“
terraform fmt -check
   โ†“
terraform init
   โ†“
terraform validate
   โ†“
TFLint
   โ†“
Trivy
   โ†“
terraform test
   โ†“
Cloud OIDC
   โ†“
terraform plan
   โ†“
Cost Analysis

Authentication

Never:

AWS_ACCESS_KEY_ID: AKIA...
AWS_SECRET_ACCESS_KEY: ...
Code language: HTTP (http)

Prefer:

GitHub OIDC
     โ†“
AWS STS
     โ†“
temporary credentials
     โ†“
IAM role

GitHub’s AWS credential action explicitly supports OIDC, eliminating the need for long-lived AWS secrets in GitHub.

Realistic PR Pipeline

name: Terraform CI

on:
  pull_request:
    paths:
      - "**/*.tf"
      - "**/*.tfvars"
      - "**/*.tftest.hcl"
      - ".terraform.lock.hcl"

permissions:
  contents: read
  id-token: write
  pull-requests: write

env:
  TF_IN_AUTOMATION: "true"
  TF_INPUT: "false"

jobs:
  quality:
    runs-on: ubuntu-latest

    defaults:
      run:
        working-directory: environments/development

    steps:
      - name: Checkout
        uses: actions/checkout@v7

      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v4
        with:
          terraform_version: "1.16.0"

      - name: Terraform Format
        run: terraform fmt -check -recursive
        working-directory: .

      - name: Terraform Init for Validation
        run: terraform init -backend=false

      - name: Terraform Validate
        run: terraform validate -no-color

      - name: Setup TFLint
        uses: terraform-linters/setup-tflint@v6
        with:
          tflint_version: v0.64.0
          cache: true

      - name: TFLint Init
        run: tflint --init
        working-directory: .

      - name: TFLint
        run: tflint --recursive --format=compact
        working-directory: .

      - name: Trivy IaC Scan
        uses: aquasecurity/trivy-action@v0.36.0
        with:
          scan-type: config
          scan-ref: .
          severity: HIGH,CRITICAL
          exit-code: "1"

      - name: Terraform Test
        run: terraform test
Code language: PHP (php)

Current major lines include:

hashicorp/setup-terraform v4
terraform-linters/setup-tflint v6
Trivy Action v0.36.x
actions/checkout v7

Production Supply-Chain Hardening

Readable documentation commonly shows:

uses: vendor/action@v4
Code language: HTTP (http)

Enterprise production workflows should consider pinning third-party actions to immutable full commit SHAs and letting Renovate update those pins automatically.


Authoritative Plan Job

  plan:
    needs: quality
    runs-on: ubuntu-latest

    defaults:
      run:
        working-directory: environments/development

    steps:
      - uses: actions/checkout@v7

      - uses: hashicorp/setup-terraform@v4
        with:
          terraform_version: "1.16.0"

      - name: Authenticate to AWS using OIDC
        uses: aws-actions/configure-aws-credentials@v6.2.3
        with:
          role-to-assume: arn:aws:iam::123456789012:role/github-terraform-plan
          aws-region: us-east-1

      - name: Terraform Init
        run: terraform init

      - name: Terraform Plan
        run: terraform plan -out=tfplan -no-color

      - name: Export Plan JSON
        run: terraform show -json tfplan > tfplan.json

The role shown is an example placeholderโ€”not a credential.


Apply

Production apply should normally happen only after:

PR merged
      โ†“
branch protection passed
      โ†“
authoritative plan
      โ†“
policy passed
      โ†“
environment approval
      โ†“
apply

If using HCP Terraform, an even cleaner division is:

GitHub Actions
   โ†“
lint/security/tests

HCP Terraform
   โ†“
authoritative remote plan
policy
approval
apply
state
audit

Avoid two independent systems both believing they own production execution.


17. GitLab CI

Equivalent architecture:

validate
   โ†“
lint
   โ†“
security
   โ†“
test
   โ†“
plan
   โ†“
approval
   โ†“
apply

Skeleton:

stages:
  - validate
  - security
  - test
  - plan
  - deploy

variables:
  TF_IN_AUTOMATION: "true"
  TF_INPUT: "false"

validate:
  stage: validate
  script:
    - terraform fmt -check -recursive
    - terraform init -backend=false
    - terraform validate
    - tflint --init
    - tflint --recursive

security:
  stage: security
  script:
    - trivy config --exit-code 1 --severity HIGH,CRITICAL .

test:
  stage: test
  script:
    - terraform test

plan:
  stage: plan
  script:
    - terraform init
    - terraform plan -out=tfplan
  artifacts:
    paths:
      - tfplan

apply:
  stage: deploy
  when: manual
  script:
    - terraform apply tfplan
  rules:
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
Code language: PHP (php)

For AWS authentication, use GitLab id_tokens + AWS STS rather than static access keys. GitLab’s current OIDC workflow uses ID tokens; the older CI_JOB_JWT_V2 approach has been removed.


PART 13 โ€” ATLANTIS

18. Atlantis

Atlantis turns Terraform into a pull-request-driven workflow.

Developer:

Open PR
   โ†“
Atlantis detects change
   โ†“
atlantis plan
   โ†“
Plan posted to PR
   โ†“
Review
   โ†“
atlantis apply

Typical commands:

atlantis plan
atlantis apply

Important Capability: Locking

After Atlantis plans a project, that Terraform project/workspace can remain locked against conflicting PRs until the workflow is resolved.

Atlantis vs GitHub Actions

GitHub Actions
      โ†“
general-purpose CI engine

Atlantis
      โ†“
Terraform-specific PR workflow engine

Atlantis vs HCP Terraform

Atlantis
  self-hosted PR automation
  Terraform-focused
  relatively lightweight
Code language: PHP (php)
HCP Terraform
  state platform
  remote execution
  private registry
  RBAC
  governance
  agents
  drift detection
  policy
  audit
Code language: PHP (php)

Use Atlantis when PR-driven Terraform execution is the key requirement and you want to own the platform.

Use HCP Terraform when centralized Terraform platform capabilities are needed.

Do not automatically operate both.


PART 14 โ€” HCP TERRAFORM

19. HCP Terraform

HCP Terraform is substantially more than remote state.

Capabilities include:

Remote state
State locking
Remote runs
Workspaces
Projects
Variable sets
Agents
Private module registry
Private provider registry
Run tasks
Policy enforcement
VCS integration
RBAC
Team access
Drift detection
Continuous validation
Audit capabilities
Code language: PHP (php)

HCP Terraform remote execution uses managed workers, while agents provide outbound connectivity to private infrastructure.

Organization

HCP Terraform Organization
       โ†“
Projects
       โ†“
Workspaces / Stacks
       โ†“
Runs
       โ†“
State

Workspace

Traditionally manages one root Terraform configuration/state.

Project

Groups related Terraform workspaces/stacks and helps organize access/governance.

Agent

Used when Terraform needs network access to:

private VPCs
private Kubernetes APIs
on-premises systems
private databases
internal services
Code language: PHP (php)

Agent communication is designed around outbound connectivity to HCP Terraform.


Private Registry

Platform Team
      โ†“
approved module
      โ†“
Private Module Registry
      โ†“
Application Teams
Code language: JavaScript (javascript)

This enables:

versioned modules
standard patterns
discoverability
controlled reuse

HCP Terraform also supports private providers.


Run Tasks

Integrate external checks into Terraform runs:

Terraform Plan
     โ†“
Security Run Task
     โ†“
Cost Run Task
     โ†“
Compliance Run Task
     โ†“
Apply

Drift Detection

Desired:

Terraform state/config
      =
real infrastructure

Reality:

Terraform
     โ†“
someone changes cloud console
     โ†“
drift
Code language: JavaScript (javascript)

HCP Terraform health assessments support drift detection and continuous validation.


Terraform CLI vs HCP Terraform vs Terraform Enterprise

Terraform CLI

Terraform execution engine

HCP Terraform

HashiCorp-hosted Terraform platform

Terraform Enterprise

Self-hosted distribution of the Terraform platform
Code language: PHP (php)

Terraform Enterprise is targeted at organizations requiring stronger self-hosting, network, compliance, availability or isolated-environment controls.

Recommended Enterprise Workflow

Developer
    โ†“
Git Branch
    โ†“
Static Checks
    โ†“
PR
    โ†“
HCP Terraform Speculative Plan
    โ†“
Security / Cost Run Tasks
    โ†“
OPA / Sentinel
    โ†“
Review
    โ†“
Merge
    โ†“
Authoritative Run
    โ†“
Approval
    โ†“
Apply
    โ†“
State + Audit + Drift Detection
Code language: PHP (php)

PART 15 โ€” POLICY AS CODE

20. Sentinel

Sentinel is HashiCorp’s policy-as-code framework.

Examples:

Only approved AWS regions
No public S3 buckets
No oversized EC2
Mandatory tags
Encryption required
Approved providers/modules
Code language: PHP (php)

Policy evaluation:

Terraform Plan
    โ†“
Sentinel
    โ†“
Policy Decision
    โ†“
Apply allowed / denied

Sentinel enforcement levels include advisory and mandatory modes.

Example conceptual policy:

Allowed regions:
  us-east-1
  us-west-2

terraform plan uses eu-west-1
        โ†“
DENY

21. OPA / Conftest

OPA

Open Policy Agent is a general-purpose policy engine.

Policy language:

Rego

Terraform workflow:

terraform plan
      โ†“
terraform show -json
      โ†“
OPA
      โ†“
Rego policy
      โ†“
allow / deny

OPA specifically documents Terraform plan evaluation as a policy pattern.

Conftest

Conftest provides a convenient CLI for applying Rego policies to structured configuration.

conftest test tfplan.json
Code language: CSS (css)

It is therefore better thought of as:

Rego policy runner for configuration

than as a competing policy language.


Sentinel vs OPA vs Conftest

ToolRoleBest Fit
SentinelHashiCorp policy language/runtimeHashiCorp-centered governance
OPAGeneral policy engineVendor-neutral platform governance
ConftestRego CLI/testing utilityLocal/CI policy validation

Current HCP Terraform supports both Sentinel and OPA policy sets.

Therefore:

"HCP Terraform requires Sentinel"
Code language: JSON / JSON with Comments (json)

is outdated advice.

Recommendation

HashiCorp-only enterprise platform:

Sentinel remains reasonable.

Multi-platform organization:

OPA/Rego often creates better policy reuse.

Already operating OPA elsewhere:

Reuse OPA.

Do not introduce two policy languages without a concrete need.


PART 16 โ€” DEPENDENCY MANAGEMENT

22. Renovate

Terraform has several dependencies:

Terraform CLI
Providers
Modules
GitHub Actions
Terragrunt
TFLint plugins

Without automation:

versions age
security fixes get missed
upgrades become huge
Code language: JavaScript (javascript)

Renovate automatically creates dependency-update PRs.

Terraform support includes providers, modules and core version references.

Example renovate.json

{
  "extends": [
    "config:recommended"
  ],

  "labels": [
    "dependencies"
  ],

  "packageRules": [
    {
      "matchManagers": [
        "terraform",
        "terraform-version"
      ],
      "groupName": "terraform dependencies"
    },

    {
      "matchManagers": [
        "github-actions"
      ],
      "groupName": "github actions"
    }
  ]
}
Code language: JSON / JSON with Comments (json)

Recommended workflow:

Renovate
   โ†“
Provider update PR
   โ†“
terraform init
   โ†“
lock-file update
   โ†“
tests
   โ†“
security
   โ†“
plan
   โ†“
review

Do not automatically merge major provider versions.


23. tfupdate

tfupdate is a focused CLI that can update:

  • Terraform/OpenTofu versions
  • providers
  • modules
  • associated lock information

Good for:

local scripts
migration tooling
custom automation
one-time upgrades

For ongoing repository dependency management:

Renovate > tfupdate

because Renovate also manages:

PR creation
scheduling
grouping
release tracking
many other dependency types

PART 17 โ€” TERRAFORM CONSOLE

terraform console is one of Terraform’s most underrated productivity tools.

Start:

terraform console
Code language: JavaScript (javascript)

Strings

> upper("production")
"PRODUCTION"
Code language: JavaScript (javascript)
> replace("prod-app", "prod", "staging")
"staging-app"
Code language: JavaScript (javascript)

Lists

> length(["a", "b", "c"])
3
Code language: CSS (css)
> element(["dev", "stage", "prod"], 2)
"prod"
Code language: JavaScript (javascript)

Maps

> lookup({dev="t3.micro", prod="m6i.large"}, "prod")
"m6i.large"
Code language: JavaScript (javascript)

Sets

> toset(["a", "a", "b"])
toset([
  "a",
  "b",
])
Code language: JavaScript (javascript)

Conditional

> true ? "production" : "development"
"production"
Code language: JavaScript (javascript)

CIDR

> cidrsubnet("10.0.0.0/16", 8, 1)
"10.0.1.0/24"
Code language: JavaScript (javascript)
> cidrhost("10.0.1.0/24", 10)
"10.0.1.10"
Code language: JavaScript (javascript)

for Expression

> [for x in ["dev", "prod"] : upper(x)]
[
  "DEV",
  "PROD",
]
Code language: JavaScript (javascript)

Map Transformation

> {for x in ["dev", "prod"] : x => upper(x)}
{
  "dev" = "DEV"
  "prod" = "PROD"
}
Code language: JavaScript (javascript)

Use console before writing complicated expressions directly into a module.


PART 18 โ€” IDE + AI DEVELOPMENT WORKFLOW

Modern workflow:

VS Code / Cursor / Claude Code
             โ†“
Terraform Extension
             โ†“
terraform-ls
             โ†“
Terraform MCP
             โ†“
Terraform Registry
             โ†“
Generated / Edited Terraform
             โ†“
terraform fmt
             โ†“
terraform validate
             โ†“
TFLint
             โ†“
Trivy
             โ†“
terraform test
             โ†“
terraform plan

AI Should Accelerate

boilerplate
module structures
variable definitions
outputs
tests
documentation
refactoring
provider-document research
plan explanation
migration suggestions
Code language: JavaScript (javascript)

Human Review Must Own

architecture
security boundaries
IAM
network exposure
state manipulation
cost decisions
resource replacement
production approval

GOLD rule:

AI can write Terraform. Terraform’s tooling pipeline decides whether that Terraform deserves to continue toward production.


PART 19 โ€” TERRAFORM DEVELOPER DAILY WORKFLOW

1. Pull Latest Code

git checkout main
git pull

2. Select Terraform Version

tenv tf use
terraform version
Code language: PHP (php)

3. Create Branch

git checkout -b feat/add-app-vpc

4. Write Terraform

Use:

IDE
terraform-ls
MCP/Registry
approved modules

5. Format

terraform fmt -recursive

6. Validate

terraform init
terraform validate

7. Lint

tflint --init
tflint

8. Security Scan

trivy config .

9. Test

terraform test

10. Generate Documentation

terraform-docs .

11. Plan

terraform plan

Carefully examine:

create
update
destroy
replace
IAM
network
state

12. Cost

infracost scan

13. Commit

git add .
git commit -m "feat: add application VPC"
Code language: JavaScript (javascript)

14. Pre-Commit Runs

fmt
validate
TFLint
Trivy
docs

15. Push

git push -u origin feat/add-app-vpc

16. CI

Runs the authoritative quality gates.

17. Pull Request

Reviewer evaluates both:

code diff
Terraform plan

18. Policy

Sentinel/OPA evaluates organizational rules.

19. Apply

Prefer controlled remote execution.

20. Verify

After apply:

service health
metrics
logs
alerts
cloud state
Terraform outputs
drift

A successful terraform apply means Terraform finished.

It does not automatically mean the application is healthy.


PART 20 โ€” COMPLETE DEVELOPER TOOLCHAIN

ToolProblem SolvedBasic UsageLimitationStatus
Terraform CLIInfrastructure lifecycleterraform planNot a full governance platformMUST HAVE
VS CodeEditingOpen repositoryGeneral-purpose editorMUST HAVE/Equivalent
Terraform ExtensionTerraform-aware IDEInstall extensionVS Code-specificMUST HAVE for VS Code
terraform-lsLanguage intelligenceUsually extension-managedNot lint/securityMUST HAVE via IDE
Terraform MCPAI groundingConnect AI clientAI still needs reviewRECOMMENDED
tenvVersion consistencytenv tf useAnother tool to maintainRECOMMENDED
TFLintStatic qualitytflintNot security scannerMUST HAVE professionally
TrivySecurity/IaC scanningtrivy config .Policies need tuningMUST HAVE professionally
CheckovAdditional policy scanningcheckov -d .Can overlap TrivyOPTIONAL
terraform testNative module teststerraform testApply tests cost moneyMUST HAVE professionally
TerratestIntegration testinggo testSlower/more complexOPTIONAL
terraform-docsGenerated docsterraform-docsDoes not write architecture docsRECOMMENDED
InfracostCost feedbackinfracost scanEstimates, not invoicesRECOMMENDED
pre-commit-terraformLocal automationpre-commit runCan be bypassedRECOMMENDED
GitVersion controlgit commitNot Terraform-specificMUST HAVE
GitHubCollaborationPRHosted platformRECOMMENDED
GitLabCollaborationMRAlternative to GitHubRECOMMENDED
GitHub ActionsCI/CDworkflow YAMLGeneral CI, DIY governanceRECOMMENDED
GitLab CICI/CD.gitlab-ci.ymlSameRECOMMENDED
TerragruntMulti-root orchestrationterragrunt run --all planAdditional abstractionOPTIONAL
AtlantisPR Terraform automationatlantis planRequires operationOPTIONAL
HCP TerraformTerraform platformRemote runsPlatform dependency/costENTERPRISE/TEAMS
Terraform EnterpriseSelf-hosted platformRemote runsOperational complexityENTERPRISE
SentinelHashiCorp policypolicy setsHashiCorp-specificENTERPRISE
OPAVendor-neutral policyRegoLearning curveENTERPRISE
ConftestLocal Rego validationconftest testRunner, not governance platformOPTIONAL
RenovateDependency updatesAutomated PRsRequires policy/tuningRECOMMENDED
tfupdateTargeted version updatesCLINarrower than RenovateOPTIONAL

PART 21 โ€” RECOMMENDED STACKS

Beginner Terraform Developer

Terraform CLI
      โ†“
VS Code
      โ†“
Terraform Extension
      โ†“
terraform fmt
      โ†“
terraform validate
      โ†“
Git

Goal:

Learn Terraform itself before introducing orchestration layers.


Professional Terraform Developer

VS Code
   โ†“
Terraform Extension / terraform-ls
   โ†“
tenv
   โ†“
Terraform CLI
   โ†“
fmt + validate
   โ†“
TFLint
   โ†“
Trivy
   โ†“
terraform test
   โ†“
terraform-docs
   โ†“
pre-commit
   โ†“
GitHub Actions / GitLab CI

This should be the default professional baseline.


Senior / Platform Engineer

VS Code / Cursor / Claude Code
      โ†“
Terraform MCP
      โ†“
tenv
      โ†“
Terraform
      โ†“
TFLint
      โ†“
Trivy
      โ†“
terraform test
      โ†“
terraform-docs
      โ†“
Infracost
      โ†“
pre-commit
      โ†“
CI
      โ†“
HCP Terraform

Add Terragrunt only when environment/root-module complexity requires it.


Enterprise Platform

Developer IDE
      โ†“
AI + Terraform MCP
      โ†“
Approved Modules
      โ†“
Local Quality Gates
      โ†“
GitHub / GitLab
      โ†“
CI
      โ†“
Security + Cost
      โ†“
HCP Terraform / Terraform Enterprise
      โ†“
Private Registry
      โ†“
OPA / Sentinel
      โ†“
Approval
      โ†“
Remote Apply
      โ†“
Audit + Drift Detection
Code language: PHP (php)

PART 22 โ€” GOLD STANDARD TERRAFORM PIPELINE

Developer
   โ†“
IDE
   โ†“
AI/MCP Assistance
   โ†“
Terraform Code
   โ†“
Format
   โ†“
Validate
   โ†“
Lint
   โ†“
Security Scan
   โ†“
Test
   โ†“
Documentation
   โ†“
Cost Analysis
   โ†“
Pre-Commit
   โ†“
Git Push
   โ†“
CI
   โ†“
Terraform Plan
   โ†“
Plan Security Scan
   โ†“
Policy Validation
   โ†“
Code Review
   โ†“
Approval
   โ†“
Terraform Apply
   โ†“
Post-Deployment Verification
   โ†“
Monitoring
   โ†“
Drift Detection

Quality Gates

Gate 1 โ€” Format

Question:

Is code consistently formatted?

Gate 2 โ€” Validate

Is it valid Terraform?

Gate 3 โ€” Lint

Are there obvious Terraform/provider quality problems?

Gate 4 โ€” Security

Does this introduce known insecure infrastructure?
Code language: JavaScript (javascript)

Gate 5 โ€” Test

Does the module behave as designed?
Code language: JavaScript (javascript)

Gate 6 โ€” Documentation

Did interface documentation stay synchronized?
Code language: PHP (php)

Gate 7 โ€” Cost

What does this change cost?
Code language: JavaScript (javascript)

Gate 8 โ€” Plan

What will Terraform actually change?

Gate 9 โ€” Policy

Does the plan comply with organizational requirements?
Code language: JavaScript (javascript)

Gate 10 โ€” Human Review

Should we make this change?
Code language: JavaScript (javascript)

Gate 11 โ€” Approval

Is this authorized for this environment?
Code language: JavaScript (javascript)

Gate 12 โ€” Apply

Execute the approved change.

Gate 13 โ€” Verification

Did infrastructure AND workload health remain correct?

PART 23 โ€” LOCAL DEVELOPMENT SETUP

macOS

HashiCorp’s official Homebrew installation:

brew tap hashicorp/tap
brew install hashicorp/tap/terraform

Recommended stack:

brew install tenv
brew install terraform-linters/tap/tflint
brew install trivy
brew install terraform-docs
brew install infracost
brew install pre-commit
brew install git

Verify:

terraform version
tenv --version
tflint --version
trivy --version
terraform-docs --version
infracost --version
pre-commit --version
git --version

Linux

Terraform should preferably come from HashiCorp’s official package repository rather than random binary mirrors.

Other tools can use their official package repositories/installers.

Generic verification:

terraform version
tflint --version
trivy --version
terraform-docs --version

For Checkov:

python3 -m pip install --user pipx
pipx install checkov

Windows

Terraform:

winget search Terraform

tenv:

winget install Tofuutils.Tenv
Code language: CSS (css)

TFLint:

winget install -e --id TerraformLinters.tflint
Code language: CSS (css)

Checkov:

pipx install checkov

HashiCorp notes that some Windows community package-manager distributions are community-maintained rather than official HashiCorp repositories, so enterprise environments should standardize and verify their software distribution source.


Local Setup Checklist

[ ] Git installed
[ ] Terraform version manager installed
[ ] Terraform stable version selected
[ ] VS Code/Cursor installed
[ ] HashiCorp Terraform extension installed
[ ] terraform-ls working
[ ] TFLint installed
[ ] Trivy installed
[ ] terraform-docs installed
[ ] pre-commit installed
[ ] Infracost installed if used
[ ] Terraform MCP configured if AI workflow used
[ ] Cloud SSO/profile configured
[ ] No permanent cloud access keys stored in repository
[ ] pre-commit install completed

PART 24 โ€” SAMPLE REAL AWS PROJECT

Architecture

Internet
   โ†“
Public Subnet
   โ†“
Security Group
   โ†“
EC2

Inside:

VPC
โ”œโ”€โ”€ Public Subnet A
โ”œโ”€โ”€ Public Subnet B
โ”œโ”€โ”€ Internet Gateway
โ”œโ”€โ”€ Route Table
โ””โ”€โ”€ EC2
Code language: PHP (php)

Directory:

aws-web/
โ”œโ”€โ”€ versions.tf
โ”œโ”€โ”€ backend.tf
โ”œโ”€โ”€ providers.tf
โ”œโ”€โ”€ variables.tf
โ”œโ”€โ”€ network.tf
โ”œโ”€โ”€ compute.tf
โ”œโ”€โ”€ outputs.tf
โ”œโ”€โ”€ terraform.tfvars.example
โ”œโ”€โ”€ backend.hcl.example
โ”œโ”€โ”€ tests/
โ”‚   โ””โ”€โ”€ main.tftest.hcl
โ”œโ”€โ”€ .tflint.hcl
โ”œโ”€โ”€ .terraform-docs.yml
โ”œโ”€โ”€ .pre-commit-config.yaml
โ””โ”€โ”€ .github/
    โ””โ”€โ”€ workflows/
        โ””โ”€โ”€ terraform.yml

versions.tf

terraform {
  required_version = "~> 1.16.0"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 6.59"
    }
  }
}
Code language: JavaScript (javascript)

AWS provider 6.59.0 was the latest indexed stable release in August 2026 when this guide was verified.

After initialization:

terraform init
git add .terraform.lock.hcl
Code language: CSS (css)

backend.tf

terraform {
  backend "s3" {}
}
Code language: JavaScript (javascript)

backend.hcl.example:

bucket       = "company-terraform-state"
key          = "aws-web/development/terraform.tfstate"
region       = "us-east-1"
encrypt      = true
use_lockfile = true
Code language: JavaScript (javascript)

Initialize:

terraform init -backend-config=backend.hcl

Modern S3 backends support native S3 state locking through:

use_lockfile = true
Code language: JavaScript (javascript)

HashiCorp currently marks DynamoDB-based S3 backend locking as deprecated. Bucket versioning is also strongly recommended for state recovery.

Do not put credentials in backend.hcl.

HashiCorp specifically recommends environment-based credentials/partial configuration because backend credentials can otherwise be persisted under .terraform and in plan artifacts.


providers.tf

provider "aws" {
  region = var.aws_region

  default_tags {
    tags = {
      Environment = var.environment
      ManagedBy   = "Terraform"
      Project     = var.project
    }
  }
}
Code language: JavaScript (javascript)

No credentials.

Local authentication can come from:

AWS IAM Identity Center
AWS profile
environment temporary credentials
credential_process
assume-role

CI should use OIDC.


variables.tf

variable "aws_region" {
  description = "AWS region."
  type        = string
  default     = "us-east-1"
}

variable "environment" {
  description = "Deployment environment."
  type        = string

  validation {
    condition = contains(
      ["development", "staging", "production"],
      var.environment
    )

    error_message = "Environment must be development, staging or production."
  }
}

variable "project" {
  description = "Project name."
  type        = string
  default     = "gold-web"
}

variable "vpc_cidr" {
  description = "CIDR for the VPC."
  type        = string
  default     = "10.20.0.0/16"
}

variable "ami_id" {
  description = "Approved AMI ID."
  type        = string

  validation {
    condition     = startswith(var.ami_id, "ami-")
    error_message = "ami_id must be an AWS AMI ID."
  }
}

variable "instance_type" {
  description = "EC2 instance type."
  type        = string
  default     = "t3.micro"
}
Code language: JavaScript (javascript)

network.tf

resource "aws_vpc" "main" {
  cidr_block           = var.vpc_cidr
  enable_dns_support   = true
  enable_dns_hostnames = true

  tags = {
    Name = "${var.project}-${var.environment}-vpc"
  }
}

resource "aws_internet_gateway" "main" {
  vpc_id = aws_vpc.main.id

  tags = {
    Name = "${var.project}-${var.environment}-igw"
  }
}

resource "aws_subnet" "public_a" {
  vpc_id                  = aws_vpc.main.id
  cidr_block              = cidrsubnet(var.vpc_cidr, 8, 1)
  availability_zone       = "${var.aws_region}a"
  map_public_ip_on_launch = true

  tags = {
    Name = "${var.project}-${var.environment}-public-a"
  }
}

resource "aws_subnet" "public_b" {
  vpc_id                  = aws_vpc.main.id
  cidr_block              = cidrsubnet(var.vpc_cidr, 8, 2)
  availability_zone       = "${var.aws_region}b"
  map_public_ip_on_launch = true

  tags = {
    Name = "${var.project}-${var.environment}-public-b"
  }
}

resource "aws_route_table" "public" {
  vpc_id = aws_vpc.main.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.main.id
  }

  tags = {
    Name = "${var.project}-${var.environment}-public"
  }
}

resource "aws_route_table_association" "public_a" {
  subnet_id      = aws_subnet.public_a.id
  route_table_id = aws_route_table.public.id
}

resource "aws_route_table_association" "public_b" {
  subnet_id      = aws_subnet.public_b.id
  route_table_id = aws_route_table.public.id
}
Code language: PHP (php)

compute.tf

resource "aws_security_group" "web" {
  name_prefix = "${var.project}-${var.environment}-web-"
  description = "HTTP access to demonstration web instance"
  vpc_id      = aws_vpc.main.id

  ingress {
    description = "HTTP"
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    description = "Outbound connectivity"
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  lifecycle {
    create_before_destroy = true
  }

  tags = {
    Name = "${var.project}-${var.environment}-web"
  }
}

resource "aws_instance" "web" {
  ami                         = var.ami_id
  instance_type               = var.instance_type
  subnet_id                   = aws_subnet.public_a.id
  vpc_security_group_ids      = [aws_security_group.web.id]
  associate_public_ip_address = true

  metadata_options {
    http_endpoint = "enabled"
    http_tokens   = "required"
  }

  root_block_device {
    encrypted = true
  }

  tags = {
    Name = "${var.project}-${var.environment}-web"
  }
}
Code language: JavaScript (javascript)

Notice what is deliberately missing:

SSH 0.0.0.0/0
access keys
passwords
secret user_data
unencrypted disk
IMDSv1

outputs.tf

output "vpc_id" {
  description = "VPC ID."
  value       = aws_vpc.main.id
}

output "public_subnet_ids" {
  description = "Public subnet IDs."
  value = [
    aws_subnet.public_a.id,
    aws_subnet.public_b.id
  ]
}

output "instance_id" {
  description = "EC2 instance ID."
  value       = aws_instance.web.id
}

output "public_ip" {
  description = "Public IPv4 address."
  value       = aws_instance.web.public_ip
}
Code language: JavaScript (javascript)

terraform.tfvars.example

aws_region    = "us-east-1"
environment   = "development"
project       = "gold-web"
vpc_cidr      = "10.20.0.0/16"
ami_id        = "ami-REPLACE_WITH_APPROVED_AMI"
instance_type = "t3.micro"
Code language: JavaScript (javascript)

tests/main.tftest.hcl

variables {
  environment   = "development"
  aws_region    = "us-east-1"
  project       = "gold-web"
  vpc_cidr      = "10.20.0.0/16"
  ami_id        = "ami-1234567890abcdef0"
  instance_type = "t3.micro"
}

run "plan_defaults" {
  command = plan

  assert {
    condition     = aws_vpc.main.cidr_block == "10.20.0.0/16"
    error_message = "Unexpected VPC CIDR."
  }

  assert {
    condition     = aws_vpc.main.enable_dns_support
    error_message = "VPC DNS support must be enabled."
  }

  assert {
    condition     = aws_instance.web.instance_type == "t3.micro"
    error_message = "Unexpected instance type."
  }

  assert {
    condition     = aws_instance.web.metadata_options[0].http_tokens == "required"
    error_message = "IMDSv2 must be required."
  }
}
Code language: JavaScript (javascript)

Development Workflow

terraform fmt -recursive

terraform init \
  -backend-config=backend.hcl

terraform validate

tflint --init
tflint

trivy config .

terraform test

terraform plan \
  -var-file=terraform.tfvars \
  -out=tfplan

terraform show tfplan
Code language: JavaScript (javascript)

That single example now has:

networking
compute
variables
outputs
provider constraints
remote state
locking
tests
linting
security
documentation
pre-commit
CI

which is the minimum shape of a professional Terraform repository.


PART 25 โ€” COMMON DEVELOPER MISTAKES

1. Hardcoded Access Keys

Bad:

provider "aws" {
  access_key = "..."
  secret_key = "..."
}
Code language: JavaScript (javascript)

Correct:

local โ†’ SSO / temporary profile
CI    โ†’ OIDC

2. Hardcoded Secrets

Bad:

password = "SuperSecret123"
Code language: JavaScript (javascript)

Use:

AWS Secrets Manager
Azure Key Vault
GCP Secret Manager
Vault
HCP variable security

But remember:

A secret referenced or generated by Terraform can still reach Terraform state.


3. Committing State

Never:

terraform.tfstate
terraform.tfstate.backup
Code language: CSS (css)

State can contain highly sensitive information.

HashiCorp explicitly warns that Terraform state and plan data may contain sensitive values. Local state is plaintext, while remote state should be encrypted and access-controlled.


4. Committing .terraform

Do not.

It contains downloaded providers/modules and local initialization data.


5. No Provider Constraints

Bad:

aws = {
  source = "hashicorp/aws"
}
Code language: JavaScript (javascript)

Better:

aws = {
  source  = "hashicorp/aws"
  version = "~> 6.59"
}
Code language: JavaScript (javascript)

6. Unversioned Modules

Bad:

source = "git::https://example/module.git"
Code language: JavaScript (javascript)

Better:

source = "git::https://example/module.git?ref=v3.2.1"
Code language: JavaScript (javascript)

7. Applying Production From Laptops

Better:

CI / HCP
+ approval
+ audit
+ temporary identity

8. No Code Review

Infrastructure is production code.

Use PRs.


9. No Linting

Use TFLint.


10. No Security Scanning

Use Trivy.


11. No Tests

Use terraform test.


12. No State Locking

Concurrent state modification can corrupt workflow assumptions.

Use backend locking.

For current S3 backends:

use_lockfile = true
Code language: JavaScript (javascript)

13. Overusing Workspaces

Do not use CLI workspaces as a substitute for serious security/environment boundaries.


14. Giant Root Modules

Bad:

10,000 Terraform resources
one state

Every plan becomes slow and high-risk.

Split by lifecycle/ownership/blast radius.


15. Copy-Paste Infrastructure

Extract stable repeating patterns into modules.


16. Poor Variables

Avoid:

variable "config" {
  type = any
}
Code language: JavaScript (javascript)

when a precise type is possible.

Prefer:

variable "config" {
  type = object({
    instance_type = string
    replicas      = number
  })
}
Code language: JavaScript (javascript)

17. Excessive -target

-target is an exceptional recovery/troubleshooting tool.

It should not become the normal deployment mechanism.


18. Manual State Operations

Avoid casual:

terraform state rm
terraform state mv

Prefer declarative migration constructs when possible.


19. Blind AI Terraform

AI output should still pass:

Registry/MCP verification
fmt
validate
lint
security
test
plan
human review

20. Ignoring Cost

A Terraform PR can be syntactically perfect and financially disastrous.


PART 26 โ€” TERRAFORM SECURITY BEST PRACTICES

[ ] No hardcoded credentials
[ ] CI uses OIDC/workload identity
[ ] Human users use SSO/temporary credentials
[ ] Least-privilege execution roles
[ ] State encrypted at rest
[ ] State encrypted in transit
[ ] State access tightly restricted
[ ] State bucket/object versioning enabled
[ ] State locking enabled
[ ] Secrets not embedded in HCL
[ ] Plan artifacts treated as sensitive
[ ] Trivy/security scanning enabled
[ ] Policy-as-code for organization-wide controls
[ ] Protected main branch
[ ] Production approval enabled
[ ] Terraform version constrained
[ ] Provider versions constrained
[ ] Provider lock file committed
[ ] Reusable modules versioned
[ ] Production apply separated from developer permissions
[ ] Audit trail maintained
[ ] Drift detection established
[ ] Destructive actions reviewed
[ ] Resource replacement reviewed
[ ] AI agents cannot casually apply production infrastructure
Code language: JavaScript (javascript)

PART 27 โ€” TERRAFORM CODE REVIEW CHECKLIST

Terraform Pull Request Review

CODE
[ ] terraform fmt passes
[ ] terraform validate passes
[ ] TFLint passes
[ ] naming matches standards
[ ] no needless complexity
[ ] no copy/paste that should become a module

VERSIONS
[ ] Terraform version constrained
[ ] providers constrained
[ ] module versions constrained
[ ] .terraform.lock.hcl reviewed when changed

VARIABLES
[ ] variable types are explicit
[ ] descriptions provided
[ ] validations added where useful
[ ] secrets are not defaulted

OUTPUTS
[ ] outputs are genuinely useful
[ ] sensitive values marked sensitive
[ ] unnecessary data is not exposed

SECURITY
[ ] no credentials
[ ] no secrets
[ ] least privilege IAM
[ ] network exposure intentional
[ ] encryption enabled
[ ] Trivy passes

PLAN
[ ] create count expected
[ ] update count expected
[ ] destroy count expected
[ ] replacements understood
[ ] IAM changes reviewed
[ ] network changes reviewed
[ ] state movement understood
[ ] blast radius acceptable

COST
[ ] cost increase understood
[ ] expensive resources justified

TESTING
[ ] terraform test passes
[ ] integration tests added where needed

DOCUMENTATION
[ ] module README current
[ ] architecture notes updated if behavior changed

OPERATIONS
[ ] rollback/recovery strategy understood
[ ] monitoring impact understood
[ ] post-deployment verification defined
Code language: JavaScript (javascript)

PART 28 โ€” TERRAFORM PRODUCTIVITY BEST PRACTICES

1. Standardize Modules

Developers should not reinvent:

VPC
EKS
RDS
IAM role
KMS
S3 baseline
logging
monitoring

for every project.


2. Standard Module Contract

Every production module should ideally contain:

main.tf
variables.tf
outputs.tf
versions.tf
README.md
examples/
tests/

3. IDE Feedback

Catch errors in seconds, not CI minutes.


4. AI + MCP

Use AI for creation speed, MCP for current Terraform context.


5. Pre-Commit

Automate repetitive checks.


6. Repository Templates

Offer teams a bootstrap:

terraform-new-project
Code language: JavaScript (javascript)

that already contains:

CI
TFLint
Trivy
tests
docs
pre-commit
Renovate
CODEOWNERS
PR template

7. Reusable CI Templates

Don’t maintain 200 hand-written Terraform pipelines.

Create:

company/terraform-ci

centrally.


8. Dependency Automation

Use Renovate.

Small routine upgrades are safer than annual giant upgrades.


9. Automated Documentation

Use terraform-docs.


10. Cost Feedback

Give developers cost information before merge.


11. Remote Execution

Production should not depend on:

Raj's laptop
Sarah's VPN
someone's local AWS profile
Code language: PHP (php)

12. Small State Boundaries

State should align with:

ownership
lifecycle
failure domain
security boundary
blast radius

not arbitrary folder organization.


PART 29 โ€” TOOL OVERLAP AND DECISION GUIDE

terraform validate vs TFLint

validate โ†’ correctness
TFLint   โ†’ quality/provider lint

Use both.


Trivy vs Checkov

Default โ†’ Trivy

Add Checkov only when its policy capabilities provide
specific additional value.
Code language: PHP (php)

Sentinel vs OPA

HashiCorp-focused โ†’ Sentinel is reasonable
Vendor-neutral    โ†’ OPA
Existing Rego     โ†’ OPA

Do not automatically run both.


tfenv vs tenv

New Terraform-focused setup โ†’ tenv
Existing stable tfenv setup โ†’ migration not urgent
Code language: PHP (php)

Terraform vs Terragrunt

Terraform is mandatory.

Terragrunt is optional orchestration.


GitHub Actions vs Atlantis

GitHub Actions โ†’ general CI

Atlantis โ†’ Terraform PR execution

You can integrate them, but do not add Atlantis merely to duplicate CI.


Atlantis vs HCP Terraform

Atlantis
     โ†“
PR Terraform automation

HCP Terraform
     โ†“
Terraform platform

If HCP Terraform already owns remote runs, Atlantis is usually unnecessary.


terraform test vs Terratest

terraform test
      โ†“
default

Terratest
      โ†“
real external-system/integration verification
Code language: JavaScript (javascript)

terraform-docs vs Manual README

Not competitors.

terraform-docs
      โ†“
machine-derived module interface

Human README
      โ†“
purpose + architecture + examples + operations
Code language: JavaScript (javascript)

Use both in the same README.


GitHub Actions vs HCP Terraform

A very strong enterprise pattern is:

GitHub Actions
      โ†“
code-quality pipeline

HCP Terraform
      โ†“
Terraform execution/governance pipeline

Each owns a different responsibility.


PART 30 โ€” FINAL GOLD STANDARD RECOMMENDATION

Final Terraform Developer Stack

VS Code / Cursor / Claude Code
        โ†“
HashiCorp Terraform Extension
        โ†“
terraform-ls
        โ†“
Terraform MCP Server
        โ†“
tenv
        โ†“
Terraform CLI
        โ†“
terraform fmt
        โ†“
terraform validate
        โ†“
terraform test
        โ†“
TFLint
        โ†“
Trivy
        โ†“
terraform-docs
        โ†“
Infracost
        โ†“
pre-commit-terraform
        โ†“
Git
        โ†“
GitHub / GitLab
        โ†“
GitHub Actions / GitLab CI
        โ†“
HCP Terraform
        โ†“
Sentinel / OPA
        โ†“
Cloud Infrastructure
        โ†“
Monitoring + Drift Detection

Tool Priority

MUST HAVE

Terraform CLI
Git
Terraform version constraints
Provider constraints
.terraform.lock.hcl
terraform fmt
terraform validate
Remote state
State locking
Code review
CI
Code language: CSS (css)

For professional teams also treat these as baseline:

TFLint
Trivy
terraform test
OIDC/workload identity

STRONGLY RECOMMENDED

VS Code/Cursor + Terraform Extension
terraform-ls
tenv
terraform-docs
pre-commit-terraform
Infracost
Renovate
standard module templates
standard CI templates
Code language: JavaScript (javascript)

OPTIONAL

Terraform MCP Server
Checkov
Terratest
Terragrunt
Atlantis
Conftest
tfupdate

“Optional” does not mean low quality.

It means:

Introduce the tool when the problem it solves actually exists.


ENTERPRISE

HCP Terraform
Terraform Enterprise
Private Module Registry
Private Provider Registry
Sentinel
OPA governance
Run Tasks
RBAC
Audit
Drift Detection
Central agent pools
Central policy sets
Code language: PHP (php)

The Recommended Minimal Professional Stack

If I had to standardize Terraform development for a new engineering organization today without unnecessary complexity, I would start with:

VS Code / Cursor
        โ†“
HashiCorp Terraform Extension
        โ†“
tenv
        โ†“
Terraform 1.16.x
        โ†“
terraform fmt
        โ†“
terraform validate
        โ†“
TFLint
        โ†“
Trivy
        โ†“
terraform test
        โ†“
terraform-docs
        โ†“
pre-commit-terraform
        โ†“
GitHub
        โ†“
GitHub Actions
        โ†“
OIDC
        โ†“
Remote State / HCP Terraform

Then add:

Terraform MCP

for AI-heavy development.

Add:

Infracost

for meaningful cloud-cost environments.

Add:

Terragrunt

only when many accounts/regions/root modules make live Terraform configuration repetitive.

Add:

OPA / Sentinel

when organizational policies require centralized enforcement.

Add:

HCP Terraform / Terraform Enterprise

when centralized execution, RBAC, registry, audit, private connectivity, governance and drift detection justify operating a Terraform platform.


GOLD STANDARD DESIGN PRINCIPLE

Every tool must answer one question:

What engineering problem does this solve?
Code language: JavaScript (javascript)

Examples:

terraform-ls
โ†’ writing Terraform faster and more accurately

tenv
โ†’ version consistency

terraform fmt
โ†’ formatting consistency

terraform validate
โ†’ configuration correctness

TFLint
โ†’ code quality

Trivy
โ†’ infrastructure security

terraform test
โ†’ behavioral correctness

terraform-docs
โ†’ documentation drift

Infracost
โ†’ cost visibility

pre-commit
โ†’ fast developer feedback

GitHub Actions
โ†’ authoritative automation

HCP Terraform
โ†’ execution/state/governance

OPA/Sentinel
โ†’ organization-wide policy

Renovate
โ†’ dependency drift

Terraform MCP
โ†’ grounded AI assistance

If you cannot clearly answer that question for a proposed Terraform tool, do not add it.


TERRAFORM DEVELOPER GOLD STANDARD CHECKLIST

Before merging any Terraform pull request:

SOURCE
[ ] Terraform version pinned/constrained
[ ] Provider versions constrained
[ ] Module versions constrained
[ ] .terraform.lock.hcl correct

QUALITY
[ ] terraform fmt -check passes
[ ] terraform validate passes
[ ] TFLint passes

SECURITY
[ ] Trivy passes
[ ] No credentials committed
[ ] No secrets committed
[ ] IAM reviewed
[ ] Network exposure reviewed
[ ] Encryption reviewed

TESTING
[ ] terraform test passes
[ ] Integration tests pass where required

DOCUMENTATION
[ ] terraform-docs current
[ ] README/architecture documentation current

PLAN
[ ] Terraform plan reviewed
[ ] Creates understood
[ ] Updates understood
[ ] Deletes understood
[ ] Replacements understood
[ ] State movement understood
[ ] Blast radius acceptable

COST
[ ] Cost difference reviewed
[ ] Unexpected billable resources investigated

GOVERNANCE
[ ] OPA/Sentinel policies pass where applicable
[ ] Required reviewers approved
[ ] Production approval satisfied

EXECUTION
[ ] CI uses temporary identity/OIDC
[ ] No long-lived cloud credentials
[ ] Remote state protected
[ ] State locking enabled
[ ] Production apply controlled

POST DEPLOYMENT
[ ] Infrastructure verified
[ ] Application/service health verified
[ ] Monitoring checked
[ ] Unexpected drift absent
Code language: PHP (php)

FINAL GOLD RULE

WRITE
  โ†“
FORMAT
  โ†“
VALIDATE
  โ†“
LINT
  โ†“
SECURE
  โ†“
TEST
  โ†“
DOCUMENT
  โ†“
PRICE
  โ†“
PLAN
  โ†“
REVIEW
  โ†“
POLICY
  โ†“
APPROVE
  โ†“
APPLY
  โ†“
VERIFY
  โ†“
MONITOR

That is the Terraform Developer GOLD Standard.

Terraform is not merely a tool for creating infrastructure.

At production scale it becomes an engineering discipline built around:

repeatability
automation
security
testing
review
governance
cost awareness
controlled execution
continuous verification

The goal is not to make developers run more tools.

The goal is to move mistakes as far left and as cheaply as possible, while making production changes predictable, reviewable, recoverable and auditable.

0 0 votes
Article Rating
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
0
Would love your thoughts, please comment.x