Terraform Count

Terraform Count [Save your time by using it]

Terraform is excellent for describing your environment using code. However, copying and pasting the exact resource representation can be dull if you build multiple resources. Fortunately, Terraform gives two ways to handle similar resourcesĀ for eachĀ andĀ count. Later, you will be able to use it on Dynamic Block.

A module is known as a block of re-usable code in its most basic form. Confusingly, a module in Terraform isĀ technicallyĀ any set of templates in a folder. But, in my belief, it makes more sense to separate modules up into ā€œparentā€ and ā€œchild.ā€ The parent module describes infrastructure by passing variables to the children.

How to use Terraform Count

We useĀ countĀ to deploy multiple resources. TheĀ countĀ meta-argument generates numerous resources of a module. You use theĀ countĀ argument within the block and assign a whole number (could by variable or expression), and Terraform generates the number of resources. Each resource is its discrete object generated, refreshed, or destroyed when performing the ā€œterraform apply.ā€ TheĀ countĀ is exceptional for resources that share the same configurations.

WhileĀ countĀ has eternally worked with resources, Hashicorp (the Terraform owner) didn't append support for modules until Terraform v0.13.

Basic Syntax

TheĀ Terraform countĀ is a meta-argument determined by the Terraform language. The count can utilize it with modules and with all resource types.

Also, theĀ countĀ meta-argument supports a whole number moreover makes that many instances of the resource or module. Each instance has a different infrastructure object connected with it, and each is individually built, updated, or destroyed when the configuration is applied.

resource "aws_instance" "web-inst" {
  count = 2 # generate two similar EC2 instances
  ami           = "ami-0ed9277fb7eb570c9"
  instance_type = "t3.xlarge"
  tags = {
    Name = "Instance ${count.index}"
    Owner = "Bits Lovers"
  }
}

How to use expressions in the count

The count meta-argument admits numeric expressions. But, unlike most arguments, the count value must be definedĀ beforeĀ Terraform execute remote resource operations. This indicates count canā€™t point to any resource attributes that arenā€™t identified until after a configuration is implemented (like a unique ID created by the remote API during an object creation).

Terraform Count ā€“ Real Example 1

Letā€™s suppose that you need to create multiple Kubernetes secrets for each namespace in your cluster. So, to avoid making it one by one, you can receive an array ofĀ namespacesĀ that contains all namespaces from your cluster to create the secrets according to the array length.

resource "kubernetes_secret" "k8_secret" {
  count    = length(var.namespaces)
  provider = kubernetes
  metadata {
    name = "credentials"
    namespace = var.namespaces[count.index]
  }
  data = {
    username = "k8-user"
    password = var.SECRECT
  }
}

Terraform Count ā€“ Real Example 2

In our second case, itā€™s beneficial to useĀ countĀ to create multiple DNS entries when we have all DNS in a single array if you need to create them using the same configuration.

resource "aws_route53_record" "public-dns" {
  count           = length(var.public_dns)
  zone_id         = var.public_dns_zone_id
  name            = var.public_dns[count.index]
  type            = "CNAME"
  records         = [var.cname]
  ttl             = "90"
}

In this example, we received the array public_dns, containing all DNS we need to create. So, the length of this array will tell us how many DNS we need to make.Ā 

Easy, right? Letā€™s jump to our following real example.

Terraform Count ā€“ Real Example 3

Another practical scenario to useĀ countĀ is when we decide if we need to create a specific resource dynamically by changing one variable value or any other conditional.

In this example, we create one Network Load Balance only when theĀ lb_typeĀ variable equalsĀ ā€œnetwork-lb.ā€Ā So, if itā€™s equal, the count value will be 1. If not, it will be zero, which means we will not create that resource.

resource "aws_lb" "network-lb" {
  count = var.lb_type == "network-lb" ? 1 : 0
  name = "bitslovers-nlb"
  load_balancer_type = "network"
  subnets         = split(",", var.subnet_ids)
  idle_timeout    = 120
  internal        = var.enable_internal
  enable_deletion_protection = true
}

In cases like the above, we need to be aware of any resource that depends on that resource, and we need to use the same logic or somehow avoid any issue if this resource is not available.

Terraform Count Limitations

Sadly, the count has two restrictions that significantly decrease its utility. First, while you can apply count to loop across a whole resource, you canā€™t apply count inside a resource to circle across inline blocks. AnĀ inline-blockĀ is an argument you placed in a resource of the form:

resource "type" "name" {
  {
 [ARG...]
 }
}

WhereĀ TEXTĀ is the name of the inline-block (e.g., tag) andĀ ARGĀ consists of one or more arguments that are defined to that inline-block (like key and value). For instance, consider how our tags are set in the aws_autoscaling_group resource:

resource "aws_autoscaling_group" "example" {
  launch_configuration = aws_launch_configuration.bitslovers.name
  target_group_arns    = [aws_lb_target_group.as_g.arn]
  health_check_type    = "ELB"  
  min_size = var.min_size
  vpc_zone_identifier  = data.aws_subnet_ids.public.ids
  max_size = var.max_size  tag {
    key                 = "Owner"
    propagate_at_launch = true
    value               = var.env_name
  }
}

Here, we are using Terraform Data, check our post to learn mode.

So, each tag requires you to make a new inline-block with key, value, and propagate_at_launch values. The initial code hardcodes a single tag, but you may desire to permit users to pass in custom tags. You might be seduced to employ theĀ countĀ parameter to loop over these tags and render dynamic inline tag blocks. Unfortunately, usingĀ countĀ inside an inline block is not supported.

How to Deploy Multiple Resources using For Each

As we saw in the count argument, theĀ for_eachĀ meta-argument generates multiple module or resource block instances. But this time, instead of determining the number of resources, theĀ for_eachĀ argument assumes in a map or set of strings. The number of resources Terraform makes it depends on the number of input objects on this map.

For instance, letā€™s how we can make the same to create IAM users usingĀ for_each:

resource "aws_iam_user" "users" {
  for_each = toset(var.list_users)
  name     = each.value
}

Look for the use ofĀ tosetĀ to transform theĀ var.list_usersĀ list into aĀ set, asĀ for_eachĀ only enables sets and maps when utilized on a resource. WhenĀ for_eachĀ loops over this set, it will create each user name known in each.value. The user name will also be understood in each key, though you usually only utilize each.key with maps of key/value pairs.

When you useĀ for_eachĀ on a resource, it becomes a map of resources, rather than just one resource (or a set of resources asĀ count).Ā 

Notes on Terraform Count and For Each

Before you decide which one it should be used, there are a couple of ideas to keep in mind when working withĀ for eachĀ and TerraformĀ countĀ and for each:

  • You cannot useĀ countĀ andĀ for_eachĀ simultaneously in the same module or resource block.
  • When usingĀ for_each, theĀ keysĀ of theĀ mapĀ must be known values, and sensitivity values are not permitted as arguments.
  • When resources are almost similar, useĀ count.
  • If some arguments demand different values that cannot be derived from theĀ countĀ integer, utilizeĀ for_eachĀ instead.

Terraform also enables us to use very identical functionality in aĀ for expressionĀ (not to be mistaken with theĀ for_each). The fundamental syntax of aĀ forĀ presentation is:

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

TheĀ LISTĀ is a list to loop over, and theĀ ITEMĀ represents the local variable name to allocate to an individual item inĀ LIST. Finally, theĀ OUTPUTĀ represents an expression that changesĀ ITEMĀ somehow. For instance, here is the Terraform code to convert the list of names:

variable "users" {
  description = "A list of users"
  type        = list(string)
  default     = ["bits", "lovers", ".com"]
}
output "upper_names" {
  value = [for user in var.users : upper(user)]
}

Conclusion

Employing theĀ Terraform countĀ andĀ for_eachĀ technique drives your Terraform configurations more dynamic. Also, it can simplify your code and avoid code duplication.

Boost your Terraform Skills:

How and When to use Terraform Modules?

Do you know what is Terraform Data and How to use it?

Learn How to use Output on your Terraform and share data across multiple configurations.

Create multiple copies of the same resource using Terraform Count.

Learn How to use Terraform Template, to build dynamic configurations.

Do you know the difference between Locals and Do you know what is Terraform Data and How to use it?

Execute Terraform on Gitlab CI.

2 thoughts on ā€œTerraform Count [Save your time by using it]ā€

  1. how to delete/destroy one of the instances of a resource without the count index??
    for example, i have created 3 buckets & i want to destroy 2nd bucket without the index value of the bucket i.e bucket[1]. so how can i destroy the bucket with the bucket name??

    1. Hello,
      If you created the S3 bucket using count, it means that you are using a List/Array on your Terraform to specify the 3 bucketsā€™ names at the same variable. So, you have to remove the bucket that you want from the Array/List and execute the Terraform again, to destroy the Bucket for you. Remember that, to make it work, the S3 bucket must be empty.

      Thanks

Leave a Comment

Your email address will not be published.