Terraform
Terraform
July 31, 2023

The 4 Most Useful Terraform Functions

By
Brendan Thompson

format

This is one of the most use functions for me as I tend to prefer using this over string interpolation, since Terraform v0.12+ string interprolation has gone out of fashion a bit and that is part of what caused me to move towards format initially, however now I love it because I can do more complex things with my formatting and maintain good readability by putting elements on new lines.

locals {
  first = "FIRST   "
  second = "SECOND"
}

resource "scratch_string" "with_format" {
  in = format(
    "Something interesting from the %s and then from the %s",
    trimspace(local.first), # Remove whitespace
    lower(local.second)     # Return in lowercase
  )
}

resource "scratch_string" "with_string_interpolation" {
  in = "Something interesting from the ${trimspace(local.first)} and then from the ${lower(local.second)}"
}

In the first resource we have you can clearly see what is going on, even without the comments using the format function. In the second example however we are doing the same thing with string interpolation, whilst it is still reasonably legible you can see how confusing this might get the longer the string and formatting requirements get.

When it comes to writing Terraform you should always have in "How easy will this be for other engineers to read and work on" in the forefront of your mind. Think back to the last time you were in a codebase that was hard to read.

split

The split function is useful in many ways, most especially when you're dealing with non-comma delimited strings. A good example of this is where you have a naming standard, think Azure resource names. We are going to look at a simple example where we want to retrieve the region property from an Azure resource name.

locals {
  resourceGroupName = "rg-aus-prd-meow"

  regions = {
    aus = "Australia Southeast"
    aue = "Australia East"
  }
}

We have our resourceGroupName property that has a hyphen (-) delimited string in it, we want to grab the second element -which is the region- then we would use split like below

output "shortRegion" {
  value = split("-", local.resourceGroupName)[1]
}

We can even go further and pull out a value from local.regions based on the string we get out of our split, that looks like:

output "fullRegion" {
  value = lookup(
    local.regions,
    split("-", local.resourceGroupName)[1]
  )
}

This simple ability to split strings is immensely powerful when working with named resources. I would have to say I use split more than any other function!

yamldecode

Personally I am a big proponent for config driven Terraform, and my configuration data type of choice has to be yaml it allows for good human and machine readability and can be validated in a similar way to JSON. Terraform provides us an excellent easy way to decode yaml into Terraform native usable objects. Lets have a look at decoding a yaml string.

locals {
  yaml = 
  first: <<EOF
    name: FIRST
  second:
    name: SECOND
EOF

  decoded = yamldecode(local.yaml)
}

As you can see above we have a string containing some yaml called local.yaml we now need to decode that into Terraform objects so we can easily manipulate the data. This is done with the yamldecode function and can be see on line 9. Now that we have the yaml decoded lets take a really quick look at how we can consume that data in a meaningful way.

resource "scratch_string" "yaml" {
  for_each = local.decoded

  in = format(
    "Property: %s, Name: %s",
    each.key,
    each.value.name
  )
}

In the above we are iterating over each of the keys in our local.decoded variable. And that is how yaml can be a powerful way to deal with configuration in Terraform. With the next function the above ca become even more powerful!

file

The final function we are going to look at today is the file function, this allows us to read in data from a file (or even a number of files with fileset) and then perform some actions on that data like we would with anything else. In this example we are going to look at reading in a yaml file and as that's how we like to do configuration around here!

locals {
  yamlFile = file("./example.yaml")
}

First off we are just going to look at it in its simplest form, wherein we are going to try and read in a file to a local variable. This is however thawte with danger, if the file doesn't exist Terraform will simply error out. In some instances this could be a good thing, however there is an alternative that we will look at now.

locals {
  decodedFile = (
    fileexists("./example.yaml") ?
    yamldecode(file("./example.yaml")) :
    {}
  )
}

In this example you can see we are checking to see if the file exists first, if it does we will attempt to load the file with file() and then decode it into our variable with yamldecode() if however the file does not exist then we simply return an empty object ({})

Closing Out

That's been the most useful functions offered by Terraform in my opinion. This opinion is based on the functions that I use most often or that provide me the most value when writing Terraform code. We looked at format for formatting strings in a clean and concise manner, split for carving up strings, yamldecode for ingesting yaml configuration into Terraform objects and finally the file function that allows us to read in any file type into Terraform. (N.B. just because you and read it doesn't mean you can use it!). I'd love to know what functions you find most useful when writing Terraform!

Get started using the functions in Scalr today!

Start using the Terraform platform of the future.

A screenshot of the modules page in the Scalr Platform