website: revisions to the "Configuration Syntax" page and expressions
This rewrite of the "Configuration Syntax" page now gives some more detail on the top-level structural constructs and de-emphasizes the name "HCL" as subordinate to "the Terraform language". It also now includes some commentary on valid identifiers and comments, and issues around character encodings and line endings. In addition, we now have a new "Expressions" page that replaces the old "Interpolation Syntax" page, covering the expression language features we inherit from HCL and how they behave in the context of Terraform. The "Expressions" page currently links to a page about the built-in functions which does not yet exist. This will be created in a later commit.
This commit is contained in:
parent
e1754970a8
commit
39579e8d0f
|
@ -0,0 +1,735 @@
|
|||
---
|
||||
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.
|
|
@ -3,135 +3,178 @@ layout: "docs"
|
|||
page_title: "Configuration Syntax"
|
||||
sidebar_current: "docs-config-syntax"
|
||||
description: |-
|
||||
The syntax of Terraform configurations is custom. It is meant to strike a
|
||||
balance between human readable and editable as well as being machine-friendly.
|
||||
For machine-friendliness, Terraform can also read JSON configurations. For
|
||||
general Terraform configurations, however, we recommend using the Terraform
|
||||
syntax.
|
||||
The Terraform language has its own syntax, intended to combine declarative
|
||||
structure with expressions in a way that is easy for humans to read and
|
||||
understand.
|
||||
---
|
||||
|
||||
# Configuration Syntax
|
||||
|
||||
The syntax of Terraform configurations is called [HashiCorp Configuration
|
||||
Language (HCL)](https://github.com/hashicorp/hcl). It is meant to strike a
|
||||
balance between human readable and editable as well as being machine-friendly.
|
||||
For machine-friendliness, Terraform can also read JSON configurations. For
|
||||
general Terraform configurations, however, we recommend using the HCL Terraform
|
||||
syntax.
|
||||
Other pages in this section have described various configuration constructs
|
||||
that can appear in the Terraform language. This page describes the lower-level
|
||||
syntax of the language in more detail, revealing the building blocks that
|
||||
those constructs are built from.
|
||||
|
||||
## Terraform Syntax
|
||||
This page describes the _native syntax_ of the Terraform language, which is
|
||||
a rich language designed to be easy for humans to read and write. The
|
||||
constructs in the Terraform language can also be expressed in
|
||||
[JSON syntax](/docs/configuration/syntax-json.html), which is harder for humans
|
||||
to read and edit but easier to generate and parse programmatically.
|
||||
|
||||
Here is an example of Terraform's HCL syntax:
|
||||
This low-level syntax of the Terraform language is defined in terms of a
|
||||
syntax called _HCL_, which is also used by configuration languages in
|
||||
other applications, and in particular other HashiCorp products.
|
||||
It is not necessary to know all of the details of HCL syntax in
|
||||
order to use Terraform, and so this page summarizes the most important
|
||||
details. If you are interested, you can find a full definition of HCL
|
||||
syntax in
|
||||
[the HCL native syntax specification](https://github.com/hashicorp/hcl2/blob/master/hcl/hclsyntax/spec.md).
|
||||
|
||||
## Attributes and Blocks
|
||||
|
||||
The Terraform language syntax is built around two key syntax constructs:
|
||||
attributes and blocks.
|
||||
|
||||
An _attribute_ assigns a value to a particular name:
|
||||
|
||||
```hcl
|
||||
# An AMI
|
||||
variable "ami" {
|
||||
description = "the AMI to use"
|
||||
}
|
||||
|
||||
/* A multi
|
||||
line comment. */
|
||||
resource "aws_instance" "web" {
|
||||
ami = "${var.ami}"
|
||||
count = 2
|
||||
source_dest_check = false
|
||||
|
||||
connection {
|
||||
user = "root"
|
||||
}
|
||||
}
|
||||
image_id = "abc123"
|
||||
```
|
||||
|
||||
Basic bullet point reference:
|
||||
The identifier before the equals sign is the _attribute name_, and after
|
||||
the equals sign is the attribute's value. The semantics applied to each
|
||||
attribute name define what value types are valid, but many attributes
|
||||
accept arbitrary [expressions](/docs/configuration/expressions.html),
|
||||
which allow the value to either be specified literally or generated from
|
||||
other values programmatically.
|
||||
|
||||
* Single line comments start with `#`
|
||||
|
||||
* Multi-line comments are wrapped with `/*` and `*/`
|
||||
|
||||
* Values are assigned with the syntax of `key = value` (whitespace
|
||||
doesn't matter). The value can be any primitive (string,
|
||||
number, boolean), a list, or a map.
|
||||
|
||||
* Strings are in double-quotes.
|
||||
|
||||
* Strings can interpolate other values using syntax wrapped
|
||||
in `${}`, such as `${var.foo}`. The full syntax for interpolation
|
||||
is [documented here](/docs/configuration/interpolation.html).
|
||||
|
||||
* Multiline strings can use shell-style "here doc" syntax, with
|
||||
the string starting with a marker like `<<EOF` and then the
|
||||
string ending with `EOF` on a line of its own. The lines of
|
||||
the string and the end marker must *not* be indented.
|
||||
|
||||
* Numbers are assumed to be base 10. If you prefix a number with
|
||||
`0x`, it is treated as a hexadecimal number.
|
||||
|
||||
* Boolean values: `true`, `false`.
|
||||
|
||||
* Lists of primitive types can be made with square brackets (`[]`).
|
||||
Example: `["foo", "bar", "baz"]`.
|
||||
|
||||
* Maps can be made with braces (`{}`) and colons (`:`):
|
||||
`{ "foo": "bar", "bar": "baz" }`. Quotes may be omitted on keys, unless the
|
||||
key starts with a number, in which case quotes are required. Commas are
|
||||
required between key/value pairs for single line maps. A newline between
|
||||
key/value pairs is sufficient in multi-line maps.
|
||||
|
||||
In addition to the basics, the syntax supports hierarchies of sections,
|
||||
such as the "resource" and "variable" in the example above. These
|
||||
sections are similar to maps, but visually look better. For example,
|
||||
these are nearly equivalent:
|
||||
A _block_ is a container for other content:
|
||||
|
||||
```hcl
|
||||
variable "ami" {
|
||||
description = "the AMI to use"
|
||||
resource "aws_instance" "example" {
|
||||
ami = "abc123"
|
||||
|
||||
network_interface {
|
||||
# ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
is equal to:
|
||||
A block has a _type_ ("resource" in this example). Each block type defines
|
||||
how many _labels_ must follow the type keyword. The "resource" block type
|
||||
shown here expects two labels, which are "aws_instance" and "example"
|
||||
in this case. A particular block type may have any number of required labels,
|
||||
or it may require none as with the nested "network_interface" block type.
|
||||
|
||||
After the block type keyword and any labels, the block _body_ is delimited
|
||||
by the `{` and `}` characters. Within the block body, further attributes
|
||||
and blocks may be nested, creating a heirarchy of blocks and their associated
|
||||
attributes.
|
||||
|
||||
Unfortunately, the low-level syntax described here uses the noun "attribute"
|
||||
to mean something slightly different to how it is used by the main
|
||||
Terraform language. Elsewhere in this documentation, "attribute" usually
|
||||
refers to a named value exported by an object that can be accessed in an
|
||||
expression, such as the "id" portion of the expression
|
||||
`aws_instance.example.id`. To reduce confusion, other documentation uses the
|
||||
term "argument" to refer to the syntax-level idea of an attribute.
|
||||
|
||||
### Style Conventions
|
||||
|
||||
The Terraform parser allows you some flexibility in how you lay out the
|
||||
elements in your configuration files, but the Terraform language also has some
|
||||
idiomatic style conventions which we recommend users should always follow
|
||||
for consistency between files and modules written by different teams.
|
||||
Automatic source code formatting tools may apply these conventions
|
||||
automatically.
|
||||
|
||||
* Indent two spaces for each nesting level.
|
||||
|
||||
* When multiple attributes with single-line values appear on consecutive lines
|
||||
at the same nesting level, align their equals signs:
|
||||
|
||||
```hcl
|
||||
variable = [{
|
||||
"ami": {
|
||||
"description": "the AMI to use",
|
||||
}
|
||||
}]
|
||||
ami = "abc123"
|
||||
instance_type = "t2.micro"
|
||||
```
|
||||
|
||||
Notice how the top stanza visually looks a lot better? By repeating
|
||||
multiple `variable` sections, it builds up the `variable` list. When
|
||||
possible, use sections since they're visually clearer and more readable.
|
||||
* When both attributes and blocks appear together inside a block body,
|
||||
place all of the attributes together at the top and then place nested
|
||||
blocks below them. Use one blank line to separate the attributes from
|
||||
the blocks.
|
||||
|
||||
## JSON Syntax
|
||||
* Use empty lines to separate logical groups of attributes within a block.
|
||||
|
||||
Terraform also supports reading JSON formatted configuration files.
|
||||
The above example converted to JSON:
|
||||
* For blocks that contain both arguments and "meta-arguments" (as defined by
|
||||
the Terraform language semantics), list meta-argument attributes first
|
||||
and separate them from other attributes with one blank line. Place
|
||||
meta-argument blocks _last_ and separate them from other blocks with
|
||||
one blank line.
|
||||
|
||||
```json
|
||||
{
|
||||
"variable": {
|
||||
"ami": {
|
||||
"description": "the AMI to use"
|
||||
```hcl
|
||||
resource "aws_instance" "example" {
|
||||
count = 2 # meta-argument attribute first
|
||||
|
||||
ami = "abc123"
|
||||
instance_type = "t2.micro"
|
||||
|
||||
network_interface {
|
||||
# ...
|
||||
}
|
||||
},
|
||||
|
||||
"resource": {
|
||||
"aws_instance": {
|
||||
"web": {
|
||||
"ami": "${var.ami}",
|
||||
"count": 2,
|
||||
"source_dest_check": false,
|
||||
|
||||
"connection": {
|
||||
"user": "root"
|
||||
}
|
||||
}
|
||||
}
|
||||
lifecycle { # meta-argument block last
|
||||
create_before_destroy = true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The conversion should be pretty straightforward and self-documented.
|
||||
* Top-level blocks should always be separated from one another by one
|
||||
blank line. Nested blocks should also be separated by blank lines, except
|
||||
when grouping together related blocks of the same type.
|
||||
|
||||
The downsides of JSON are less human readability and the lack of
|
||||
comments. Otherwise, the two are completely interoperable.
|
||||
* Avoid separating multiple blocks of the same type with other blocks of
|
||||
a different type, unless the block types are defined by semantics to
|
||||
form a family.
|
||||
(For example: `root_block_device`, `ebs_block_device` and
|
||||
`ephemeral_block_device` on `aws_instance` form a family of block types
|
||||
describing AWS block devices, and can therefore be grouped together and
|
||||
mixed.)
|
||||
|
||||
## Identifiers
|
||||
|
||||
Attribute names, block type names, and the names of most Terraform-specific
|
||||
constructs like resources, input variables, etc. are all _identifiers_.
|
||||
The Terraform language implements
|
||||
[the Unicode identifier syntax](http://unicode.org/reports/tr31/), extended
|
||||
to also include the ASCII hyphen character `-`.
|
||||
|
||||
In practice, this means that identifiers can contain letters, digits,
|
||||
underscores, and hyphens. To avoid ambiguity with literal numbers, the
|
||||
first character of an identifier must not be a digit.
|
||||
|
||||
## Comments
|
||||
|
||||
The Terraform language supports three different syntaxes for comments:
|
||||
|
||||
* `#` begins a single-line comment, ending at the end of the line
|
||||
|
||||
* `//` also begins a single-line comment, as an alternative to `#`.
|
||||
|
||||
* `/*` and `*/` are start and end delimiters for a comment that might span
|
||||
over multiple lines.
|
||||
|
||||
The `#` single-line comment style is the default comment style and should be
|
||||
used in most cases. Automatic configuration formatting tools may automatically
|
||||
transform `//` comments into `#` comments, since the double-slash style is
|
||||
not idiomatic.
|
||||
|
||||
## Character Encoding and Line Endings
|
||||
|
||||
Terraform configuration files must always be UTF-8 encoded. While the
|
||||
delimiters of the language are all ASCII characters, Terraform accepts
|
||||
non-ASCII characters in identifiers, comments, and string values.
|
||||
|
||||
Terraform accepts configuration files with either Unix-style line endings
|
||||
(LF only) or Windows-style line endings (CR then LF), but the idiomatic style
|
||||
is to use the Unix convention, and so automatic configuration formatting tools
|
||||
may automatically transform CRLF endings to LF.
|
||||
|
|
Loading…
Reference in New Issue