Terraform
Conditional Expressions
A conditional expression uses the value of a boolean expression to select one of two values.
Hands-on: Try the Create Dynamic Expressions tutorial.
Syntax
The syntax of a conditional expression is as follows:
condition ? true_val : false_val
If condition
is true
then the result is true_val
. If condition
is
false
then the result is false_val
.
A common use of conditional expressions is to define defaults to replace invalid values:
var.a == "" ? "default-a" : var.a
If var.a
is an empty string then the result is "default-a"
, but otherwise
it is the actual value of var.a
.
Conditions
The condition can be any expression that resolves to a boolean value. This will usually be an expression that uses the equality, comparison, or logical operators.
Custom Condition Checks
You can create conditions that produce custom error messages for several types of objects in a configuration. For example, you can add a condition to an input variable that checks whether incoming image IDs are formatted properly.
Custom conditions can help capture assumptions, helping future maintainers understand the configuration design and intent. They also return useful information about errors earlier and in context, helping consumers more easily diagnose issues in their configurations.
Refer to Validate your configuration for details.
Result Types
The two result values may be of any type, but they must both be of the same type so that Terraform can determine what type the whole conditional expression will return without knowing the condition value.
If the two result expressions don't produce the same type then Terraform will attempt to find a type that they can both convert to, and make those conversions automatically if so.
For example, the following expression is valid and will always return a string, because in Terraform all numbers can convert automatically to a string using decimal digits:
var.example ? 12 : "hello"
Relying on this automatic conversion behavior can be confusing for those who are not familiar with Terraform's conversion rules though, so we recommend being explicit using type conversion functions in any situation where there may be some uncertainty about the expected result type.
The following example is contrived because it would be easier to write the
constant "12"
instead of the type conversion in this case, but shows how to
use tostring
to explicitly convert a number to
a string.
var.example ? tostring(12) : "hello"
Examples
You can use any of Terraform's built-in functions or language operators in a condition as long as the expression is valid and returns a boolean result. The following examples demonstrate language features that can help you write condition expressions.
Logical operator examples
Use the logical operators &&
(AND), ||
(OR), and !
(NOT) to combine multiple conditions together.
condition = var.name != "" && lower(var.name) == var.name
You can also use arithmetic operators, for example a + b
, equality operators, for example a == b
, and comparison operators, for example a < b
. Refer to Arithmetic and Logical Operators for details.
contains
Function
Use the contains
function to test whether a given value is one of a set of predefined valid values.
condition = contains(["STAGE", "PROD"], var.environment)
length
Function
Use the length
function to test a collection's length and require a non-empty list or map.
condition = length(var.items) != 0
Using the length
function is more suitable than using ==
or !=
when both operands potentially have exactly the same type, which is often ambiguous for empty collections.
for
Expressions
Use for
expressions in conjunction with the functions alltrue
and anytrue
to test whether a condition holds for all or for any elements of a collection.
condition = alltrue([
for v in var.instances : contains(["t2.micro", "m3.medium"], v.type)
])
can
Function
Use the can
function to concisely use the validity of an expression as a condition. It returns true
if its given expression evaluates successfully and false
if it returns any error, so you can use various other functions that typically return errors as a part of your condition expressions.
For example, you can use can
with regex
to test if a string matches a particular pattern because regex
returns an error when given a non-matching string.
condition = can(regex("^[a-z]+$", var.name))
You can also use can
with the type conversion functions to test whether a value is convertible to a type or type constraint.
# This remote output value must have a value that can
# be used as a string, which includes strings themselves
# but also allows numbers and boolean values.
condition = can(tostring(data.terraform_remote_state.example.outputs["name"]))
# This remote output value must be convertible to a list
# type of with element type.
condition = can(tolist(data.terraform_remote_state.example.outputs["items"]))
You can also use can
with attribute access or index operators to test whether a collection or structural value has a particular element or index.
# var.example must have an attribute named "foo"
condition = can(var.example.foo)
# var.example must be a sequence with at least one element
condition = can(var.example[0])
# (although it would typically be clearer to write this as a
# test like length(var.example) > 0 to better represent the
# intent of the condition.)
self
Object
Use the self
object in postcondition blocks to refer to attributes of the instance under evaluation.
resource "aws_instance" "example" {
instance_type = "t2.micro"
ami = "ami-abc123"
lifecycle {
postcondition {
condition = self.instance_state == "running"
error_message = "EC2 instance must be running."
}
}
}
each
and count
Objects
In blocks where for_each
or count
are set, use each
and count
objects to refer to other resources that are expanded in a chain.
variable "vpc_cidrs" {
type = set(string)
}
data "aws_vpc" "example" {
for_each = var.vpc_cidrs
filter {
name = "cidr"
values = [each.key]
}
}
resource "aws_internet_gateway" "example" {
for_each = data.aws_vpc.example
vpc_id = each.value.id
lifecycle {
precondition {
condition = data.aws_vpc.example[each.key].state == "available"
error_message = "VPC ${each.key} must be available."
}
}
}