Terraform — conditional resources based on a map key

Jacek Kikiewicz
3 min readSep 11, 2020

--

Why?

So first question is… why would you want that? What for? The short answer is to DRY (Don’t Repeat Yourself). In terraform (especially in older versions) there were many times where repeating yourself was the only option (or the only easy option). I on the other hand am trying to avoid whenever I can (and now with terraform 12 / 13 that means pretty much always).

Ok, but in which case would I need this?

Given I work with Google Cloud, I will use GCP examples here. Imagine a case that you have a few Cloud Functions in your project and in order not to repeat yourself you’d like to create them in a loop. Naturally those Cloud functions aren’t the same, they will run different code, probably have different memory settings — those things are easy, you can configure a map that describes them and use for_each on resource itself, something along these lines:

variable "cloud_function_configs" {
default = {
"function_1" = {
"memory" = 128
"description" = "my function 1, does something"
}
"function_2" = {
"memory" = 256
"description" = "my function 2, does something"
}
"function_3" = {
"memory" = 512
"description" = "my function 3, does something"
}
"function_4" = {
"memory" = 1024
"description" = "my function 4, does something"
}
}
}
resource "google_cloudfunctions_function" "my_functions" {
for_each = var.cloud_function_configs
entry_point = each.key
name = each.key
description = each.value["description"]
runtime = "python37

available_memory_mb = each.value["memory"]
source_archive_bucket = "some_bucket_name"
source_archive_object = "some_file_in_bucket"
trigger_http = true
}

So what is going in above example:
- There is a variable of type map with keys that are used as function names and entry points
- Each object in map has a different memory as well as description, these get applied accordingly so function 1 will have 128mb ram, function 2 256mb and so on.
EASY! :)

Now, let’s get to slightly more complicated stuff, so let’s say we want to extend this (again DRY!). Let’s say we want some of the functions public (as in anyone on the internet can trigger them) and some not. In GCP (I am not sure how does that work for lambdas @ AWS) this requires a separate resource in TF:

resource "google_cloudfunctions_function_iam_member" "my_public_functions" {
cloud_function = google_cloudfunctions_function.my_functions.name
role = "roles/cloudfunctions.invoker"
member = "allUsers"
}

So we need to assign above resource to any function to make it public… but now thing is, this is a bit more complex if we want to do this by just adding a parameter in our config map:

variable "cloud_function_configs" {
default = {
"function_1" = {
"memory" = 128
"description" = "my function 1, does something"
}
"function_2" = {
"memory" = 256
"description" = "my function 2, does something"
"public" = true
}
"function_3" = {
"memory" = 512
"description" = "my function 3, does something"
}
"function_4" = {
"memory" = 1024
"description" = "my function 4, does something"
"public" = true
}
}
}

So to achieve this, we have introduce a for_each to “google_cloudfunctions_function_iam_member” resource that will only return something if public = true

resource "google_cloudfunctions_function_iam_member" "my_public_functions" {
for_each = {
for k in keys(var.cloud_function_configs) :
k => var.cloud_function_configs[k]
if lookup(var.cloud_function_configs[k], "public", "false")
}
cloud_function = google_cloudfunctions_function.zrh_imports[each.key].name
role = "roles/cloudfunctions.invoker"
member = "allUsers"
}

So above will do just that! :)
This at first (and a few there after) glance looks a bit complicated (or not a bit), so what happens above is:
- as for_each we give a for loop that loops through var.cloud_function_configs
-
in this loop we create a map which is basically a copy of original with exception that only keys / objects where public = true are included and returned (and others are not as ‘default’ value for lookup function is “false”)

This way we will end up with 4 Cloud Functions and 2 Iam Memberships for the public ones.

If there are questions, let me know in the comments!

Also, have a look at my other Terraform articles on medium!

--

--