Skip to content

10 — Validation and sensitive values

Folder: none yet — paste this into a scratch root.

Two ways of constraining a variable: one rejects bad input, the other hides the value. Neither does what the other does, and the second one does less than its name suggests.

Validation — reject bad input early

variable "name_length" {
  description = "The length of the random pet name"
  type        = number
  default     = 1

  validation {
    condition     = var.name_length >= 1 && var.name_length <= 5
    error_message = "name_length must be between 1 and 5."
  }
}
terraform plan -var="name_length=10"

Plan fails immediately with your custom message, before Terraform makes a single provider call. That's the value: the failure is instant and legible, rather than a cryptic API error thirty seconds into an apply.

Use it for constraints a teammate wouldn't know about — naming conventions, allowed regions, ranges the downstream API silently truncates.

Sensitive — masking, not encryption

variable "api_token" {
  description = "A fake secret, for practicing sensitive handling."
  type        = string
  default     = "super-secret-value-123"
  sensitive   = true
}

resource "local_file" "config" {
  filename = "/tmp/app-config.txt"
  content  = "token=${var.api_token}"
}
terraform plan

Terraform masks the value in plan output and marks the resource attribute as sensitive because a sensitive value flowed into it. Then:

terraform apply
cat /tmp/app-config.txt        # token=super-secret-value-123, in plaintext
grep -o 'super-secret[^"]*' terraform.tfstate

The secret is sitting there in plain text in both the artifact and the state file.

sensitive = true controls one thing: whether the value is printed in CLI and plan output. It does not encrypt the value, does not keep it out of state, and does not stop it landing in whatever the resource writes.

What follows from that

  • State is a secret. If any sensitive value has ever passed through your config, the state file must be treated with the same care as the secret itself. That's the real argument for a remote backend with restricted IAM and encryption at rest, covered in Theory §14 and set up in Lab 2.
  • sensitive is for shoulder-surfing and CI logs, which is genuinely useful — a plan posted to a PR comment shouldn't leak a token. It is not a security boundary.
  • Real secrets belong in Secret Manager or Vault, referenced by a data block at apply time, so the value is never in the config. The sandbox does this in infrastructure/infra/secrets.tf.

Key takeaway

validation moves a failure from thirty seconds into an apply to instantly at plan — a real guarantee. sensitive keeps a value out of your terminal and nowhere else — a courtesy. Treating the second as though it were the first is exactly how secrets end up committed in a state file.


Theory: §7 Variables, §14 State file security · Next: Provider versions