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 ({}
)
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!