Terraform Scripting Concepts: Part 1 of 2 @ SerDigital64 | Monday, Dec 27, 2021 | 7 | Sunday, Feb 6, 2022

Overview

Terraform is an automation tool focused on infrastructure provisioning. The product is developed by HashiCorp, and the command line version is provided as an Open Source Project available on GitHub.

Terraform has the following core components:

  • Terraform CLI: stand-alone app that provides the run-time environment for Terraform Scripts and maintains build-state information of Managed Resources.
  • Terraform Script: text file with Terraform Language code used to describe the required end-state of the Managed Resources.
  • Managed Resource: logical representation of a real infrastructure resource in the target infrastructure.
  • Terraform Provider: stand-alone apps (plugins) that are used by the Terraform CLI to interact with Managed Resources.
  • Terraform Workspace: working directory in which Terraform Scripts are located and the Terraform CLI stores data files (state, temporary, plugins, etc.)

Using Terraform

CLI Installation

The recommended method for production environments is to add HashiCorp’s package repository to the OS package manager and install it using native tools.

For more relaxed environments the binary package provides a simpler alternative that does not require root privilege:

1
2
3
4
5
6
# Create the installation repository
INSTALL_PATH="${HOME}/terraform-cli"; mkdir "${INSTALL_PATH}"; cd "${INSTALL_PATH}"
# Download the binary package
curl -LO https://releases.hashicorp.com/terraform/1.1.2/terraform_1.1.2_linux_amd64.zip
# Uncompress the binary package
ZIP_PACKAGE='terraform_1.1.2_linux_amd64.zip'; unzip "${ZIP_PACKAGE}" && rm -f "${ZIP_PACKAGE}"

Running scripts

The execution process of Terraform Scripts is divided into stages:

  1. init: prepares the Terraform Workspace, sets run-time parameters, downloads, and installs providers.
  2. plan: creates/updates state information of managed resources and executes a dry-run to create the change plan. For already existing resources, if the provider is unable to modify attributes on the fly it will propose a destructive change (remove and recreate the resource with the new end-state).
  3. apply: executes the change plan, and updates resource state information. The provider will modify the target infrastructure by creating, updating and removing resources until the end-state is reached.

To execute the script the Terraform CLI must be already inside the workspace directory:

1
2
3
4
5
6
7
cd WORKSPACE_PATH
# Initialize the workspace
terraform init
# Prepare and review the action plan
terraform plan
# Execute the action plan
terraform apply

Notice that the user running the process doesn’t need root privilege, just write permissions to the workspace path.

Script structure

The first thing to understand about Terraform Scripts is that the Terraform Language is not for general-purpose development. Based on HCL (Hashicorp Configuration Language), it’s specifically designed to describe infrastructure resources.

Terraform Scripts are developed by defining blocks: a complex data structure that describes a configuration object through its attributes.

Use the following template for declaring blocks:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
BLOCK_TYPE "RESOURCE_TYPE" "RESOURCE_LABEL1" {

  ATTRIBUTE1 = VALUE
  ATTRIBUTE2 = VALUE
  ATTRIBUTEN = VALUE

  # Nested block
  BLOCK_TYPE "RESOURCE_TYPE" "RESOURCE_LABEL2" {
    ATTRIBUTE = VALUE
  }

}
  • BLOCK_TYPE: defines what type of configuration object is going to be described. Notice that Blocks can have any number of attributes and other blocks (nested).
  • RESOURCE_TYPE: optional field used to select object subtypes. Not all objects will require one.
  • RESOURCE_LABEL: optional field to assign a label (ID) to the object.
  • ATTRIBUTE: name of the object’s attribute.
  • VALUE: value that will be assigned to the object’s attribute.

The following list shows common built-in block types:

  • variable: declare input parameter.
  • output: declare module output variable.
  • local: declare local variable.
  • terraform: provide project-wide terraform configuration settings.
  • provider: define and configure a terraform provider.
  • resource: configure a provider’s resource.
  • data: provided run-time information for a provider’s resource

Additional types and subtypes are implemented through Terraform Providers. For example:

Scripts can have comments anywhere:

  • //: single-line comment. Anything at the right is ignored.
  • #: single-line comment. Anything at the right is ignored.
  • /* */: multi-line comment. Anything between the delimiters is ignored.

As a good practice, after the script is created/updated run the built-in linter and code-formatter:

1
2
3
cd WORKSPACE_PATH
terraform fmt
terraform validate

Using Variables

Declaration

As everything in Teraform, variables are also a block type. Use the following template to declare variables:

1
2
3
locals {
  VARIABLE_NAME = VALUE
}

Data Types

Both variables and attributes have types:

  • string: a sequence of Unicode characters.
  • number: integer or float numbers.
  • bool: boolean values.
  • list: indexed sequence of values. The index is numerical and starts at cero. VALUEs can be of any type.
  • map: group of key + value pairs. VALUEs can be of any type.
  • null: empty value.

Types are inferred from the value (i.e. no explicit data type declaration). For example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
locals {
  test_string = "abc123"
  test_number_integer = 15
  test_number_float = 15.1
  test_bool = true
  test_list = [
    "value 1",
    100
  ]
  test_map = {
    key1 = "value 1",
    key2 = 100
  }
}

Parameters

Parameters are variables that are used to provide run-time data to Terraform Scripts. They are declared using the variable block type:

1
2
3
4
variable "PARAMETER_NAME" {
  # Optional parameter attributes
  ATTRIBUTE = VALUE
}

They are different from regular variables as they allow attributes to further describe the parameter:

  • default: value that is assigned if none is provided.
  • description: text message that is used to describe the parameter.
  • type: expected variable type. This is not used to declare the type, but rather to constrain the provided value.
  • validation: validation block used to perform additional checks.
  • sensitive: flag to restrict script output. Used to mask sensitive values such as passwords.
  • nullable: flag to indicate if the value can be null.

For example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
variable "aws_region" {
  type = string
  default = "us-west-1a"
  description = "target AWS region"
  nullable = false
  sensitive = false
}

variable "xaas_test_api_secret" {
  type = string
  description = "API secret for the Test API"
  nullable = false
  sensitive = true
}

Output

In addition to the local and variable block types, Terraform provides the output type to define variables that will be exported and made available to external apps and scripts. The following attributes are available:

  • value: the value assigned to the variable.
  • description: describe what the value represents.
  • sensitive: flag to restrict script output. Used to mask sensitive values such as passwords.

For example:

1
2
3
4
5
output "sysadmin_key" {
  value = "secret-api-key"
  description = "API secret for the new service"
  sensitive = true
}

Retreive variable’s value

In general, block attributes (variables) can be referenced from within other blocks by using the following structure: BLOCK_TYPE.ATTRIBUTE or BLOCK_TYPE.BLOCK_LABEL.ATTRIBUTE

In addition to the generic format there are special cases to consider:

  • Locals: use local instead of locals: local.ATTRIBUTE
  • Parameters: use the short form var instead of the full type name variable: var.ATTRIBUTE
  • Outputs: prepend module to module name where the output is declared: module.MODULE_NAME.OUTPUT_NAME
  • Data: prepend data to the data block name that provides the attribute: data.DATA_BLOCK_NAME.ATTRIBUTE

Variables can also be used in String Templates (simple programming code that can be embedded inside string declarations): "${BLOCK_TYPE.ATTRIBUTE}". The resulting string will replace the ${} expression with the value of the variable.

The following example shows the three variable types seen so far:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
locals {
  api_secret_seed = "abc123"
}

variable "new_service_name" {
  type = string
  default = "new_service_name"
  description = "What name will the new service have"
  nullable = false
  sensitive = false
}

output "service_secret" {
  value = "${local.api_secret_seed}${var.new_service_name}"
  description = "API secret for the new service"
  sensitive = true
}

Next Steps

Continue reading the second part of the tutorial: Terraform Scripting Concepts: Part 2 of 2

This article is licensed under a Creative Commons Attribution 4.0 International License. For copyright information on the product or products mentioned inhere refer to their respective owner.

Disclaimer

Opinions presented in this article are personal and belong solely to me, and do not represent people or organizations associated with me in a professional or personal way. All the information on this site is provided “as is” with no guarantee of completeness, accuracy or the results obtained from the use of this information.

© 2021 - 2022 SerDigital64's Blog

Powered by Hugo with theme Dream.

Articles in this site are licensed under a [Creative Commons Attribution 4.0 International License](http://creativecommons.org/licenses/by/4.0)

avatar

SerDigital64's BlogTraveler log from my journey through the lands of the ever evolving digital world

About SerDigital64
███████╗███████╗██████╗                         
██╔════╝██╔════╝██╔══██╗                        
███████╗█████╗  ██████╔╝                        
╚════██║██╔══╝  ██╔══██╗                        
███████║███████╗██║  ██║                        
╚══════╝╚══════╝╚═╝  ╚═╝                        
                                  
██████╗ ██╗ ██████╗ ██╗████████╗ █████╗ ██╗     
██╔══██╗██║██╔════╝ ██║╚══██╔══╝██╔══██╗██║     
██║  ██║██║██║  ███╗██║   ██║   ███████║██║     
██║  ██║██║██║   ██║██║   ██║   ██╔══██║██║     
██████╔╝██║╚██████╔╝██║   ██║   ██║  ██║███████╗
╚═════╝ ╚═╝ ╚═════╝ ╚═╝   ╚═╝   ╚═╝  ╚═╝╚══════╝
                                  
██████╗  ██╗  ██╗                               
██╔════╝ ██║  ██║                               
███████╗ ███████║                               
██╔═══██╗╚════██║                               
╚██████╔╝     ██║                               
╚═════╝      ╚═╝                               


Solutions_Architect && SysAdmin && DevOpsEngineer
Developer = 'for_the_fun'
Linux && OSS_advocate
Sci_Fi = 'fan'
Photography && DIY == enthusiast()

eMail('serdigital64@gmail.com')
Articles in this site are licensed under a [Creative Commons Attribution 4.0 International License](http://creativecommons.org/licenses/by/4.0)