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:
Martin Atkins 2018-05-06 10:36:32 -07:00
parent e1754970a8
commit 39579e8d0f
2 changed files with 877 additions and 99 deletions

View File

@ -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.

View File

@ -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"
}
image_id = "abc123"
```
/* A multi
line comment. */
resource "aws_instance" "web" {
ami = "${var.ami}"
count = 2
source_dest_check = false
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.
connection {
user = "root"
A _block_ is a container for other content:
```hcl
resource "aws_instance" "example" {
ami = "abc123"
network_interface {
# ...
}
}
```
Basic bullet point reference:
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.
* Single line comments start with `#`
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.
* Multi-line comments are wrapped with `/*` and `*/`
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.
* 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.
### Style Conventions
* Strings are in double-quotes.
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.
* 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).
* Indent two spaces for each nesting level.
* 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.
* When multiple attributes with single-line values appear on consecutive lines
at the same nesting level, align their equals signs:
* Numbers are assumed to be base 10. If you prefix a number with
`0x`, it is treated as a hexadecimal number.
```hcl
ami = "abc123"
instance_type = "t2.micro"
```
* Boolean values: `true`, `false`.
* 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.
* Lists of primitive types can be made with square brackets (`[]`).
Example: `["foo", "bar", "baz"]`.
* Use empty lines to separate logical groups of attributes within a block.
* 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.
* 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.
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:
```hcl
resource "aws_instance" "example" {
count = 2 # meta-argument attribute first
```hcl
variable "ami" {
description = "the AMI to use"
}
```
ami = "abc123"
instance_type = "t2.micro"
is equal to:
```hcl
variable = [{
"ami": {
"description": "the AMI to use",
}
}]
```
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.
## JSON Syntax
Terraform also supports reading JSON formatted configuration files.
The above example converted to JSON:
```json
{
"variable": {
"ami": {
"description": "the AMI to use"
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.