In this blog, we are going to learn about how to use FOR loops in terraform.
INTRODUCTION
As we know, Terraform is a declarative language.
Infrastructure-as-code in a declarative language tends to provide a more accurate about deployed items.
It is easier to reason about and makes it easier to keep the codebase small.
However, without access to a full programming language, certain types of tasks become more difficult in a declarative language.
For example, since declarative languages typically don’t have for-loops, how do we repeat a piece of logic — such as creating multiple similar EC2 Instances — without copy and paste?
And if the declarative language doesn’t support if-statements, how can we conditionally configure resources, such as creating public IP addresses for frontend services, but not for backend services?
Fortunately, Terraform provides a few primitives—namely, the count meta-parameter, for_each and for expressions.
A lifecycle block called create_before_destroy, a ternary operator, plus a large number of functions—that allow us to do certain types of loops, if-statements, and other logic.
Here are the topics we’ll go over:
Loops
Loops with for_each expressions
The for_each expression allows you to loop over lists, sets and maps to create either multiple copies of an entire resource.
It also allows us to create multiple copies of an inline-block within a resource.
Let’s first walk through how to use for_each to create multiple copies of a resource.
all_users output variable contains a map where the keys are the keys in for_each (in this case, the user names) and the values are all the outputs for that resource.
If we want to bring back the all_arns output variable, we have to do a little extra work to extract those ARNs using the values built-in function (which returns just the values from a map) and a splat expression:
output "all_arns" {
value = values(aws_iam_user.example)[*].arn
}
The fact that we now have a map of resources with for_each rather than a list of resources as with count is a big deal, as it allows you to remove items from the middle of a collection safely.
For Each with mutiple line blocks
Let’s now turn our attention to another advantage of for_each:
Its ability to create multiple inline blocks within a resource.
For example, we can use for_each to dynamically generate tag inline blocks for the ASG in the webserver-cluster module.
First, to allow users to specify custom tags, add a new map input variable called custom_tags:
variable "custom_tags" {
description = "Custom tags to set on the Instances in the ASG"
type = map(string)
default = {}
}
How do we actually set these tags on the aws_autoscaling_group resource?
What we need is a for loop over var.custom_tags, similar to the following pseudo code:
resource "aws_autoscaling_group" "example" {
# (...)
# This is just pseudo code. It won't actually work in Terraform.
for (tag in var.custom_tags) {
tag {
key = tag.key
value = tag.value
propagate_at_launch = true
}
}
}
The pseudo code above won’t work, but a for_each expression will.
The syntax for using for_each to dynamically generate inline blocks looks like this:
where VAR_NAME is the name to use for the variable that will store the value each “iteration” (instead of each).
COLLECTION is a list or map to iterate over, and the content block is what to generate from each iteration.
We can use <VAR_NAME>.key and <VAR_NAME>.value within the content block to access the key and value, respectively, of the current item in the COLLECTION.
Note that when we are using for_each with a list, the key will be the index and the value will be the item in the list at that index,
And when using for_each with a map, the key and value will be one of the key-value pairs in the map.
Putting this all together, here is how we can dynamically generate tag blocks using for_each in the aws_autoscaling_group resource:
Am a technology enthusiast having 3+ years of experience. I have worked on Core Java, Apache Flink, Apache Beam, AWS, GCP, Kafka, Spark, MySQL. I am curious about learning new technologies.