Terraform Maps
Intro
I’ve been doing a lot with terraform lately, and I’ve been looking for ways to make my terraform configurations a lot simpler and have less repetition. Like a lot of people, I’ve found myself repeating the same code over and over. An example is where I repeat the same resource over and over but with different configuration parameters. It’s essentially the same resource. Why should I do this? There has to be a better way.
What is a map?
In looking for a way to make my codebase cleaner and simpler, I started looking at using maps. I’ve not delved into them in depth before, so it was a fun thing to do on a rainy afternoon.
The terraform docs do a better job than I can of telling you what a map is - see here
Why are maps cool?
Maps are cool because they allow you to have groups of key value pairs that can be accessed in a neat way.
A simple example
A very simple example of a map is as follows:
variable "tcp_lb" {
type = map
default = {
unit-config-origin = "8888"
unit-git-origin = "8080"
}
}
You will notice that the map above is actually a variable. That’s right you can use a map as a variable.
In this case, I am using a map to assign different ports to origin servers within a volterra resource. In this way, I don’t need to declare the same resource over and over, I can use a loop within my resource to access all of the items within my map.
The resource
The resource that uses the map above looks like this:
resource "volterra_tcp_loadbalancer" "unit-config" {
for_each = var.tcp_lb
name = "${each.key}"
namespace = var.ns
listen_port = "${each.value}"
dns_volterra_managed = true
domains = ["${var.domain_host}.${var.domain}"]
advertise_on_public_default_vip = true
retract_cluster = true
origin_pools_weights {
pool {
name = "${each.key}"
namespace = var.ns
}
}
hash_policy_choice_round_robin = true
}
There are two pieces to this resource that I need to point out.
- The for_each loop
- Accessing map keys and values
The for_each loop
The for_each loop is used to loop through the map within the context of the resource. The for_each loop in terraofrm is documented here. The interesting thing is that a for_each loop can accept a map as an input!
for_each = var.tcp_lb
Note that the for_each loop references the variable that I defined above. The variable is actually a map, which the for_each loop can accept as an input. The for_each loop will loop through each key within the variable tcp_lb. In my case, the variable has two values. The for_each loop will run twice as it iterates over my map variable.
Accessing map keys and values
The map that I have defined has two keys and two values. Each key and each value can be acess separately within the context of the for_each loop.
In order to access each of the map values or names, I can use the following syntax:
name = "${each.key}"
and
listen_port = "${each.value}"
The each.key and each.value keywords are used to access either the key or the value within the map.
In my case, on each iteration, the following will be true:
variable "tcp_lb" {
type = map
default = {
unit-config-origin = "8888"
unit-git-origin = "8080"
}
}
One first iteration:
each.key = unit-config-origin
each.value = 8888
On the second iteration:
each.key = unit-git-origin
each.value = 8080
Conclusion
Using terraform maps simplifies your code by reducing the number of resources that you need to duplicate. It also makes your code a lot more readable.
There are traps and pitfalls using this method if you have dependant resources, but I’ll cover this in another post. :)