Terraform check and precondition Generator

Generate the three kinds of assertion Terraform has. Choosing between them comes down to one question, and most people get the answer backwards.

Paste below, or drop a file anywhere on this panel

Or drop a file anywhere on this panel. Nothing is uploaded: the analysis runs in this tab.

The answer appears here

Paste on the left and press Generate. Nothing leaves this tab.

Examples

Real input you can load into the tool above. Each one shows a different thing going wrong, because that is what the tool is for.

Assertions for a resource

The generated skeleton marks which condition can use self and which cannot

resource "aws_instance" "web" {
  ami           = var.ami_id
  instance_type = var.instance_type
}

A data source

Data sources take conditions too, and one inside a check block fails softly

data "aws_ami" "ubuntu" {
  most_recent = true
}

Common mistakes

These are the ones that fail silently. The config is accepted, nothing raises an error, and the consequence arrives later.

  1. Using a check block to stop a bad apply

    A failed check is a warning. The apply proceeds, so the guard exists, reports the problem, and changes nothing about the outcome.

    Instead:Use a precondition or a postcondition when the run must stop. Keep check blocks for what you want reported.

  2. Referring to self in a precondition

    It runs before the object exists, so there is nothing for self to refer to, and using the resource's own address instead is a dependency cycle.

    Instead:Check inputs and other resources in a precondition; check the result in a postcondition through self.

  3. Writing a generic error_message

    Terraform prints the message and the address and nothing else, so a message without the offending value leaves the reader to work out what happened from the configuration.

    Instead:Interpolate the value: "Expected at least two subnets, got ${length(var.subnets)}."

A check block warns. A precondition stops.

Almost everybody adding their first check block expects it to be a gate. It is a monitor: the assertion fails, a warning appears, and the apply succeeds.

check is a monitor, not a gate

Its assert produces a warning in the output and stops nothing. That is useful for something you want reported and are willing to proceed past, such as an endpoint that should be healthy. Used as a guard, the guard is there and does nothing, which is worse than no guard because everybody believes it.

precondition and postcondition fail the operation

These live inside a resource's lifecycle block and they do stop the run. A precondition is evaluated before Terraform creates or changes the object, so it can prevent the work. A postcondition runs after and checks the result.

self exists in a postcondition and not in a precondition

The precondition runs before the object exists, so there is nothing to look at, and referring to the resource by its own address is a dependency cycle. In a postcondition, self.attribute is how you check what was actually created.

A data source inside a check block is scoped to it

And its failure produces a warning rather than failing the plan. That is the reason to put a probe there rather than at the top level: an ordinary data source that cannot be read fails the plan, which is exactly what you do not want from something reaching out to the network.

error_message is the only thing the reader gets

Terraform prints the message and the address and nothing about the values, unless you interpolate them yourself. "Invalid configuration" tells somebody at three in the morning nothing at all; "Expected at least two subnets, got 1" tells them everything.

Preconditions arrived in 1.2 and check blocks in 1.5

On an older version each is reported as an unsupported block, naming the block rather than the version. Setting required_version turns that into a message about the version instead.