DevOps Peach

Advanced Terraform · Testing and CI/CD · Lesson 1 of 4

Formatting, Validation, Linting and Scanning

Cheap checks first: terraform fmt and validate, TFLint for provider-aware mistakes, and Trivy or Checkov for insecure settings, wired into pre-commit and CI.

Practice interactively →

The cheapest bug is the one caught before plan. Static checks read the code only: no credentials, no state, seconds to run.

flowchart LR
  ED["Edit .tf"] --> FMT["terraform fmt"]
  FMT --> VAL["terraform validate"]
  VAL --> LINT["tflint"]
  LINT --> SEC["trivy config / checkov"]
  SEC --> PR["Pull request"]
  %% step ED: You save a change in a .tf file.
  %% step FMT: fmt rewrites the file into the canonical style, so diffs only show real changes.
  %% step VAL: validate checks syntax, references and types without contacting any API.
  %% step LINT: TFLint knows provider rules, such as invalid instance types or unused variables.
  %% step SEC: A misconfiguration scanner flags insecure settings such as public buckets or privileged containers.
  %% step PR: Only code that passes all checks reaches review.

The four layers

Tool Command Catches
fmt terraform fmt -check -recursive Style differences (exit 3 if files need formatting, CI fails)
validate terraform init -backend=false && terraform validate Syntax errors, unknown arguments, wrong types, bad references
TFLint tflint --init && tflint --recursive Deprecated syntax, unused declarations, provider-specific errors (with a ruleset plugin)
Trivy / Checkov trivy config . / checkov -d . Security misconfigurations against hundreds of built-in checks

validate needs providers but not a backend, so init -backend=false is the trick in CI. Note it cannot catch errors that only the API knows about (an image that does not exist, a name already taken): that is what plan and tests are for.

TFLint configuration

# .tflint.hcl
plugin "terraform" {
  enabled = true
  preset  = "recommended"
}
plugin "aws" {
  enabled = true
  version = "0.38.0"
  source  = "github.com/terraform-linters/tflint-ruleset-aws"
}

Suppressing a finding (with a reason)

resource "docker_container" "debug" {
  #trivy:ignore:AVD-DS-0002 debug tool, dev only, removed after the incident
  privileged = true
}

A suppression without a reason is a smell; reviewers should ask for one.

pre-commit

The pre-commit-terraform hooks run these tools on every commit:

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/antonbabenko/pre-commit-terraform
    rev: v1.99.0
    hooks:
      - id: terraform_fmt
      - id: terraform_validate
      - id: terraform_tflint
      - id: terraform_trivy

Run the same checks again in CI: hooks can be skipped with git commit --no-verify.

Traps: terraform fmt without -check rewrites files, which in CI hides the problem instead of failing; and a clean validate says nothing about whether apply will succeed.

Practice interactively →