terraform/website/docs/configuration/expressions.html.md

736 lines
28 KiB
Markdown
Raw Normal View History

---
layout: "docs"
page_title: "Configuration Expressions"
sidebar_current: "docs-config-expressions"
description: |-
The Terraform language allows the use of expressions to access data exported
by resources and to transform and combine that data to produce other values.
---
# Expressions
_Expressions_ are used to refer to or compute values within a configuration.
The simplest expressions are just literal values, like `"hello"` or `5`,
but the Terraform language also allows more complex expressions such as
references to data exported by resources, arithmetic, conditional evaluation,
and a number of built-in functions.
Expressions can be used in a number of places in the Terraform language,
but some contexts place restrictions on which expression constructs are allowed,
such as requiring a literal value of a particular type, or forbidding
references to resource attributes. The other pages in this section describe
the contexts where expressions may be used and which expression features
are allowed in each case.
The following sections describe all of the features of the configuration
syntax.
## Types and Values
The result of an expression is a _value_. All values have a _type_, which
dictates where that value can be used and what transformations can be
applied to it.
A _literal expression_ is an expression that directly represents a particular
constant value.
Expressions are most commonly used to set the values of arguments to resources
and to child modules. In these cases, the argument itself has an expected
type and so the given expression must produce a value of that type. Where
possible, Terraform will automatically convert values from one type to another
in order to produce the expected type. If this isn't possible, Terraform will
produce a type mismatch error and you must update the configuration with
a more suitable expression.
This section describes all of the value types in the Terraform language, and
the literal expression syntax that can be used to create values of each
type.
### Primitive Types
A _primitive_ type is a simple type that isn't made from any other types.
The available primitive types in the Terraform language are:
* `string`: a sequence of Unicode characters representing some text, such
as `"hello"`.
* `number`: a numeric value. The `number` type can represent both whole
numbers like `15` and fractional values such as `6.283185`.
* `bool`: either `true` or `false`. `bool` values can be used in conditional
logic.
The Terraform language will automatically convert `number` and `bool` values
to `string` values when needed, and vice-versa as long as the string contains
a valid representation of a number of boolean value.
* `true` converts to `"true"`, and vice-versa
* `false` converts to `"false"`, and vice-versa
* `15` converts to `"15"`, and vice-versa
### Collection Types
A _collection_ type allows multiple values of another type to be grouped
together as a single value. The type of value _within_ a collection is called
its _element type_, and all collection types must have an element type.
For example, the type `list(string)` means "list of strings", which is a
different type than `list(number)`, a list of numbers. All elements of a
collection must always be of the same type.
The three _collection type kinds_ in the Terraform language are:
* `list(...)`: a sequence of values identified by consecutive whole numbers
starting with zero.
* `map(...)`: a collection of values where each is identified by a string label.
* `set(...)`: a collection of unique values that do not have any secondary
identifiers or ordering.
There is no direct syntax for creating collection type values, but the
Terraform language can automatically convert a structural type value (as
defined in the next section) to a similar collection type as long as all
of its elements can be converted to the required element type.
### Structural Types
A _structural_ type is another way to combine multiple values into a single
value, but structural types allow each value to be of a distinct type.
The two _structural type kinds_ in the Terraform language are:
* `object(...)`: has named attributes that each have their own type.
* `tuple(...)`: has a sequence of elements identified by consecutive whole
numbers starting with zero, where each element has its own type.
An object type value can be created using an object expression:
```hcl
{
name = "John"
age = 52
}
```
The type of the object value created by this expression is
`object({name=string,age=number})`. In most cases it is not important to know
the exact type of an object value, since the Terraform language automatically
checks and converts object types when needed.
Similarly, a tuple type value can be created using a tuple expression:
```hcl
["a", 15, true]
```
The type of the tuple value created by this expression is
`tuple([string, number, bool])`. Tuple values are rarely used directly in
the Terraform language, and are instead usually converted immediately to
list values by converting all of the elements to the same type.
Terraform will automatically convert object values to map values when required,
so usually object and map values can be used interchangably as long as their
contained values are of suitable types.
Likewise, Terraform will automatically convert tuple values to list values
when required, and so tuple and list values can be used interchangably in
most cases too.
Because of these automatic conversions, it is common to not make a strong
distinction between object and map or tuple and list in everyday discussion
of the Terraform language. The Terraform documentation usually discusses the
object and tuple types only in rare cases where it is important to distinguish
them from the map and list types.
## References to Named Objects
A number of different named objects can be accessed from Terraform expressions.
For example, resources are available in expressions as named objects that have
an object value corresponding to the schema of their resource type, accessed by
a dot-separated sequence of names like `aws_instance.example`.
The following named objects are available:
* `TYPE.NAME` is an object representing a
[managed resource](/docs/configuration/resources.html) of the given type
and name. If the resource has the `count` argument set, the value is
a list of objects representing its instances. Any named object that does
not match one of the other patterns listed below will be interpreted by
Terraform as a reference to a managed resource.
* `var.NAME` is the value of the
[input variable](/docs/configuration/variables.html) of the given name.
* `local.NAME` is the value of the
[local value](/docs/configuration/locals.html) of the given name.
* `module.MOD_NAME.OUTPUT_NAME` is the value of the
[output value](/docs/configuration/outputs.html) of the given name from the
[child module call](/docs/configuration/modules.html) of the given name.
* `data.SOURCE.NAME` is an object representing a
[data resource](/docs/configuration/data-sources.html) of the given data
source and name. If the resource has the `count` argument set, the value is
a list of objects representing its instances.
* `path.` is the prefix of a set of named objects that are filesystem
paths of various kinds:
* `path.module` is the filesystem path of the module where the expression
is placed.
* `path.root` is the filesystem path of the root module of the configuration.
* `path.cwd` is the filesystem path of the current working directory. In
normal use of Terraform this is the same as `path.root`, but some advanced
uses of Terraform run it from a directory other than the root module
directory, causing these paths to be different.
* `terraform.workspace` is the name of the currently selected
[workspace](/docs/state/workspaces.html).
Terraform analyses the block bodies of constructs such as resources and module
calls to automatically infer dependencies between objects from the use of
some of these reference types in expressions. For example, an object with an
argument expression that refers to a managed resource creates and implicit
dependency between that object and the resource.
The first name in each of these dot-separated sequence is called a
_variable_, but do not confuse this with the idea of an
[input variable](/docs/configuration/variables.html), which acts as a
customization parameter for a module. Input variables are often referred
to as just "variables" for brevity when the meaning is clear from context,
but due to this other meaning of "variable" in the context of expressions
this documentation page will always refer to input variables by their full
name.
Additional expression variables are available in specific contexts. These are
described in other documentation sections describing those specific features.
### Values Not Yet Known
When Terraform is planning a set of changes that will apply your configuration,
some resource attribute values cannot be populated immediately because their
values are decided dynamically by the remote system. For example, if a
particular remote object type is assigned a generated unique id on creation,
Terraform cannot predict the value of this id until the object has been created.
To allow expressions to still be evaluated during the plan phase, Terraform
uses special "unknown value" placeholders for these results. In most cases you
don't need to do anything special to deal with these, since the Terraform
language automatically handles unknown values during expressions, so that
for example adding a known value to an unknown value automatically produces
an unknown value as the result.
However, there are some situations where unknown values _do_ have a significant
effect:
* The `count` meta-argument for resources cannot be unknown, since it must
be evaluated during the plan phase to determine how many instances are to
be created.
* If unknown values are used in the configuration of a data resource, that
data resource cannot be read during the plan phase and so it will be deferred
until the apply phase. In this case, the results of the data resource will
_also_ be unknown values.
* If an unknown value is assigned to an argument inside a `module` block,
any references to the corresponding input variable within the child module
will use that unknown value.
* If an unknown value is used in the `value` argument of an output value,
any references to that output value in the parent module will use that
unknown value.
* Terraform will attempt to validate that unknown values are of suitable
types where possible, but incorrect use of such values may not be detected
until the apply phase, causing the apply to fail.
Unknown values appear in the `terraform plan` output as `(not yet known)`.
## Arithmetic and Logical Operators
An _operator_ is a type of expression that transforms or combines one or more
other expressions. Operators either combine two values in some way to
produce a third result value, or simply transform a single given value to
produce a single result.
Operators that work on two values place an operator symbol between the two
values, similar to mathematical notation: `1 + 2`. Operators that work on
only one value place an operator symbol before that value, like
`!true`.
The Terraform language has a set of operators for both arithmetic and logic,
which are similar to operators in programming languages such as JavaScript
or Ruby.
When multiple operators are used together in an expression, they are evaluated
according to a default order of operations:
| Level | Operators |
| ----- | -------------------- |
| 6 | `*`, `/`, `%` |
| 5 | `+`, `-` |
| 4 | `>`, `>=`, `<`, `<=` |
| 3 | `==`, `!=` |
| 2 | `&&` |
| 1 | `||` |
Parentheses can be used to override the default order of operations. Without
parentheses, higher levels are evaluated first, so `1 + 2 * 3` is interpreted
as `1 + (2 * 3)` and _not_ as `(1 + 2) * 3`.
The different operators can be gathered into a few different groups with
similar behavior, as described below. Each group of operators expects its
given values to be of a particular type. Terraform will attempt to convert
values to the required type automatically, or will produce an error message
if this automatic conversion is not possible.
### Arithmetic Operators
The arithmetic operators all expect number values and produce number values
as results:
* `a + b` returns the result of adding `a` and `b` together.
* `a - b` returns the result of subtracting `b` from `a`.
* `a * b` returns the result of multiplying `b` and `b`.
* `a / b` returns the result of dividing `a` by `b`.
* `a % b` returns the remainder of dividing `a` by `b`. This operator is
generally useful only when used with whole numbers.
* `-a` returns the result of multiplying `a` by `-1`.
### Equality Operators
The equality operators both take two values of any type and produce boolean
values as results.
* `a == b` returns `true` if `a` and `b` both have the same type and the same
value, or `false` otherwise.
* `a != b` is the opposite of `a == b`.
### Comparison Operators
The comparison operators all expect number values and produce boolean values
as results.
* `a < b` returns `true` if `a` is less than `b`, or `false` otherwise.
* `a <= b` returns `true` if `a` is less than or equal to `b`, or `false`
otherwise.
* `a > b` returns `true` if `a` is greater than `b`, or `false` otherwise.
* `a >= b` returns `true` if `a` is greater than or equal to `b`, or `false otherwise.
### Logical Operators
The logical operators all expect bool values and produce bool values as results.
* `a || b` returns `true` if either `a` or `b` is `true`, or `false` if both are `false`.
* `a && b` returns `true` if both `a` and `b` are `true`, or `false` if either one is `false`.
* `!a` returns `true` if `a` is `false`, and `false` if `a` is `true`.
## Conditional Expressions
A _conditional expression_ allows the selection of one of two values based
on whether another bool expression is `true` or `false`.
The syntax of a conditional expression is as follows:
```hcl
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 != "" ? var.a : "default-a"
```
If `var.a` is an empty string then the result is `"default-a"`, but otherwise
it is the actual value of `var.a`.
Any of the equality, comparison, and logical operators can be used to define
the condition. 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.
## Function Calls
The Terraform language has a number of
[built-in functions](/docs/configuration/functions.html) that can be used
within expressions as another way to transform and combine values. These
are similar to the operators but all follow a common syntax:
```hcl
function_name(argument1, argument2)
```
The `function_name` specifies which function to call. Each defined function has
a _signature_, which defines how many arguments it expects and what value types
those arguments must have. The signature also defines the type of the result
value for any given set of argument types.
Some functions take an arbitrary number of arguments. For example, the `min`
function takes any amount of number arguments and returns the one that is
numerically smallest:
```hcl
min(55, 3453, 2)
```
If the arguments to pass are available in a list or tuple value, that value
can be _expanded_ into separate arguments using the `...` symbol after that
argument:
```hcl
min([55, 2453, 2]...)
```
For a full list of available functions, see
[the function reference](/docs/configuration/functions.html).
## `for` Expressions
A _`for` expression_ allows you create a structural type value by transforming
another structural or collection type value. Each element in the input value
can correspond to either one or zero values in the result, and an arbitrary
expression can be used to transform each input element into an output element.
For example, if `var.list` is a list of strings then it can be converted to
a list of strings with all-uppercase letters with the following:
```hcl
[for s in var.list: upper(s)]
```
This `for` expression iterates over each element of `var.list`, and then
evaluates the expression `upper(s)` with `s` set to each respective element.
It then builds a new tuple value with all of the results of executing that
expression in the same order.
The type of brackets around the `for` expression decide what type of result
it produces. The above example uses `[` and `]`, which produces a tuple. If
`{` and `}` are used instead, the result is an object, and two result
expressions must be provided separated by the `=>` symbol:
```hcl
{for s in var.list: s => upper(s)}
```
This expression produces an object whose attributes are the original elements
from `var.list` and their corresponding values are the uppercase versions.
A `for` expression can also include an optional `if` clause to filter elements
from the source collection:
```
[for s in var.list: upper(s) if s != ""]
```
The source value can also be an object or map value, in which case two
temporary variable names can be provided to access the keys and values
respectively:
```
[for k, v in var.map: length(k) + length(v)]
```
Finally, if the result type is an object (using `{` and `}` delimiters) then
the value result expression can be followed by the `...` symbol to group
together results that have a common key:
```
{for s in var.list: substr(s, 0, 1) => s... if s != ""}
```
## Splat Expressions
A _splat expressions_ provides a more concise way to express a common
operation that could otherwise be performed with a `for` expression.
If `var.list` is a list of objects that all have an attribute `id`, then
a list of the ids could be obtained using the following `for` expression:
```
[for o in var.list: o.id]
```
This is equivalent to the following _splat expression_:
```
var.list[*].id
```
The special `[*]` symbol iterates over all of the elements of the list given
to its left and accesses from each one the attribute name given on its
right. A splat expression can also be used to access attributes and indexes
from lists of complex types by extending the sequence of operations to the
right of the symbol:
```
var.list[*].interfaces[0].name
```
The above expression is equivalent to the following `for` expression:
```
[for o in var.list: o.interfaces[0].name]
```
A second variant of the _splat expression_ is the "attribute-only" splat
expression, indicated by the sequence `.*`:
```
var.list.*.interfaces[0].name
```
This form has a subtly different behavior, equivalent to the following
`for` expression:
```
[for o in var.list: o.interfaces][0].name
```
Notice that with the attribute-only splat expression the index operation
`[0]` is applied to the result of the iteration, rather than as part of
the iteration itself.
The standard splat expression `[*]` should be used in most cases, because its
behavior is less surprising. The attribute-only splat expression is supported
only for compatibility with earlier versions of Terraform, and should not be
used in new configurations.
Splat expressions also have another useful effect: if they are applied to
a value that is _not_ a list or tuple then the value is automatically wrapped
in a single-element list before processing. That is, `var.single_object[*].id`
is equivalent to `[var.single_object][*].id`, or effectively
`[var.single_object.id]`. This behavior is not interesting in most cases,
but it is particularly useful when referring to resources that may or may
not have `count` set, and thus may or may not produce a tuple value:
```hcl
aws_instance.example[*].id
```
The above will produce a list of ids whether `aws_instance.example` has
`count` set or not, avoiding the need to revise various other expressions
in the configuration when a particular resource switches to and from
having `count` set.
## `dynamic` blocks
Expressions can usually be used only when assigning a value to an attribute
argument using the `name = expression` form. This covers many uses, but
some resource types include in their arguments _nested blocks_, which
do not accept expressions:
```hcl
resource "aws_security_group" "example" {
name = "example" # can use expressions here
ingress {
# but the "ingress" block is always a literal block
}
}
```
To allow nested blocks like `ingress` to be constructed dynamically, a special
block type `dynamic` is supported inside `resource`, `data`, `provider`,
and `provisioner` blocks:
```hcl
resource "aws_security_group" "example" {
name = "example" # can use expressions here
dynamic "ingress" {
for_each = var.service_ports
content {
from_port = ingress.value
to_port = ingress.value
protocol = "tcp"
}
}
}
```
A `dynamic` block iterates over a collection or structural value given in its
`for_each` argument, generating a nested block for each element by evaluating
the nested `content` block. When evaluating the block, a temporary variable
is defined that is by default named after the block type being generated,
or `ingress` in this example. An optional additional argument `iterator` can be
used to override the name of the iterator variable.
Since the `for_each` argument accepts any collection or structural value,
you can use a `for` expression or splat expression to transform an existing
collection.
Overuse of `dynamic` blocks can make configuration hard to read and maintain,
so we recommend using this only when a re-usable module is hiding some details.
Avoid creating modules that are just thin wrappers around single resources,
passing through all of the input variables directly to resource arguments.
Always write nested blocks out literally where possible.
A `dynamic` block can only generate arguments that belong to the resource type,
data source, provider or provisioner being configured. It is _not_ possible
to generate meta-argument blocks such as `lifecycle` and `provisioner`
blocks, since Terraform must process these before it is safe to evaluate
expressions.
## String Literals
The Terraform language has two different syntaxes for string literals. The
most common is to delimit the string with quote characters (`"`), like
`"hello"`. In quoted strings, the backslash character serves as an escape
sequence, with the following characters selecting the escape behavior:
| Sequence | Replacement |
| ------------ | ----------------------------------------------------------------------------- |
| `\n` | Newline |
| `\r` | Carriage Return |
| `\t` | Tab |
| `\"` | Literal quote (without terminating the string) |
| `\\` | Literal backslash |
| `\uNNNN` | Unicode character from the basic multilingual plane (NNNN is four hex digits) |
| `\UNNNNNNNN` | Unicode character from supplimentary planes (NNNNNNNN is eight hex digits) |
The alternative syntax for string literals is the so-called "heredoc" style,
inspired by Unix shell languages. This style allows multi-line strings to
be expressed more clearly by using a custom delimiter word on a line of its
own to close the string:
```hcl
<<EOT
hello
world
EOT
```
The `<<` marker followed by any identifier at the end of a line introduces the
sequence. Terraform then processes the following lines until it finds one that
consists entirely of the identifier given in the introducer. In the above
example, `EOT` is the identifier selected. Any identifier is allowed, but
conventionally this identifier is in all-uppercase and beings with `EO`, meaning
"end of". `EOT` in this case stands for "end of text".
The "heredoc" form shown above requires that the lines following be flush with
the left margin, which can be awkward when an expression is inside an indented
block:
```hcl
block {
value = <<EOT
hello
world
EOT
}
```
To improve on this, Terraform also accepts an _indented_ heredoc string variant
that is introduced by the `<<-` sequence:
```hcl
block {
value = <<-EOT
hello
world
EOT
}
```
In this case, Terraform analyses the lines in the sequence to find the one
with the smallest number of leading spaces, and then trims that many spaces
from the beginning of all of the lines, leading to the following result:
```
hello
world
```
Backslash sequences are not interpreted in a heredoc string expression.
Instead, the backslash character is interpreted literally.
In both quoted and heredoc string expressions, Terraform supports template
sequences introduced by `${` and `%{`. These are described in more detail
in the following section. To include these sequences _literally_ without
beginning a template sequence, double the leading character: `$${` or `%%{`.
## String Templates
Within quoted and heredoc string expressions, the sequences `${` and `%{`
begin _template sequences_. Templates allow expressions to be embedded directly
into the string sequence, and thus allow strings to be dynamically constructed
from other values in a concise way.
A `${ ... }` sequence is an _interpolation_, which evaluates the expression
given between the markers, converts the result to a string if necessary, and
then inserts it into the final string:
```hcl
"Hello, ${var.name}!"
```
In the above example, the named object `var.name` is accessed and its value
inserted into the string, producing a result like "Hello, Juan!".
A `%{ ... }` sequence is a _directive_, which allows for conditional
results and iteration over collections, similar to conditional and
and `for` expressions.
The following directives are supported:
* The `if` directive chooses between two templates based on a conditional
expression:
```hcl
"Hello, %{ if var.name != "" }${var.name}%{ else }unnamed%{ endif }!"
```
The "else" portion may be omitted, in which case the result is an empty
string if the condition expression returns `false`.
* The `for` directive iterates over each of the elements of a given collection
or structural value and evaluates a given template once for each element,
concatenating the results together:
```hcl
<<EOT
%{ for ip in aws_instance.example.*.private_ip }
server ${ip}
%{ endfor }
EOT
```
The name given immediately after the `for` keyword is used as a temporary
variable name which can then be referenced from the nested template.
To allow for template directives to be formatted for readability without
introducing unwanted additional spaces and newlines in the result, all
template sequences can include optional _strip markers_ `~` either immediately
after the introducer or immediately before the end. When present, the sequence
consumes all of the literal whitespace (spaces and newlines) either before
or after the sequence:
```hcl
<<EOT
%{ for ip in aws_instance.example.*.private_ip ~}
server ${ip}
%{ endfor ~}
EOT
```
In the above example, the newline after each of the directives is not included
in the output, but the newline after the `server ${ip}` sequence is retained,
causing only one line to be generated for each element:
```
server 10.1.16.154
server 10.1.16.1
server 10.1.16.34
```
When using template directives, we recommend always using the "heredoc" string
expression form and then formatting the template over multiple lines for
readability. Quoted string literals should usually include only interpolation
sequences.