Validation, pre/postconditions and checks

Terraform · Advanced · 10 min

Failing early with a clear message beats a half-applied plan or a silently wrong deployment.

Terraform has several assertion mechanisms, each for a different moment:

  • Variable validation: inside a variable, one or more validation { condition = ... error_message = "..." } blocks. Evaluated at plan time. Since 1.9 conditions may also refer to other variables and objects, not only the variable itself.
  • precondition (in a resource's, data source's or output's lifecycle): checked before that object is planned or applied. Good for assumptions about inputs or data sources ("the AMI must be x86_64").
  • postcondition: checked after the object is created or read, with self referring to it ("the instance must have received a public IP").
  • check blocks: top-level check "name" { assert {...} }, optionally with a scoped data source, run on every plan and apply. A failure is a warning, not an error, so checks suit continuous health assertions like "the website returns 200".

Write error messages for the person who will hit them: say what's wrong and what to do.

Use can() in conditions to turn errors into booleans: can(regex("^[a-z0-9-]+$", var.name)). Keep conditions simple; complex logic hides mistakes.

These checks complement, and don't replace, terraform test and policy tools (OPA/conftest, Sentinel) that judge the plan as a whole.

Example

variable "env" {
  type = string
  validation {
    condition     = contains(["dev", "staging", "prod"], var.env)
    error_message = "env must be dev, staging or prod."
  }
}

variable "bucket_name" {
  type = string
  validation {
    condition     = can(regex("^[a-z0-9.-]{3,63}$", var.bucket_name))
    error_message = "Bucket names: 3-63 chars of a-z, 0-9, dots and hyphens."
  }
}

resource "terraform_data" "bucket" {
  input = var.bucket_name
  lifecycle {
    postcondition {
      condition     = startswith(self.output, var.env)
      error_message = "Bucket name must start with the environment name."
    }
  }
}

Remember

  • validation for inputs; pre/postconditions for assumptions about objects
  • check blocks warn on every run without blocking
  • can() turns failing expressions into false

Watch out for

  • Vague error messages like 'invalid input' that don't tell users how to fix it.

Check yourself

  1. Which mechanism reports a failure as a warning rather than stopping the apply?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: developer.hashicorp.com/terraform/language/expressions/custom-conditions