Terraform Study Notes: Read, generate, and modify configuration pt.2
In the previous chapter, we’ve seen how to write a Terraform configuration using variables and handling secrets. Let’s continue the topic by diving into Expressions, Collections Structural Types, and the anatomy of a resource with the way to address resources and modules in the configuration.
Expressions, Collections, and Structural Types
Expressions refer to or compute values within a configuration
- can be used in several places in the Terraform language, but some contexts limit which expression constructs are allowed, such as requiring a literal value of a particular type or forbidding references to resource attributes.
- can experiment with the behavior of Terraform’s expressions from the Terraform expression console
- Can be used with:
- Types and values
- like input variables can use the following types: string, number, bool, list, set, map
-
there is one special value that has no type,
null
: a value that represents absence or omission - A literal expression is an expression that directly represents a particular constant value
- Elements of list/tuple and map/object values can be accessed using the square-bracket index notation, like
local.list[3]
. - Map/object attributes with names that are valid identifiers can also be accessed using the dot-separated attribute notation, like
local.object.attrname
. - Terraform automatically converts number and bool values to strings when needed.
- Strings and Template
- String literals are the most complex kind of literal expression in Terraform, and also the most commonly used.
- supports both a quoted syntax and a “heredoc” syntax for strings
- Terraform also supports a “heredoc” style of string literal inspired by Unix shell languages, which allows multi-line strings to be expressed more clearly. (Don’t use “heredoc” strings to generate JSON or YAML)
- the
jsonencode
function or theyamlencode
function can handle and guarantee valid JSON or YAML syntax. - The standard heredoc form (shown above) treats all space characters as literal spaces
- Within quoted and heredoc string expressions, the sequences
${
and%{
begin template sequences- A
${ ... }
sequence is an interpolation, that evaluates the expression given between the markers, converts the result to a string if necessary, and then inserts it into the final string - A
%{ ... }
sequence is a directive, which allows for conditional results and iteration over collections, similar to conditional andfor
expressions
- A
- 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
- References to Values
- Terraform makes several kinds of named values available
-
The main kinds of named values available in Terraform are:
- Resources
- Input variables
- Local values
- Child module outputs
- Data sources
- Filesystem and workspace info
- Block-local values
<RESOURCE TYPE>.<NAME>
represents a managed resource of the given type and name- the value of a resource reference can vary, depending on whether the resource uses
count
orfor_each
:- If the resource doesn’t use
count
orfor_each
, the reference’s value is an object - If the resource has the
count
argument set, the reference’s value is a list of objects representing its instances - If the resource has the
for_each
argument set, the reference’s value is a map of objects representing its instances
- If the resource doesn’t use
var.<NAME>
is the value of the input variable of the given namelocal.<NAME>
is the value of the local value of the given namemodule.<MODULE NAME>
is a value representing the results of amodule
block.- To access one of the module’s output values, use
module.<MODULE NAME>.<OUTPUT NAME>
- If the corresponding
module
usesfor_each
then the value will be a map of objects whose keys correspond with the keys in thefor_each
expression, and whose values are each object with one attribute for each output value defined in the child module, each representing one module instance.
- To access one of the module’s output values, use
data.<DATA TYPE>.<NAME>
is an object representing a data resource of the given data source type and name- For filesystem and workspace info
path.module
is the filesystem path of the module where the expression is placedpath.root
is the filesystem path of the root module of the configurationpath.cwd
is the filesystem path of the original working directory from where you ran Terraform before applying any-chdir
argumentterraform.workspace
is the name of the currently selected workspace.
- Within the bodies of certain blocks, or in some other specific contexts, there are other named values available beyond the global values listed above
count.index
, in resources that use thecount
meta-argument.each.key
/each.value
, in resources that use thefor_each
meta-argument.self
, in provisioner and connection blocks.
- Constructs like resources and module calls often use references to named values in their block bodies, and Terraform analyzes these expressions to automatically infer dependencies between objects
- The most common reference type is a reference to an attribute of a resource that has been declared either with a
resource
ordata
block - When defining the schema for a resource type, a provider developer can mark certain attributes as sensitive, in which case Terraform will show a placeholder marker
(sensitive value)
instead of the actual value when rendering a plan involving that attribute - 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.
- Operators
- An operator is a type of expression that transforms or combines one or more other expressions
-
The arithmetic operators all expect number values and produce number values as a result:
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
.
-
The equality operators both take two values of any type and produce boolean values as results.
-
The comparison operators all expect number values and produce boolean values as results.
-
The logical operators all expect bool values and produce bool values as result.
- Function Calls
- The Terraform language has several built-in functions that can be used in expressions to transform and combine values
-
1<FUNCTION NAME>(<ARGUMENT 1>, <ARGUMENT 2>)
-
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:1<span class="code-lines_linesColumn__szsmm code-lines_styledScrollbars__2NpYc"><span class="code-lines_linesScrollableContent___HFem"><span class="code-lines_LineOfCode__Q1cnP g-type-code code-lines_padRight__jnuQs">min(<span class="token punctuation">[</span><span class="token number">55</span>, <span class="token number">2453</span>, <span class="token number">2</span><span class="token punctuation">]</span>...)</span></span></span> - When using sensitive data, such as an input variable or an output defined as sensitive as function arguments, the result of the function call will be marked as sensitive.
- Most of Terraform’s built-in functions are, in programming language terms, pure functions. This means that their result is based only on their arguments and so it doesn’t make any practical difference when Terraform calls them
- The small set of special functions includes
file
,templatefile
,timestamp
, anduuid
. If you are not working with these functions then you don’t need to read this section, although the information here may still be interesting background information
- The small set of special functions includes
- Conditional Expressions
-
The syntax of a conditional expression is as follows:
12<span class="code-lines_linesColumn__szsmm code-lines_styledScrollbars__2NpYc"><span class="code-lines_linesScrollableContent___HFem"><span class="code-lines_LineOfCode__Q1cnP g-type-code code-lines_padRight__jnuQs">condition ? true_val : false_val</span></span></span>Ifcondition
istrue
then the result istrue_val
. Ifcondition
isfalse
then the result isfalse_val
. - The condition can be any expression that resolves to a boolean value. This will usually be an expression that uses equality, comparison, or logical operators.
- You can create conditions that produce custom error messages for several types of objects in a configuration
- 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.
For example, the following expression is valid and will always return a string, because in Terraform all numbers can convert automatically to a string using decimal digits:
1<span class="code-lines_linesColumn__szsmm code-lines_styledScrollbars__2NpYc"><span class="code-lines_linesScrollableContent___HFem"><span class="code-lines_LineOfCode__Q1cnP g-type-code code-lines_padRight__jnuQs">var.example ? <span class="token number">12</span> : <span class="token string">"hello"</span></span></span></span>
-
- For Expressions
- A
for
expression creates a complex type value by transforming another complex type value -
For example, if
var.list
were a list of strings, then the following expression would produce a tuple of strings with all-uppercase letters:1<span class="code-lines_linesColumn__szsmm code-lines_styledScrollbars__2NpYc"><span class="code-lines_linesScrollableContent___HFem"><span class="code-lines_LineOfCode__Q1cnP g-type-code code-lines_padRight__jnuQs"><span class="token punctuation">[</span>for s in var.list : upper(s)<span class="token punctuation">]</span></span></span></span> - A
for
expression’s input (given after thein
keyword) can be a list, a set, a tuple, a map, or an object. - The type of brackets around the
for
expression decides what type of result it produces.-
1{for s in var.list : s => upper(s)}
-
-
A
for
expression can also include an optionalif
clause to filter elements from the source collection, producing a value with fewer elements than the source value:1<span class="code-lines_linesColumn__szsmm code-lines_styledScrollbars__2NpYc"><span class="code-lines_linesScrollableContent___HFem"><span class="code-lines_LineOfCode__Q1cnP g-type-code code-lines_padRight__jnuQs">[for s in var.list : upper(s) if s != ""]</span></span></span> - Because
for
expressions can convert from unordered types (maps, objects, sets) to ordered types (lists, tuples), Terraform must choose an implied ordering for the elements of an unordered collection - If the result type is an object (using
{
and}
delimiters) then normally the given key expression must be unique across all elements in the result, or Terraform will return an error. - The
for
expressions mechanism is for constructing collection values from other collection values within expressions, which you can then assign to individual resource arguments that expect complex values.
- A
- Splat Expressions
- A splat expression 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 attributeid
, then a list of the ids could be produced with the followingfor
expression:1<span class="code-lines_linesColumn__szsmm code-lines_styledScrollbars__2NpYc"><span class="code-lines_linesScrollableContent___HFem"><span class="code-lines_LineOfCode__Q1cnP g-type-code code-lines_padRight__jnuQs"><span class="token punctuation">[</span>for o in var.list : o.id<span class="token punctuation">]</span></span></span></span> - The splat expression patterns shown above apply only to lists, sets, and tuples. To get a similar result with a map or object value you must use
for
expressions. - Splat expressions have a special behavior when you apply them to a value that isn’t a list, set, or tuple
- A splat expression provides a more concise way to express a common operation that could otherwise be performed with a
- Dynamic Blocks
- Within top-level block constructs like resources, expressions can usually be used only when assigning a value to an argument using the
name = expression
form. - A
dynamic
block acts much like afor
expression, but produces nested blocks instead of a complex typed value. It iterates over a given complex value, and generates a nested block for each element of that complex value.- The label of the dynamic block (
"setting"
in the example above) specifies what kind of nested block to generate. - The
for_each
the argument provides the complex value to iterate over. - The
iterator
argument (optional) sets the name of a temporary variable that represents the current element of the complex value. If omitted, the name of the variable defaults to the label of thedynamic
block ("setting"
in the example above). - The
labels
argument (optional) is a list of strings that specifies the block labels, in order, to use for each generated block. You can use the temporary iterator variable in this value. - The nested
content
block defines the body of each generated block. You can use the temporary iterator variable inside this block.
- The label of the dynamic block (
-
The iterator object (
setting
in the example above) has two attributes: - Some providers define resource types that include multiple levels of blocks nested inside one another. You can generate these nested structures dynamically when necessary by nesting
dynamic
blocks in thecontent
portion of otherdynamic
blocks - Overuse of
dynamic
blocks can make configuration hard to read and maintain, so we recommend using them only when you need to hide details in order to build a clean user interface for a re-usable module
- Within top-level block constructs like resources, expressions can usually be used only when assigning a value to an argument using the
- Custom Conditions
- You can create conditions that produce custom error messages for several types of objects in a configuration. For example, you can add a condition to an input variable that checks whether incoming image IDs are formatted properly
-
Terraform’s different custom conditions are best suited to various situations. Use the following broad guidelines to select the best custom condition for your use case:
- Check blocks with assertions validate your infrastructure as a whole. Additionally, check blocks do not prevent or block the overall execution of Terraform operations.
- Validation conditions or output postconditions can ensure your configuration’s inputs and outputs meet specific requirements.
- Resource preconditions and postconditions can validate that Terraform produces your configuration with predictable results.
- Type Constraints
- Terraform module authors and provider developers can use detailed type constraints to validate user-provided values for their input variables and resource arguments
-
Type constraints are expressed using a mixture of type keywords and function-like constructs called type constructors.
- Type keywords are unquoted symbols that represent a static type.
- Type constructors are unquoted symbols followed by a pair of parentheses, which contain an argument that specifies more information about the type. Without its argument, a type constructor does not fully represent a type; instead, it represents a kind of similar type.
- A complex type is a type that groups multiple values into a single value. Complex types are represented by type constructors, but several of them also have shorthand keyword versions.
- A collection type allows multiple values of one other type to be grouped together as a single value. The type of value within a collection is called its element type
- A structural type allows multiple values of several distinct types to be grouped together as a single value
- Version Constraints
-
Anywhere that Terraform lets you specify a range of acceptable versions for something, it expects a specially formatted string known as a version constraint. Version constraints are used when configuring:
- Modules
- Provider requirements
- The
required_version
setting in theterraform
block.
-
The following operators are valid:
-
=
(or no operator): Allows only one exact version number. Cannot be combined with other conditions. !=
: Excludes an exact version number.>
,>=
,<
,<=
: Comparisons against a specified version, allowing versions for which the comparison is true. “Greater-than” requests newer versions, and “less-than” requests older versions.~>
: Allows only the rightmost version component to increment. This format is referred to as the pessimistic constraint operator. For example, to allow new patch releases within a specific minor release, use the full version number:
-
-
- Types and values
Create and differentiate resource configuration
Resources are the most important element in the Terraform language. Each resource block describes one or more infrastructure objects, such as virtual networks, compute instances, or higher-level components such as DNS records.
-
Resource Blocks document the syntax for declaring resources.
-
Resource Behavior explains in more detail how Terraform handles resource declarations when applying a configuration.
-
The Meta-Arguments section documents special arguments that can be used with every resource type, including
depends_on
,count
,for_each
,provider
, andlifecycle
. -
Provisioners documents configuring post-creation actions for a resource using the
provisioner
andconnection
blocks. Since provisioners are non-declarative and potentially unpredictable, we strongly recommend that you treat them as a last resort.
A “resource” block declares a resource of a specific type with a specific local name. The name is used to refer to this resource in the same Terraform module but has no meaning outside that module’s scope.
The resource type (“aws_instance”) and name (“Web”) together must be unique within a module because they serve as an identifier for a given resource.
1 2 3 4 |
<span class="code-lines_linesColumn__szsmm code-lines_styledScrollbars__2NpYc"><span class="code-lines_linesScrollableContent___HFem"><span class="code-lines_LineOfCode__Q1cnP g-type-code code-lines_padRight__jnuQs"><span class="token keyword">resource <span class="token type variable">"aws_instance"</span></span> <span class="token string">"web"</span> <span class="token punctuation">{</span> </span><span class="code-lines_LineOfCode__Q1cnP g-type-code code-lines_padRight__jnuQs"> <span class="token property">ami</span> <span class="token punctuation">=</span> <span class="token string">"ami-a1b2c3d4"</span> </span><span class="code-lines_LineOfCode__Q1cnP g-type-code code-lines_padRight__jnuQs"> <span class="token property">instance_type</span> <span class="token punctuation">=</span> <span class="token string">"t2.micro"</span> </span><span class="code-lines_LineOfCode__Q1cnP g-type-code code-lines_padRight__jnuQs"><span class="token punctuation">}</span></span></span></span> |
A provider is a plugin for Terraform that offers a collection of resource types. Each resource type is implemented by a provider
To manage resources, a Terraform module must specify the required providers, see Provider Requirements.
Most providers need some configuration to access their remote API, which is provided by the root module, see Provider Configuration
Most of the arguments within the body of a resource
block are specific to the selected resource type.
Every Terraform provider has its own documentation, describing its resource types and their arguments. Most publicly available providers are distributed on the Terraform Registry, which also hosts their documentation
A resource
block declares that you want a particular infrastructure object to exist with the given settings. If you are writing a new configuration for the first time, the resources it defines will exist only in the configuration, and will not yet represent real infrastructure objects in the target platform.
When Terraform creates a new infrastructure object represented by a resource
block, the identifier for that real object is saved in Terraform’s state, allowing it to be updated and destroyed in response to future changes.
In summary, applying a Terraform configuration will:
- Create resources that exist in the configuration but are not associated with a real infrastructure object in the state.
- Destroy resources that exist in the state but no longer exist in the configuration.
- Update in-place resources whose arguments have changed.
- Destroy and re-create resources whose arguments have changed but which cannot be updated in-place due to remote API limitations.
Use the <RESOURCE TYPE>.<NAME>.<ATTRIBUTE>
syntax to reference a resource attribute in an expression
Most resources in a configuration don’t have any particular relationship, and Terraform can make changes to several unrelated resources in parallel.
Most resource dependencies are handled automatically. Terraform analyses any expressions within a resource
block to find references to other objects, and treats those references as implicit ordering requirements when creating, updating, or destroying resources.
However, some dependencies cannot be recognized implicitly in configuration. In these rare cases, the depends_on
meta-argument can explicitly specify a dependency.
While most resource types correspond to an infrastructure object type that is managed via a remote network API, certain specialized resource types operate only within Terraform itself, calculating some results and saving those results in the state for future use. For example, local-only resource types exist for generating private keys, issuing self-signed TLS certificates, and even generating random ids. While these resource types often have a more marginal purpose than those managing “real” infrastructure objects, they can be useful as glue to help connect together other resources. The behavior of local-only resources is the same as all other resources, but their result data exists only within the Terraform state. “Destroying” such a resource means only removing it from the state, and discarding its data.
The Terraform language defines several meta-arguments, which can be used with any resource type to change the behavior of resources.
The following meta-arguments are documented on separate pages:
depends_on
, for specifying hidden dependenciescount
, for creating multiple resource instances according to a countfor_each
, to create multiple instances according to a map, or set of stringsprovider
, for selecting a non-default provider configurationlifecycle
, for lifecycle customizationsprovisioner
, for taking extra actions after resource creation
Some resource types provide a special timeouts
nested block argument that allows you to customize how long certain operations are allowed to take before being considered to have failed. Timeouts are handled entirely by the resource type implementation in the provider, but resource types offering these features follow the convention of defining a child block called timeouts
that has a nested argument named after each operation that has a configurable timeout value
Use resource addressing and resource parameters to connect resources together
A resource address is a string that identifies zero or more resource instances in your overall configuration.
An address is made up of two parts:
1 |
<span class="code-lines_linesColumn__szsmm code-lines_styledScrollbars__2NpYc"><span class="code-lines_linesScrollableContent___HFem"><span class="code-lines_LineOfCode__Q1cnP g-type-code code-lines_padRight__jnuQs">[module path][resource spec]</span></span></span> |
In some contexts, Terraform might allow for an incomplete resource address that only refers to a module as a whole, or that omits the index for a multi-instance resource. In those cases, the meaning depends on the context, so you’ll need to refer to the documentation for the specific feature you are using which parses resource addresses.
A module is a container for multiple resources that are used together.
Every Terraform configuration has at least one module, known as its root module, which consists of the resources defined in the
.tf
files in the main working directory.A module can call other modules, which lets you include the child module’s resources into the configuration in a concise way. Modules can also be called multiple times, either within the same configuration or in separate configurations, allowing resource configurations to be packaged and re-used.
This page describes how to call one module from another. For more information about creating re-usable child modules, see Module Development.
Module topics will be covered in (.. link …) this post of this Study Guide.
A module path addresses a module within the tree of modules. It takes the form:
1 |
<span class="code-lines_linesColumn__szsmm code-lines_styledScrollbars__2NpYc"><span class="code-lines_linesScrollableContent___HFem"><span class="code-lines_LineOfCode__Q1cnP g-type-code code-lines_padRight__jnuQs">module.module_name[module index]</span></span></span> |
module
– Module keyword indicating a child module (non-root). Multiplemodule
keywords in a path indicate nesting.module_name
– User-defined name of the module.[module index]
– (Optional) Index to select an instance from a module call that has multiple instances, surrounded by square bracket characters ([
and]
).
A resource spec addresses a specific resource instance in the selected module. It has the following syntax:
1 |
<span class="code-lines_linesColumn__szsmm code-lines_styledScrollbars__2NpYc"><span class="code-lines_linesScrollableContent___HFem"><span class="code-lines_LineOfCode__Q1cnP g-type-code code-lines_padRight__jnuQs">resource_type.resource_name[instance index]</span></span></span> |
resource_type
– Type of the resource being addressed.resource_name
– User-defined name of the resource.[instance index]
– (Optional) Index to select an instance from a resource that has multiple instances, surrounded by square bracket characters ([
and]
).
The following specifications apply to index values on modules and resources with multiple instances:
[N]
whereN
is a0
-based numerical index into a resource with multiple instances specified by thecount
meta-argument. Omitting an index when addressing a resourcecount > 1
means that the address references all instances.["INDEX"]
whereINDEX
is an alphanumerical key index into a resource with multiple instances specified by thefor_each
meta-argument.
Following an example using count and output one and multiple IDs of the created container. You can find this example in my GitHub repo https://github.com/linoproject/terraform_lab under folder example_4.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
resource "docker_container" "nginx" { count = 4 image = docker_image.nginx.image_id name = "tutorial${count.index}" } ### Output variables output "container_id" { description = "First Container ID" value = docker_container.nginx[0].id } output "all_container_id" { description = "First Container ID" value = docker_container.nginx[*].id } |
After init and applying the output should be like the following:
The following example implements for_each statement (folder example_5):
Note: don’t forget to issue terraform destroy on previous examples.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
resource "docker_container" "nginx" { for_each = { "example1" = "tutorial1", "example2" = "tutorial2" } image = docker_image.nginx.image_id name = "${each.value}" } ### Output variables output "container_id_example1" { description = "First Container ID" value = docker_container.nginx["example1"].id } output "container_id_example2" { description = "First Container ID" value = docker_container.nginx["example2"].id } |
2 pensieri riguardo “Terraform Study Notes: Read, generate, and modify configuration pt.2”
I commenti sono chiusi.