PlatformEngr® Terraform course

Cheat notes · Module 06 · Safe change

Tests, checks and a pipeline on every pull request

A five-minute review for when you have done this module and want it back in your head. Open the full module for the steps and the lab, or the full text to print.

The idea

Some things can be proven from the files alone: format, validity, tests against a fake provider, and policy. A plan needs the real state and API, so it is not in a pull request workflow that cannot reach them. check blocks warn; validations and tests block.

Remember

  • terraform test runs *.tftest.hcl; each run plans the module and checks assert conditions.
  • mock_provider "kubernetes" {}: no cluster, no credentials; computed values are invented.
  • expect_failures = [var.environment]: passes only if that validation refuses the input.
  • check block: Warning: Check block assertion failed, and the plan carries on.
  • CI jobs: static (fmt, init -backend=false -lockfile=readonly, validate) per environment, test, policy (conftest on the HCL).
  • A module's own lock file is ignored by Git: a reusable module does not pin exact versions.

Pitfalls

  • All checks green while a rule is broken: a rule that has no test exists only in people's heads.
  • A new test that has never failed may assert nothing. Make it fail once for the reason it was written.
  • Expecting CI to catch a destroy: without a plan job, only the person who pastes the plan sees it.

Commands

terraform testRun the module's tests
terraform fmt -check -recursiveFail on unformatted files
terraform init -backend=false -lockfile=readonlyInit without state; fail if the lock file is wrong
docker run --rm -v "$PWD:/project" -w /project openpolicyagent/conftest:v0.71.1 test --parser hcl2 --ignore '\.terraform' -p policy envs modulesThe policy, on the files
gh pr checks --watchWait for the pull request's checks
gh run view --log-failedWhy a check failed

modules/team-namespace/tests/unit.tftest.hcl

mock_provider "kubernetes" {}

variables {
  team        = "checkout"
  environment = "dev"
}

run "deployer_cannot_read_secrets" {
  command = plan

  assert {
    condition     = !contains(flatten([for r in kubernetes_role_v1.deployer.rule : r.resources]), "secrets")
    error_message = "The deployer role must not include secrets."
  }
}

Test yourself

Answer in your head first, then open the answer.

What does a mocked test prove, and what not?

Your own expressions and defaults; nothing about what the API will answer.

A check assertion fails during apply. What happens?

A warning; the apply completes.

Why is there no plan job in this workflow?

The runner cannot reach the cluster or the state store.

Where module 06 leaves you: modules/team-namespace/tests/unit.tftest.hcl has four run blocks and terraform test passes. envs/dev and envs/prod each have a check block. .github/workflows/terraform.yaml runs three jobs on pull requests; none of them applies anything. One pull request has gone through it.