Understanding terraform count, for_each and for loop?



When you are working with collection variables inside Terrafrom then you must understand the concept of loops with count, loops with for each and `for loop, otherwise, it will be really hard to iterate over collections such list, map and set.

This article is particularly focused on understanding how the loops work. We will see the example of each concept in more details -

Table of Content

  1. Loops with count
  2. Loops with for_each
  3. for loop



1. Loops with count

As the name suggests we need to use count but to use the count first we need to declare collections inside our terraform file.

Let's create a collection variable of type list(string) -

1variable "user_names" {
2  description = "IAM usernames"
3  type        = list(string)
4  default     = ["user1", "user2", "user3"]
5}

Here is the pictorial representation of the above list variable -

terraform loop and for_each loop

In the above collection, we have created a list of type string which contains usernames and these usernames we are going to use for creating aws_iam_user.

The code snippet shows how we are going to iterate over the list(string) -

1resource "aws_iam_user" "example" {
2  count = length(var.user_names)
3  name  = var.user_names[count.index]
4}

terraform loop and for_each loop

Here is the complete terraform file -

 1provider "aws" {
 2   region     = "eu-central-1"
 3   access_key = "AKIATQ37NXB2OBQHAALW"
 4   secret_key = "ilKygurap8zSErv7jySTDi2796WGqMkEtN6txxxx"
 5}
 6resource "aws_instance" "ec2_example" {
 7
 8   ami           = "ami-0767046d1677be5a0"
 9   instance_type =  "t2.micro"
10   count = 1
11
12   tags = {
13           Name = "Terraform EC2"
14   }
15
16}
17
18resource "aws_iam_user" "example" {
19  count = length(var.user_names)
20  name  = var.user_names[count.index]
21}
22
23variable "user_names" {
24  description = "IAM usernames"
25  type        = list(string)
26  default     = ["user1", "user2", "user3"]
27}

Once you apply this terraform configuration using the terraform apply command, it will do the following on aws-

  1. Create one ec2 instance
  2. Create three IAM users - user1, user2, user3


2. Loops with for_each

The for_each is a little special in terraforming and you can not use it on any collection variable.

Note : - It can only be used on set(string) or map(string).

The reason why for_each does not work on list(string) is because a list can contain duplicate values but if you are using set(string) or map(string) then it does not support duplicate values.

Let's first create a set(string) variable -

1variable "user_names" {
2  description = "IAM usernames"
3  type        = set(string)
4  default     = ["user1", "user2", "user3s"]
5} 

Now let's iterate over the variable user_names.

1resource "aws_iam_user" "example" {
2  for_each = var.user_names
3  name  = each.value
4}

Here is the complete terraform file with implementation of for_each

 1provider "aws" {
 2   region     = "eu-central-1"
 3   access_key = "AKIATQ37NXB2NN3D4ARS"
 4   secret_key = "3v9mlwZQvmccL3ou1dxiDeEf1bWaG3kccpVlXXXX"
 5}
 6resource "aws_instance" "ec2_example" {
 7
 8   ami           = "ami-0767046d1677be5a0"
 9   instance_type =  "t2.micro"
10   count = 1
11
12   tags = {
13           Name = "Terraform EC2"
14   }
15
16}
17
18resource "aws_iam_user" "example" {
19  for_each = var.user_names
20  name  = each.value
21}
22
23variable "user_names" {
24  description = "IAM usernames"
25  type        = set(string)
26  default     = ["user1", "user2", "user3"]
27}

You can apply the above terraform configuration by running the command terraform apply.

Here is the output which you will notice after running the command -

 1# aws_iam_user.example["user1"] will be created
 2  + resource "aws_iam_user" "example" {
 3      + arn           = (known after apply)
 4      + force_destroy = false
 5      + id            = (known after apply)
 6      + name          = "user1"
 7      + path          = "/"
 8      + tags_all      = (known after apply)
 9      + unique_id     = (known after apply)
10    }
11
12  # aws_iam_user.example["user2"] will be created
13  + resource "aws_iam_user" "example" {
14      + arn           = (known after apply)
15      + force_destroy = false
16      + id            = (known after apply)
17      + name          = "user2"
18      + path          = "/"
19      + tags_all      = (known after apply)
20      + unique_id     = (known after apply)
21    }
22
23  # aws_iam_user.example["user3s"] will be created
24  + resource "aws_iam_user" "example" {
25      + arn           = (known after apply)
26      + force_destroy = false
27      + id            = (known after apply)
28      + name          = "user3s"
29      + path          = "/"
30      + tags_all      = (known after apply)
31      + unique_id     = (known after apply)
32    }


3. for loop

The for loop is pretty simple and if you have used any programming language before then I guess you will be pretty much familiar with the for loop.

Only the difference you will notice over here is the syntax in Terraform.

I am going to take the same example by declaring a list(string) and adding three users to it - user1, user2, user3

1variable "user_names" {
2  description = "IAM usernames"
3  type        = list(string)
4  default     = ["user1", "user2", "user3"]
5} 

You can use the above-declared variable inside your terraform file in a very simple way -

1output "print_the_names" {
2  value = [for name in var.user_names : name]
3}

You apply the above terraform configuration by running the command terraform apply. And if you see the logs where it will show how many users it is going to create -

 1# aws_iam_user.example["user1"] will be created
 2  + resource "aws_iam_user" "example" {
 3      + arn           = (known after apply)
 4      + force_destroy = false
 5      + id            = (known after apply)
 6      + name          = "user1"
 7      + path          = "/"
 8      + tags_all      = (known after apply)
 9      + unique_id     = (known after apply)
10    }
11
12  # aws_iam_user.example["user2"] will be created
13  + resource "aws_iam_user" "example" {
14      + arn           = (known after apply)
15      + force_destroy = false
16      + id            = (known after apply)
17      + name          = "user2"
18      + path          = "/"
19      + tags_all      = (known after apply)
20      + unique_id     = (known after apply)
21    }
22
23  # aws_iam_user.example["user3s"] will be created
24  + resource "aws_iam_user" "example" {
25      + arn           = (known after apply)
26      + force_destroy = false
27      + id            = (known after apply)
28      + name          = "user3s"
29      + path          = "/"
30      + tags_all      = (known after apply)
31      + unique_id     = (known after apply)
32    }


How to iterate over MAP?

We can use a similar approach to iterate over the map also. But always keep in mind you need to specify the type of the map like string or number.

Here is the same example which I have taken but modified a bit for map -

1variable "iam_users" {
2  description = "map"
3  type        = map(string)
4  default     = {
5    user1      = "normal user"
6    user2  = "admin user"
7    user3 = "root user"
8  }
9}

Now let's iterate over the map

1output "user_with_roles" {
2  value = [for name, role in var.iam_users : "${name} is the ${role}"]
3}


Here is the difference between list and map syntax

For list -

1{for <ITEM> in <LIST> : <OUTPUT_KEY> => <OUTPUT_VALUE>}

For Map -

1{for <KEY>, <VALUE> in <MAP> : <OUTPUT_KEY> => <OUTPUT_VALUE>} 

Read More - Terragrunt -

  1. How to use Terragrunt?

Posts in this Series