Terraform Best Practices: How to Leverage Locals, Count, and For_each

How to Use locals, count, and for_each to Simplify Your Infrastructure as Code

Terraform Best Practices: How to Leverage Locals, Count, and For_each

Prerequisite: To gain a better understanding, it is advisable to read this blog first: Click here before continuing with this one.

GitHub: Refer this repository.


Ever felt like Terraform configurations could be simpler or more flexible? Dive into the magic of the locals block and the meta-arguments count and for_each!


Locals Block:

I want to create two instances with Terraform but their name should be different. For this purpose we need to create Locals block. Add below code in our variables.tf file.

Note: All the previous code has been explained in earlier sections of the Terraform documentation, so please refer to those sections and the GitHub repository.

#varibales.tf
locals {
  instances = {
    #Key   : #Value
    "key"  : "my_instance_1"
    "key2" : "my_instacne_2"
  }
}

Now, if we want to use these local instance variables in our instance tags, how should we do it?

Solution: We will iterate over our locals and fetch their values one by one for the tags.

Apply these changes into our ec2.tf file within the existing code:

#Exisiting ec2.tf file

resource "aws_instance" "my_instance" {
  count           = var.aws_instance_count  #Meta Argument
#--------------------------------------------------------------#
  for_each        = local.instances          #Meta Argument
#--------------------------------------------------------------#
  ami             = data.aws_ami.os_image.id
  instance_type   = var.ec2_instance_type
  security_groups = [aws_security_group.my_sg.name] #Interpolation
  key_name        = aws_key_pair.my-key.key_name    #Interpolation
  root_block_device {
    volume_size = var.aws_root_volume_size
    volume_type = "gp3"
  }
  tags = {
#--------------------------------------------------------------#
    Name = each.value
#--------------------------------------------------------------#
  }
}

In this code, each.value represents the value from the instances map during each iteration of for_each. For example, if the map has {"key" = "my_instance_1"}, then each.value will be "my_instance_1". It's used here to dynamically set the Name tag for each AWS instance.

Lets try to plan the changes using terraform plan.

The error happens because you can’t use both count and for_each in the same resource because they’ are alternatives for creating multiple resources.

IMP Note: Using count, all instance names will be the same. However, by using for_each, we can assign different names to each instance and there values will be configurable.

We will comment out the count for now.

#ec2.tf
resource "aws_instance" "my_instance" {
  #count           = var.aws_instance_count  #Meta Argument
  for_each        = local.instances          #Meta Argument
  ami             = data.aws_ami.os_image.id
  instance_type   = var.ec2_instance_type
  security_groups = [aws_security_group.my_sg.name] #Interpolation
  key_name        = aws_key_pair.my-key.key_name    #Interpolation
  root_block_device {
    volume_size = var.aws_root_volume_size
    volume_type = "gp3"
  }
  tags = {
    Name = each.value
  }
}

Now we will run terraform plan again.

You will notice "Plan 5 to add," indicating that the instances are being created, but there is an error in outputs.tf.

We need to retrieve the values from the outputs block using for_each. Let's explore how to achieve this.

[ Documentation1 , Documentation2 ]

#outputs.tf

output "ec2_public_ip" {
  value = {
    for key, instance in aws_instance.my_instance : key => instance.public_ip
  }
}

This is the for loop:

  • aws_instance.my_instance refers to the EC2 instances created using for_each.

  • key is the identifier for each EC2 instance (like a name or tag).

  • instance contains the full details of the EC2 instance (like IP address, state, etc.).

  • key => instance.public_ip creates a key-value pair: the key is the instance identifier, and the value is the public_ip of that instance.

Run terraform plan again:

Run terraform apply, and you will see that it displays the public IPs of our instances as well.

You can observe in the AWS console that our two instances are running with different names.

This is how you can use locals block in Terraform.


Happy Learning :)

Chetan Mohod ✨

For more DevOps updates, you can follow me on LinkedIn.