
formatThis is one of the functions I reach for most. I tend to prefer it over string interpolation. Since Terraform v0.12+, string interpolation has gone out of fashion a bit, and that is part of what pushed me towards format to begin with. Now I love it because I can do more complex formatting and keep things readable by putting elements on their own 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 you write Terraform, keep one question in mind: how easy will this be for other engineers to read and work on? Think back to the last time you were stuck in a codebase that was hard to read.
splitThe split function is useful in a lot of situations, especially when you're dealing with strings that aren't comma delimited. A good example is a naming standard, like Azure resource names. We are going to look at a simple case: pulling the region out of 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!
yamldecodePersonally, I am a big proponent of config-driven Terraform, and my configuration format of choice is yaml. It reads well for both humans and machines, and it can be validated in a similar way to JSON. Terraform gives us an easy way to decode yaml into native objects. Let's 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 let's 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. That is how yaml gives you a clean way to handle configuration in Terraform. The next function takes this even further.
fileThe last function we are going to look at today is file. It reads data in from a file (or several files, with fileset) so you can work with that data like anything else. In this example we are reading in a yaml file, since 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 fraught 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 ({})
Those are the Terraform functions I find most useful, based on what I reach for most often and what saves me the most trouble. We looked at format for building readable strings, split for carving strings apart, yamldecode for pulling yaml configuration into Terraform objects, and file for reading any file type into Terraform. (N.B. just because you can read a file doesn't mean you can use what's in it!) I'd love to know which functions you find most useful when writing Terraform.
Get started using the functions in Scalr today!
