Terraform Configuration · Topic 4 of 4

Custom Conditions and Sensitive Data

Terraform Associate 004: validation, preconditions, postconditions and check blocks, plus sensitive values, ephemeral values, state risks and Vault.

  • 5 min
In a nutshell
  • Validation checks inputs; preconditions check assumptions; postconditions check results with self.
  • Validation, precondition and postcondition failures are errors; check block failures are warnings.
  • sensitive = true hides values in CLI output only; state and plan still contain them.

What it is

Custom conditions are rules you write so Terraform reports a clear error, or a warning, when values or infrastructure are not what you expect. They come in four forms: variable validation, preconditions, postconditions and check blocks.

Sensitive data means passwords, keys and tokens. Terraform can hide such values in CLI output, keep some of them out of state, and read them from HashiCorp Vault instead of from code.

variable validationbefore planpreconditionbefore the changepostconditionafter the changecheck blockend of run, warns only

How it works

Variable validation

A validation block inside a variable checks the input before Terraform builds a plan.

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

If the condition is false, Terraform shows the error_message and stops. Since Terraform 1.9 a validation condition can also refer to other variables and objects, not only its own variable.

Preconditions and postconditions

Both go in the lifecycle block of a resource or data source. A precondition checks assumptions before Terraform acts on the block; a postcondition checks the result, using self for the object's own attributes.

resource "aws_instance" "web" {
  ami           = data.aws_ami.app.id
  instance_type = var.instance_type
  lifecycle {
    postcondition {
      condition     = self.public_dns != ""
      error_message = "The instance needs a public DNS name."
    }
  }
}

A failed precondition or postcondition is an error that stops the operation. A failed postcondition does not undo changes already made. Output blocks can also contain a precondition, but not a postcondition.

Check blocks

A check block validates infrastructure as a whole, outside any single resource's lifecycle. It holds one or more assert blocks and can contain one nested data source used only inside the check.

check "site_up" {
  data "http" "home" {
    url = "https://${aws_lb.web.dns_name}"
  }
  assert {
    condition     = data.http.home.status_code == 200
    error_message = "The site did not return status 200."
  }
}

Checks run at the end of plan and apply. A failed assertion is only a warning; the run continues. In HCP Terraform, checks also run during health assessments.

FeatureWhere it goesWhen it runsOn failure
validationvariable blockBefore the planError, stops
preconditionlifecycle or outputBefore the block is acted onError, stops
postconditionlifecycleAfter the change or readError, stops
check with assertTop level check blockEnd of plan and applyWarning, continues

Sensitive values

Set sensitive = true on a variable or output to redact it in plan and apply output. Expressions built from a sensitive value are also treated as sensitive, and a root output that uses one must itself be marked sensitive.

The value is still stored in plain text in state and plan files. terraform output -json, terraform output -raw and terraform output NAME also print it in plain text.

Ephemeral values and write-only arguments

From Terraform 1.10, ephemeral = true on a variable or child module output, and the ephemeral resource block, give values that Terraform omits from state and plan files entirely. Root module outputs cannot be ephemeral.

From Terraform 1.11, providers can offer write-only arguments, usually ending in _wo, such as password_wo. They accept ephemeral values and are never stored, often paired with a _wo_version argument that you change to send a new value.

Secrets from Vault

The Vault provider reads secrets at run time so they never appear in code or variable files.

ephemeral "vault_kv_secret_v2" "db" {
  mount = "secret"
  name  = "prod/db"
}

The value is referenced as ephemeral.vault_kv_secret_v2.db.data.password. A Vault data source also works but stores what it reads in state, whereas the ephemeral resource does not. Vault can also issue short-lived dynamic credentials that expire after a set lease time.

Vaultsecret engineEphemeral resourceread at run timeWrite only argumentsent to providerStateno secret stored

Best practices

  • Never hard-code secrets in .tf files or commit them in .tfvars files.
  • Pass secrets through TF_VAR_ environment variables, Vault or HCP Terraform sensitive variables.
  • Store state in a remote backend with encryption and access control, because state can hold secrets.
  • Prefer ephemeral resources and write-only arguments where the provider supports them.

How the exam asks about this

  • "True or false: a password passed through a sensitive variable ends up in plain text in the state file." True; sensitive only redacts CLI output.
  • "An assertion that the website returns 200 fails during apply. What happens?" Terraform shows a warning and the apply continues.
  • "Reject any region outside an approved list before planning." Add a validation block to the variable.

What to remember for the exam

  • Validation checks inputs; preconditions check assumptions; postconditions check results with self.
  • Validation, precondition and postcondition failures are errors; check block failures are warnings.
  • sensitive = true hides values in CLI output only; state and plan still contain them.
  • Ephemeral values and write-only arguments are kept out of state and plan files.
  • Vault data sources store secrets in state; Vault ephemeral resources do not.
  • Protect state with a remote backend that encrypts it and restricts access.
Watch out

Marking a value sensitive is not encryption. Anyone who can read the state file can read every sensitive value in it.

Common mistakes

Believing sensitive values stay out of state. The sensitive argument only changes what the CLI prints. To keep a value out of state you need an ephemeral value or a write-only argument.

Expecting a check block to stop an apply. Check assertions only warn. Use a precondition or postcondition when a failure must block the run.

Committing tfvars files with secrets. A secrets.auto.tfvars file in version control leaks credentials to everyone with repository access. Use environment variables or Vault instead.

Check yourself

  • Where can a postcondition be used, and how does it refer to the object?

    In the lifecycle block of a resource or data source, using self to refer to that object's attributes.

  • Does sensitive = true keep a value out of the state file?

    No. It only redacts the value in CLI output; the value is still stored in plain text in state and plan files.

  • What happens when a check block assertion fails?

    Terraform reports a warning and continues the plan or apply, unlike a failed precondition or postcondition, which stops the run.

Topic 4 of 4 in Terraform Configuration

Test yourself on Terraform Configuration

The full HashiCorp Terraform Associate 004 question bank has 600 questions, with timed practice exams and an explanation for every answer.

This study guide is independent and is not affiliated with, endorsed by, or sponsored by HashiCorp. Vendor names, certification names and exam codes are trademarks of their respective owners and are used for identification only.