Mastering Loops in Terraform

✨This article, will explain in detail how to implement loops and variables in your Terraform code to create dynamic and efficient infrastructure deployments. ✨

❄️Synopsis:

🌿 Understand how loops can make your Terraform configurations more dynamic and flexible.


❄️Loops:

🌿 Terraform provides a few primitives namely, the meta-parameter count, for_each and for expressions, a ternary operator, a lifecycle block called create_before_destroy, and a large number of functions - that allow you to do certain types of loops, if-statements, and zero-downtime deployments.

  • count parameter, to loop over resources and modules

  • for_each expressions, to loop over resources, inline blocks within a resource, and modules

  • for expressions, to loop over lists and maps

  • for string directive, to loop over lists and maps within a string


❄️Loops with the count Parameter:

🌿Created an AWS Identity and Access Management (IAM) user by clicking around the Console. Now that you have this user, you can create and manage all future IAM users with Terraform.

For a single IAM user

 provider "aws" {
  region = "us-east-2"
 }
 resource "aws_iam_user" "example" {
  name = "neo"
 }

🌿However, every Terraform resource has a meta-parameter you can use called count. count is Terraform’s oldest, simplest, and most limited iteration construct: all it does is define how many copies of the resource to create. Here’s how you use count to create three IAM users:

variable "user_names" {
  description = "Create IAM users with these names"
  type = list(string)       
  default = ["neo", "trinity", "morpheus"]    
}

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

output "all_arns" {
  value = aws_iam_user.example[*].arn      
  description = "The ARNs for all users"
 }
💡
You can’t use count within a resource to loop over inline blocks

❄️Loops with for_each Expressions:

🌿The for_each expression allows you to loop over lists, sets, and maps to create

  1. Multiple copies of an entire resource

  2. Multiple copies of an inline block within a resource

  3. Multiple copies of a module

Multiple copies of an entire resource

resource "aws_iam_user" "example" {
  for_each = toset(var.user_names)
  name = each.value    
 }

🌿Note the use of toset to convert the var.user_names list into a set. This is because for_each supports sets and maps only when used on a resource. When for_each loops over this set, it makes each username available in each. value. The username will also be available in each.key, though you typically use each.key only with maps of key-value pairs

 output "all_arns" {
  value = values(aws_iam_user.example)[*].arn
 }

Multiple copies of a module

module "users" {
  source = "../../../modules/landing-zone/iam-user"
  for_each  = toset(var.user_names)
  user_name = each.value
 }

 output "user_arns" {
  value = values(module.users)[*].user_arn       
  description = "The ARNs of the created IAM users"
 }

🌿Ability to create multiple inline blocks within a resource. For example, you can use for_each to dynamically generate tag inline blocks for the ASG in the webserver-cluster module.

Multiple copies of an inline block within a resource

 variable "custom_tags" {
  description = "Custom tags to set on the Instances in the ASG"
  type= map(string)        
  default= {}     
 }

module "webserver_cluster" {
  source = "../../../../modules/services/webserver-cluster"
  cluster_name = "webservers-prod"          
  db_remote_state_bucket = "(YOUR_BUCKET_NAME)"
  db_remote_state_key = "prod/data-stores/mysql/terraform.tfstate"   
  instance_type= "m4.large"        
  min_size  = 2            
  max_size = 10             
  custom_tags = {
    Owner  = "team-foo"    
    ManagedBy = "terraform"
  }
 }
 resource "aws_autoscaling_group" "example" {
  launch_configuration = aws_launch_configuration.example.name
  vpc_zone_identifier  = data.aws_subnets.default.ids
  target_group_arns = [aws_lb_target_group.asg.arn]   
  health_check_type= "ELB"    

  min_size = var.min_size
  max_size = var.max_size
 tag {
    key  = "Name"               
    value  = var.cluster_name              
    propagate_at_launch = true
  }
 dynamic "tag" {
    for_each = var.custom_tags
 content {
      key= tag.key                 
      value  = tag.value              
      propagate_at_launch = true
    }
  }
 }

❄️Loops with for Expressions:

🌿for expressions are used to transform and filter lists and maps in Terraform.

 variable "names" {
  description = "A list of names"
  type= list(string)       
  default = ["neo", "trinity", "morpheus"]    
}

How could you convert all of these names to uppercase?

💡
syntax: [for <ITEM> in <LIST> : <OUTPUT>]

🌿where LIST is a list to loop over, ITEM is the local variable name to assign to each item in LIST, and OUTPUT is an expression that transforms ITEM in some way.

output "upper_names" {
  value = [for name in var.names : upper(name)]
 }

❄️Loops with the for String Directive:

🌿String directives allow you to use control statements (e.g., for-loops and if-statements) within strings using a syntax similar to string interpolations, but instead of a dollar sign and curly braces (${…}), you use a percent sign and curly braces (%{…}).

Terraform supports two types of string directives: for-loops and conditionals.

💡
syntax: %{ for <ITEM> in <COLLECTION> }<BODY>%{ endfor }

🌿where COLLECTION is a list or map to loop over, ITEM is the local variable name to assign to each item in COLLECTION, and BODY is what to render each iteration (which can reference ITEM)

variable "names" {
  description = "Names to render"
  type = list(string)       
  default  = ["neo", "trinity", "morpheus"]     
}

 output "for_directive" {
  value = "%{ for name in var.names }${name}, %{ endfor }"
 }

🕵🏻I also want to express that your feedback is always welcome. As I strive to provide accurate information and insights, I acknowledge that there’s always room for improvement. If you notice any mistakes or have suggestions for enhancement, I sincerely invite you to share them with me.

🤩 Thanks for being patient and following me. Keep supporting 🙏

Clap👏 if you liked the blog.

For more exercises — please follow me below ✅!

https://vjraghavanv.hashnode.dev/

#aws #terraform #cloudcomputing #IaC #DevOps #tools #operations #30daytfchallenge #HUG #hashicorp #HUGYDE #IaC #developers #awsugmdu #awsugncr #automatewithraghavan

1
Subscribe to my newsletter

Read articles from vijayaraghavan vashudevan directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

vijayaraghavan vashudevan
vijayaraghavan vashudevan

I'm Vijay, a seasoned professional with over 13 years of expertise. Currently, I work as a Quality Automation Specialist at NatWest Group. In addition to my employment, I am an "AWS Community Builder" in the Serverless Category and have served as a volunteer in AWS UG NCR Delhi and AWS UG MDU, a Pynt Ambassador (Pynt is an API Security Testing tool), and a Browserstack Champion. Actively share my knowledge and thoughts on a variety of topics, including AWS, DevOps, and testing, via blog posts on platforms such as dev.to and Medium. I always like participating in intriguing discussions and actively contributing to the community as a speaker at various events. This amazing experience provides me joy and fulfillment! 🙂