Expressions
Warning
This content is part of the legacy version of Waypoint that is no longer actively maintained. For additional information on the new vision of Waypoint, check out this blog post and the HCP Waypoint documentation.
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 Waypoint configuration also allows more complex expressions such as
references to attributes exported by previous stages, arithmetic, conditional evaluation,
and a number of built-in functions.
The rest of this page describes all of the expressions in Waypoint's configuration language. This page is very long so we recommend using the "Jump to Section" dropdown beneath the header above to more easily navigate.
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. Waypoint configuration uses the following types for its values:
string
: a sequence of Unicode characters representing some text, like"hello"
.number
: a numeric value. Thenumber
type can represent both whole numbers like15
and fractional values like6.283185
.bool
: eithertrue
orfalse
.bool
values can be used in conditional logic.list
(ortuple
): a sequence of values, like["us-west-1a", "us-west-1c"]
. Elements in a list or tuple are identified by consecutive whole numbers, starting with zero.map
(orobject
): a group of values identified by named labels, like{name = "Mabel", age = 52}
.
Strings, numbers, and bools are sometimes called primitive types. Lists/tuples and maps/objects are sometimes called complex types, structural types, or collection types.
Finally, there is one special value that has no type:
null
: a value that represents absence or omission. If you set a parameter value tonull
, Waypoint behaves as though you had completely omitted it — it will use the argument's default value if it has one, or raise an error if the argument is mandatory.null
is most useful in conditional expressions, so you can dynamically omit an argument if a condition isn't met.
Type Conversion
Where possible, Waypoint automatically converts values from one type to another in order to produce the expected type. If this isn't possible, Waypoint will produce a type mismatch error and you must update the configuration with a more suitable expression.
Waypoint automatically converts number and bool values to strings when needed. It also converts strings to numbers or bools, as long as the string contains a valid representation of a number or bool value.
true
converts to"true"
, and vice-versafalse
converts to"false"
, and vice-versa15
converts to"15"
, and vice-versa
Literal Expressions
A literal expression is an expression that directly represents a particular constant value. Waypoint has a literal expression syntax for each of the value types described above:
Strings are usually represented by a double-quoted sequence of Unicode characters,
"like this"
. There is also a "heredoc" syntax for more complex strings. String literals are the most complex kind of literal expression in Waypoint, and have additional documentation on this page:- See String Literals below for information about escape sequences and the heredoc syntax.
- See String Templates below for information about interpolation and template directives.
Numbers are represented by unquoted sequences of digits with or without a decimal point, like
15
or6.283185
.Bools are represented by the unquoted symbols
true
andfalse
.The null value is represented by the unquoted symbol
null
.Lists/tuples are represented by a pair of square brackets containing a comma-separated sequence of values, like
["a", 15, true]
.List literals can be split into multiple lines for readability, but always require a comma between values. A comma after the final value is allowed, but not required. Values in a list can be arbitrary expressions.
Maps/objects are represented by a pair of curly braces containing a series of
<KEY> = <VALUE>
pairs:Key/value pairs can be separated by either a comma or a line break. Values can be arbitrary expressions. Keys are strings; they can be left unquoted if they are a valid identifier, but must be quoted otherwise. You can use a non-literal expression as a key by wrapping it in parentheses, like
(var.business_unit_tag_name) = "SRE"
.
Indices and Attributes
Elements of list/tuple and map/object values can be accessed using
the square-bracket index notation, like local.list[3]
. The expression within
the brackets must be a whole number for list and tuple values or a string
for map and object values.
Map/object attributes with names that are valid identifiers can also be accessed
using the dot-separated attribute notation, like local.object.attrname
.
In cases where a map might contain arbitrary user-specified keys, we recommend
using only the square-bracket index notation (local.map["keyname"]
).
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 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
.
Waypoint configuration 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 in the following order of operations:
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. Waypoint 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 addinga
andb
together.a - b
returns the result of subtractingb
froma
.a * b
returns the result of multiplyinga
andb
.a / b
returns the result of dividinga
byb
.a % b
returns the remainder of dividinga
byb
. This operator is generally useful only when used with whole numbers.-a
returns the result of multiplyinga
by-1
.
Equality Operators
The equality operators both take two values of any type and produce boolean values as results.
a == b
returnstrue
ifa
andb
both have the same type and the same value, orfalse
otherwise.a != b
is the opposite ofa == b
.
Comparison Operators
The comparison operators all expect number values and produce boolean values as results.
a < b
returnstrue
ifa
is less thanb
, orfalse
otherwise.a <= b
returnstrue
ifa
is less than or equal tob
, orfalse
otherwise.a > b
returnstrue
ifa
is greater thanb
, orfalse
otherwise.a >= b
returnstrue
ifa
is greater than or equal tob
, orfalse
otherwise.
Logical Operators
The logical operators all expect bool values and produce bool values as results.
a || b
returnstrue
if eithera
orb
istrue
, orfalse
if both arefalse
.a && b
returnstrue
if botha
andb
aretrue
, orfalse
if either one isfalse
.!a
returnstrue
ifa
isfalse
, andfalse
ifa
istrue
.
Conditional Expressions
A conditional expression uses the value of a bool expression to select one of two values.
The syntax of a conditional expression is as follows:
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:
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 Waypoint can determine what type the whole conditional expression will return without knowing the condition value.
Function Calls
Waypoint configuration has a number of built-in functions 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:
The function name specifies which function to call. Each defined function expects a specific number of arguments with specific value types, and returns a specific value type as a result.
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:
Expanding Function Arguments
If the arguments to pass to a function are available in a list or tuple value,
that value can be expanded into separate arguments. Provide the list value as
an argument and follow it with the ...
symbol:
The expansion symbol is three periods (...
), not a Unicode ellipsis character
(…
). Expansion is a special syntax that is only available in function calls.
Available Functions
For a full list of available functions, see the function reference.
for
Expressions
A for
expression creates a complex type value by transforming
another complex 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 the following expression
produces a list of strings with all-uppercase letters:
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:
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, which can produce a value with fewer elements than
the source:
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:
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:
Splat Expressions
A splat expression is a 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 produced with the following for
expression:
This is equivalent to the following splat expression:
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:
The above expression is equivalent to the following for
expression:
String Literals
Waypoint configuration 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 supplementary 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:
The <<
marker followed by any identifier at the end of a line introduces the
sequence. Waypoint 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 begins 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 stanza:
To improve on this, Waypoint also accepts an indented heredoc string variant
that is introduced by the <<-
sequence:
In this case, Waypoint 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:
Backslash sequences are not interpreted in a heredoc string expression. Instead, the backslash character is interpreted literally.
In both quoted and heredoc string expressions, Waypoint supports template
sequences that begin with ${
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 let you directly embed expressions into a string
literal, to dynamically construct strings from other values.
Interpolation
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:
In the above example, the named object var.name
is accessed and its value
inserted into the string, producing a result like "Hello, Juan!".
Directives
A %{ ... }
sequence is a directive, which allows for conditional
results and iteration over collections, similar to conditional
and for
expressions.
The following directives are supported:
The
if <BOOL>
/else
/endif
directive chooses between two templates based on the value of a bool expression:The
else
portion may be omitted, in which case the result is an empty string if the condition expression returnsfalse
.The
for <NAME> in <COLLECTION>
/endfor
directive iterates over the elements of a given collection or structural value and evaluates a given template once for each element, concatenating the results together: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 template directives to be formatted for readability without adding
unwanted spaces and newlines to the result, all template sequences can include
optional strip markers (~
), immediately after the opening characters or
immediately before the end. When a strip marker is present, the template
sequence consumes all of the literal whitespace (spaces and newlines) either
before the sequence (if the marker appears at the beginning) or after (if the
marker appears at the end):
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:
When using template directives, we recommend always using the "heredoc" string literal form and then formatting the template over multiple lines for readability. Quoted string literals should usually include only interpolation sequences.