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

      • 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 the yamlencode 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
        • ${ ... } 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
        • %{ ... } sequence is a directive, which allows for conditional results and iteration over collections, similar to conditional and for expressions
      • 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 or for_each:
        • If the resource doesn’t use count or for_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
      • var.<NAME> is the value of the input variable of the given name
      • local.<NAME> is the value of the local value of the given name
      • module.<MODULE NAME> is a value representing the results of module block.
        • To access one of the module’s output values, use module.<MODULE NAME>.<OUTPUT NAME>
        • If the corresponding module uses for_each then the value will be a map of objects whose keys correspond with the keys in the for_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.
      • 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 placed
        • path.root is the filesystem path of the root module of the configuration
        • path.cwd is the filesystem path of the original working directory from where you ran Terraform before applying any -chdir argument
        • terraform.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
      • 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 or data 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 adding a and b together.
        • a - b returns the result of subtracting b from a.
        • a * b returns the result of multiplying a 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.
      • 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.
      • 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.
      • The logical operators all expect bool values and produce bool values as result.

        • 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.
    • Function Calls
      • The Terraform language has several built-in functions that can be used in expressions to transform and combine values
      • 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:

      • 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 filetemplatefiletimestamp, and uuid. 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
    • Conditional Expressions
      • 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.
      • 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:

    • For Expressions
      • 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:

      • for expression’s input (given after the in 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.
      • for expression can also include an optional if clause to filter elements from the source collection, producing a value with fewer elements than the source value:

      • 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.
    • Splat Expressions
      • 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 attribute id, then a list of the ids could be produced with the following for expression:

      • 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
    • 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.
      • dynamic block acts much like a for 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 the dynamic 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 iterator object (setting in the example above) has two attributes:

        • key is the map key or list element index for the current element. If the for_each expression produces a set value then key is identical to value and should not be used.
        • value is the value of the current element.
      • 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 the content portion of other dynamic 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
    • 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:

        1. Check blocks with assertions validate your infrastructure as a whole. Additionally, check blocks do not prevent or block the overall execution of Terraform operations.
        2. Validation conditions or output postconditions can ensure your configuration’s inputs and outputs meet specific requirements.
        3. 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.
      • 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.
      • 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
      • 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:

      • 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:

          • ~> 1.0.4: Allows Terraform to install 1.0.5 and 1.0.10 but not 1.1.0.
          • ~> 1.1: Allows Terraform to install 1.2 and 1.10 but not 2.0.

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_oncountfor_eachprovider, and lifecycle.

  • Provisioners documents configuring post-creation actions for a resource using the provisioner and connection 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.

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

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 keysissuing 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:

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

resource address is a string that identifies zero or more resource instances in your overall configuration.

An address is made up of two parts:

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.

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:

  • module – Module keyword indicating a child module (non-root). Multiple module 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:

  • 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] where N is a 0-based numerical index into a resource with multiple instances specified by the count meta-argument. Omitting an index when addressing a resource  count > 1 means that the address references all instances.
  • ["INDEX"] where INDEX is an alphanumerical key index into a resource with multiple instances specified by the for_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.

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.

 

   Send article as PDF   

2 pensieri riguardo “Terraform Study Notes: Read, generate, and modify configuration pt.2

I commenti sono chiusi.