Archive: Terraform 0.11 Boolean Evaluation

Unexpected implementation semantics

Kevin Gillette
2 min readMay 30, 2018

Update: this does not apply to Terraform 0.12+, which added real bool and number types among other benefits. Please update to 0.12 (or later) if you are able to avoid pre-0.12 idiosyncrasies.

Terraform has Boolean values… but it does not have a true Boolean type. Instead, Terraform transforms the true and false literal values into "1" and "0" respectively, thus in reality, Terraform’s Boolean values are string typed. Additionally, when interpreting strings as bools, such as in a conditional expression, the additional values "true" and "false" are accepted (case-insensitively).

Another subtlety comes from the behavior of Terraform’s == (equality) operator, which operates on string values. For example, the following values are all equivalent:

true == true   # "1" == "1"
false == false # "0" == "0"
"1" == true # "1" == "1"
"0" == false # "0" == "0"

However, these pairs of values, perhaps unintuitively, are not equivalent:

true != "true"   # "1" != "true"
false != "false" # "0" != "false"

To make matters more complicated, the result of evaluating Boolean operators is not consistent with the aforementioned transformation of Boolean literals. For example:

"x" == "x" # -> "true"
"x" == "y" # -> "false"
!!true # -> "true"

Remembering that true is transformed into "1", the last example is bizarrely evaluated in the following steps:

!!true
!!"1"
!"false"
"true"

This is an example of how awkward Terraform code can be to work with, and this means that we must be careful, in particular, when using Boolean values and expressions.

My recommendation:

  1. Never use the == or != operators to compare Boolean values, since these perform string comparisons, and cannot handle the multiple possible synonyms of true and false. For example, instead of:
    var.x == true ? var.y : var.z
    simply use:
    var.x ? var.y : var.z
  2. Normalize your modules’ Boolean outputs with double negation:
    output "out" { value = "${!!var.in}" }
    This will result in module output values that are consistently either "true" or "false"

--

--