Terraform Study Notes: Read, generate, and modify configuration pt.1

In the previous chapter, we finally introduced the Terraform basic workflow composed of write, plan, and infrastructure creation/modification. In Terraform, the real usage of Infrastructure as Code is to implement configuration code as a template and apply the desired state in a particular context simply giving a set of environment variables.

If you want to create a set of virtual machines, it’s possible to create a template of a generic virtual machine configuration with the company-specific implementations and apply it in the desired environment using the proper variables like IP address, number of vCPUs, Memory, etc…

So before authoring a terraform file, it’s necessary to know the concepts of variables, outputs, secret handling, resources, and data. Let’s continue our study notes diving into Input, Output, and Local variables

Input, Output, Local variables

Terraform configurations can include variables to make your configuration more dynamic and flexible. Terraform’s input variables don’t change values during a Terraform run such as plan, apply, or destroy

Variables are mission-critical, especially in module creation and usage.

If you’re familiar with traditional programming languages, it can be useful to compare Terraform modules to function definitions:

  • A variable definition is made by “variable” block type and the structure is really simple:

    Input variables are like function arguments. Terraform CLI defines the following optional arguments for variable declarations:

    • default – A default value which then makes the variable optional.
    • type – This argument specifies what value types are accepted for the variable.
    • description – This specifies the input variable’s documentation.
    • validation – A block to define validation rules, usually in addition to type constraints.
    • sensitive – Limits Terraform UI output when the variable is used in configuration.
    • nullable – Specify if the variable can be null within the module.
  • Output values are like function return values: Make information about your infrastructure available on the command line and can expose information for other Terraform configurations to use.

    • The label immediately after the output keyword is the name, which must be a valid identifier. The value argument takes an expression whose result is to be returned to the user. In this example, the expression refers to the private_ip attribute exposed by an aws_instance resource defined elsewhere in this module (not shown). Any valid expression is allowed as an output value
    • output block can optionally include descriptionsensitive, and depends_on:
      • The description should concisely explain the purpose of the output and what kind of value is expected. This description string might be included in documentation about the module. The description will not written in the state file.
      • Terraform will hide values marked as sensitive in the messages from Terraform plan and Terraform apply
      • Since output values are just a means for passing data out of a module, it is usually not necessary to specify depends_on. Terraform analyzes the value expression for an output value and automatically determines a set of dependencies.
  • Local values are like a function’s temporary local variables. A local value assigns a name to an expression, so you can use it multiple times within a module without repeating it.

    • The expressions in local values are not limited to literal constants; they can also reference other values in the module to transform or combine them, including variables, resource attributes, or other local values. Once a local value is declared, you can reference it in expressions as local.<NAME>.

      Local values can be helpful to avoid repeating the same values or expressions multiple times in a configuration, but if overused they can also make a configuration hard to read by future maintainers by hiding the actual values used.

The Terraform language uses the following types for its values:

  • string: a sequence of Unicode characters representing some text, like "hello".
  • number: a numeric value. The number type can represent both whole numbers like 15 and fractional values like 6.283185.
  • bool: a boolean value, either true or falsebool values can be used in conditional logic.
  • list (or tuple): a sequence of values, like ["us-west-1a", "us-west-1c"]. Identify elements in a list with consecutive whole numbers, starting with zero.
  • set: a collection of unique values that do not have any secondary identifiers or ordering.
  • map (or object): a group of values identified by named labels, like {name = "Mabel", age = 52}.

We can improve the config seen in the previous post (.. link ..) by introducing input variables for the container’s name and defining the internal and external ports:

To use input values in the resources just use the reference: var.<variable_name> :

So if you init and apply the configuration the default values will be used (in this case: tutorial, 80 and 8000). Otherwise, you can specify the input variables in two ways:

  • by the command apply:
  • in a key, values file with tfvars extensions
    • To apply the values in the variables.tfvars just issue
    • Note: It’s possible to use JSON instead of key-values file definition just specifying a file with tfvars.json extension and JSON content.

Following the same example, it’s possible also to display the container ID after the creation using output variables:

Then after the config is applied, the container ID will be shown:

This example is available in my GitHub repo under the folder example_2

A further improvement to the config is to implement a local variable able to implement functions like:

  • Numeric Functions (abs, ceil, floor, log…)
  • String Functions (chomp, endswith, format, formatlist, indent, join, lower…)
  • Collection Functions (concat, contains,…)
  • Encoding Functions (base64decode, jsondecode…)
  • Filesystem Functions (abspath, file, fileexists…)
  • Date and Time Functions (formatdate, timeadd…)
  • HashiCorp and Crypto Function (md5, sha1…)
  • IP Network Functions (cidrhost, cidrsubnet…)
  • Type Conversion Functions (can, sensitive, tolist…)

For further about “functions” just check the official documentation: https://developer.hashicorp.com/terraform/language/functions

Following is an example of “local value” usage:

then it’s possible to use this local var as output value:

Handling Secrets

Secrets could be handled in two ways:

  • Injecting using Vault Provider
    • developers looking to safely provision infrastructure using Terraform are given their own set of long-lived credentials: this can be dangerous and difficult to secure.
      • Operators need to manage a large number of static, long-lived credentials
      • Long-lived credentials on a developer’s local machine create a large attack surface area
    • storing your long-lived credentials in a dedicated HashiCorp’s Vault’s Secrets Engine is a solution and Terraform’s Vault provider can generate appropriately scoped & short-lived credentials to be used by Terraform to provision resources in AWS.
  • Using Terraform Cloud secret engine (available only with Terraform Cloud)
    • As the adoption of Terraform Cloud (TFC) grows, more organizations are incorporating it into their automated workflows and existing tooling. Interaction with the TFC API relies on auth tokens generated by the API and used by external systems to automate actions in Terraform Cloud, often as part of an organization’s CI/CD pipelines. Operators are left with the responsibility of tracking which tokens are in use by their organizations
    • The Vault Terraform Cloud secrets engine enables you to generate, manage, and revoke credentials for Terraform Cloud and Terraform Enterprise while adhering to best practices of access and control

Then from the configuration perspective, it’s possible to:

  • Suppress Values in CLI:
    • Setting a variable as sensitive prevents Terraform from showing its value in the plan or apply output, when you use that variable elsewhere in your configuration.
    • Terraform will still record sensitive values in the state, so anyone who can access the state data will have access to the sensitive values in cleartext
    • An output can be marked as containing sensitive material using the optional sensitive argument
  • Handle Sensitive Data in State:
    • Terraform state can contain sensitive data, depending on the resources in use and your definition of “sensitive.”
    • When using local state, the state is stored in plain-text JSON files.
    • When using remote state, the state is only ever held in memory when used by Terraform. It may be encrypted at rest, but this depends on the specific remote state backend.
    • Recommendations
      • manage any sensitive data with Terraform (like database passwords, user passwords, or private keys), treat the state itself as sensitive data
      • Storing state remotely can provide better security:
        • Terraform Cloud always encrypts the state at rest and protects it with TLS in transit
        • The S3 backend supports encryption at rest when the encrypt option is enabled
   Send article as PDF   

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

I commenti sono chiusi.