Terraform 101 tip: element function

27 Aug 2021 by Ryan Ververs-Bijkerk

Category:

    Terraform

Terraform 101 tip: element function

Table of Content

In the last couple of months, my day-to-day work involves a lot working with Terraform. Now sometimes there are features or functions which are not described clearly. Therefore introducing 101 tips, a series sharing some tips that might help you. The first blog post of the series will be covering the function element.

What is Terraform?

For those who are not familiar with Terraform, Terraform is an infrastructure as code (IaC) tool that allows you to build, change, and version infrastructure safely and efficiently. This includes low-level components such as compute instances, storage, and networking, as well as high-level components such as DNS entries, SaaS features, etc. Terraform can manage both existing service providers and custom in-house solutions.

Key Features

  • Infrastructure as Code
  • Execution Plans
  • Resource Graph
  • Change Automation

More information can be found here: Introduction - Terraform by HashiCorp.

Function: element

Let’s start with covering the function documentation. The description of the function is as followed.

Element retrieves a single element from a list.

element(list, index)

The index is zero-based. This function produces an error if used with an empty list. The index must be a non-negative integer. Use the built-in index syntax list[index] in most cases. Use this function only for the special additional “wrap-around” behavior described below. Source: element - Functions - Configuration Language - Terraform by HashiCorp.

Now, this is one of the examples that is shown in the function documentation:

> element(["a", "b", "c"], 1)
b

The function will select the property of an array based on the index number. In most programming languages when reaching a number that is out of the range of the array, you will receive an exception. But the element function will continue from the first item in the array. This will result in the following behavior.

Index Array
0 a
1 b
2 c
3 a
4 b
5 c
6 a

Use case example

To fully understand the function and how to use it let’s cover this by using a use case.

Let’s say it is required to have 6 databases distributed over 3 different locations. This can easily be done using the element function. The example is based on the azurerm_mariadb_server resource.

provider "azurerm" {
  features {}
}

locals {
  locations =["West Europe", "East US", "West US"]
}

resource "azurerm_resource_group" "example" {
  count    = 3
  name     = "example-resources"
  location = element(local.locations, count.index)
}

resource "azurerm_mariadb_server" "example" {
  count               = 6
  name                = "example-mariadb-server-${count.index + 1}"
  location            = element(azurerm_resource_group.example, count.index).location
  resource_group_name = element(azurerm_resource_group.example, count.index).name

  administrator_login          = "mariadbadmin"
  administrator_login_password = "H@Sh1CoR3!"

  sku_name   = "B_Gen5_2"
  storage_mb = 5120
  version    = "10.2"

  auto_grow_enabled             = true
  backup_retention_days         = 7
  geo_redundant_backup_enabled  = false
  public_network_access_enabled = false
  ssl_enforcement_enabled       = true
}

The locals are used to create an array with the different locations. When creating the required resource group, the location is collected from the location array by using element(local.locations, count.index).

To distribute the databases over these locations the element function is again used to select the correct resource group.

element(azurerm_resource_group.example, count.index).location

This example shows you can both select a single item from an array but also other properties, in this case from the created resource groups.

When planning the example it will result in creating 9 resources:

3 resource groups:

  # azurerm_resource_group.example[0] will be created
  + resource "azurerm_resource_group" "example" {
      + id       = (known after apply)
      + location = "westeurope"
      + name     = "example-resources-1"
    }

  # azurerm_resource_group.example[1] will be created
  + resource "azurerm_resource_group" "example" {
      + id       = (known after apply)
      + location = "eastus"
      + name     = "example-resources-2"
    }

  # azurerm_resource_group.example[2] will be created
  + resource "azurerm_resource_group" "example" {
      + id       = (known after apply)
      + location = "westus"
      + name     = "example-resources-3"
    }

6 databases

  # azurerm_mariadb_server.example[0] will be created
  + resource "azurerm_mariadb_server" "example" {
      + location                      = "westeurope"
      + name                          = "example-mariadb-server-1"
    }

  # azurerm_mariadb_server.example[1] will be created
  + resource "azurerm_mariadb_server" "example" {
      + location                      = "eastus"
      + name                          = "example-mariadb-server-2"
    }

  # azurerm_mariadb_server.example[2] will be created
  + resource "azurerm_mariadb_server" "example" {
      + location                      = "westus"
      + name                          = "example-mariadb-server-3"
    }

  # azurerm_mariadb_server.example[3] will be created
  + resource "azurerm_mariadb_server" "example" {
      + location                      = "westeurope"
      + name                          = "example-mariadb-server-4"
    }

  # azurerm_mariadb_server.example[4] will be created
  + resource "azurerm_mariadb_server" "example" {
      + location                      = "eastus"
      + name                          = "example-mariadb-server-5"
    }

  # azurerm_mariadb_server.example[5] will be created
  + resource "azurerm_mariadb_server" "example" {
      + location                      = "westus"
      + name                          = "example-mariadb-server-6"
    }

Conclusion

Sometimes you end up in a situation where you will think, how are we going to do this? From my experience, I often encounter this, especially when you are not 100% familiar with the language and all the functions.

The element function is very powerful which can help you with selecting a single element or iterating through multiple options. As shown above, is a nice example to distribute those databases over the various locations and still using 1 definition. This shows the true power of the element function.

I hope this Terraform 101 tip was helpful and if you have any suggestions please let me know in the comments below.

Photo by Fakurian Design on Unsplash

Tags:
    terraform
    iac
    code
Ryan Ververs-Bijkerk
Written by Ryan Ververs-Bijkerk

Ryan is a self-employed technologist at GO-INIT who specializes in the EUC and code area. He primarily focuses on the user experience in centralized desktop environments.

Search

    Follow me

    Community