Skip to content

Variables

Declaring variables, overriding them at run time, and how empty values behave.

On this page

Variables let one profile serve several environments without forking it. This page covers declaring them, the rules for overriding them, and what happens when a value is empty. The function set available in expressions is on the Functions page.

Declaring a variable

variable "ntp_servers" {
  description = "Approved NTP servers"
  type        = "list(string)"
  default     = ["time1.example.org", "time2.example.org"]

  validation {
    condition     = length(var.ntp_servers) > 0
    error_message = "At least one NTP server must be configured."
  }
}

The supported types are string, which is the default, number, bool, and list(string). Anything else is a load-time error.

validation conditions are evaluated against the finished variable context, so a condition may reference any variable, not only its own. The first failing rule stops the load.

Resolution order

Per variable: an override if one exists, otherwise the declared default, otherwise "" for an untyped or string variable. A non-string variable with neither a default nor an override is a load-time error.

Overrides come from two places.

refpoint run baseline --var-file production.rpvar --var max_password_age=60

--var-file keeps native HCL types, so numbers, bools, and lists arrive as themselves, and a null value is rejected. --var is always a string, and for a list target a comma-separated string is split and trimmed. --var wins over --var-file.

Overrides for variables the profile never declared pass through with their native type. Dependency profiles rely on this.

Coercion is not type checking

Conversion is coercion. A false assigned to a type = "string" variable becomes the string "false". The type guarantee is real only for number, bool, and list(string) targets. Use validation blocks to reject coercion artifacts rather than assuming the declared type rejected them for you.

Empty variables skip a check

If a check attribute draws on a profile variable and evaluates to nothing, an empty or whitespace-only string, or an empty collection, the check is skipped and the attribute is named in the reason.

The reason is that an empty pattern matches anything and an empty expected hash matches nothing. Both silently misreport.

A literal "" written in the profile is left alone, because the author said it explicitly. An interpolation that still yields text, such as "prefix-${var.suffix}", runs normally.

Using a variable in a check

variable "sshd_config_path" {
  type    = "string"
  default = "/etc/ssh/sshd_config"
}

control "ssh-02" {
  title  = "SSH protocol settings are managed"
  impact = 0.7

  check "config-owner" "file" {
    path  = var.sshd_config_path
    owner = "root"
    mode  = "0600"
  }
}

Only the address attribute, path here, is evaluated with the variable context at load time. The remaining assertions are decoded by the file provider when the check runs.