The Ops Community

Cover image for Let's Terraform IT: Pt 4 -> Count (index), Conditional Expression, Local Values
Jatin Mehrotra
Jatin Mehrotra

Posted on

Let's Terraform IT: Pt 4 -> Count (index), Conditional Expression, Local Values

By this time we know how to write a terraform code for simple infrastructure, but in reality, things are never easy, business needs are everchanging leading to complex infrastructure.

In this blog we will try to build our knowledge on features offered by terraform which helps to transform complex code into simple code and remove redundancy.

From now on, in this series, I want to introduce the topic with a use case because I believe that understanding a concept is one thing but knowing how to apply it in the real world makes the actual difference.

Count and Count Index

Use Case

Until now, we have been creating single resources for example an ec2 instance or an IAM user, but in production-grade applications, there are multiple resources, what would be an ideal way to create multiple resources of the same type? for instance multiple ec2 instances!!!

  • One approach would be to write 3 identical ec2 resource blocks with different local resource names.
provider "aws" {
  region     = "us-west-2"
  access_key = "your-access-key"
  secret_key = "your-secret-key"
}

resource "aws_instance" "instance1" {

  ami           = "ami-0ca285d4c2cda3300"
  instance_type = "t2.micro"
}

resource "aws_instance" "instance2" {

  ami           = "ami-0ca285d4c2cda3300"
  instance_type = "t2.micro"

}
Enter fullscreen mode Exit fullscreen mode
  • This approach is not scalable if we are needed to create 40 ec2 instances, which will result in 40 ec2 resource blocks, updating such blocks would be prone to errors and difficult.

  • Terraform provides a count parameter which allows creating many resources using a single resource block, hence easier updation.

  • The following code allows us to create 5 ec2 instances using the count parameter.

resource "aws_instance" "instance" {

  ami           = "ami-0ca285d4c2cda3300"
  instance_type = "t2.micro"
  count         = 5
}
Enter fullscreen mode Exit fullscreen mode

resource creating using count

Count Index

Use Case

Count parameter solved our problem but it also brings its own set of problems with it. We want to create 5 IAM users, an ideal approach to this problem would be to use the count parameter, however using count would result in 5 iam users with the same name, hence it is difficult to identify resources when the count is used.

resource "aws_iam_user" "users" {
  name  = "iam_users"
  count = 3
  path  = "/system/"
}
Enter fullscreen mode Exit fullscreen mode
  • this result in 3 iam users with the same name iam_users leading to difficulty in the identification of resources

difficult indentification using count

  • terraform provides a count.index value which behind the scenes, access count parameter (count runs a loop) index value to identify the loop cycle. This helps in generating unique values with count parameters as count.index will always be unique for a particular loop cycle ( for example if the loop is running the first time it will be 1 and so on)
resource "aws_iam_user" "users" {
  name  = "iam_users.${count.index}"
  count = 2
  path  = "/system/"
}
Enter fullscreen mode Exit fullscreen mode
  • This results in 3 iam users with the name iam_users.1, iam_user.2.

easy identification using count index

  • count.index with count can be combined together using array data type.
variable "names" {
  type    = list(any)
  default = ["admin", "dev"]
}

resource "aws_iam_user" "users" {
  name  = var.names[count.index]
  count = 2
  path  = "/system/"
}

Enter fullscreen mode Exit fullscreen mode
  • This results in 2 iam users with the name admin, dev.

users with name using count.index

Behind the scenes with Count and Count Index

  • Count internally runs a loop and the count index maps the count of the particular loop.

Conditional Expression

Use Case

We need to launch an ec2 instance for the production if a particular variable exists else for the development.

  • Conditional expression ? : helps us to select a value depending on the condition.
 count         = var.istest ? 1 : 0
Enter fullscreen mode Exit fullscreen mode
  • If is isTest variable is true, the count will be 1 otherwise 0.
resource "aws_instance" "dev" {

  ami           = "ami-0ca285d4c2cda3300"
  instance_type = "t2.micro"
  count         = var.istest ? 1 : 0
}

resource "aws_instance" "prod" {

  ami           = "ami-0ca285d4c2cda3300"
  instance_type = "t2.large"
  count         = var.istest ? 0 : 1
}

variable "istest" {
  type    = bool
  default = true
}
Enter fullscreen mode Exit fullscreen mode
  • This code block results in the creation of t2.micro instance if istest = true else t2.large instance for production if istest = false ## Locals

Use Case

We need to same name tags for 2 ec2 instances and ebs volume. One approach would be to write tags in 2 resource blocks for ec2 and in one resource block for ebs volume. This will work however, this will lead to writing code which is repetitive and if in the future the name tag needs to be changed it needs to be replaced in 3 different places. Quite inconvenient!!!!

An ideal way would be to define it in one place and reference it in all the places.

  • Locals assign a name to an expression or value and allow it to be used in multiple places.
provider "aws" {
  region     = "us-west-2"
  access_key = "your-access-key"
  secret_key = "your-secret-key"
}
locals {
  common_tags = {
    Owner   = "DevOps Team"
    service = "backend"
  }
}
resource "aws_instance" "dev" {
  ami           = "ami-082b5a644766e0e6f"
  instance_type = "t2.micro"
  tags          = local.common_tags
}

resource "aws_instance" "db-dev" {
  ami           = "ami-082b5a644766e0e6f"
  instance_type = "t2.small"
  tags          = local.common_tags
}

resource "aws_ebs_volume" "db_ebs" {
  availability_zone = "us-west-2a"
  size              = 8
  tags              = local.common_tags
}

Enter fullscreen mode Exit fullscreen mode

common tags using local

  • This code allows us to use the same tags across all resources without repeating the same code in all the resources.

Bonus Accessing data from maps and list in the variable

variable "types" {
  type    = list(any)
  default = ["t2.micro", "t2.large"]
}


resource "aws_instance" "myFirstEc2Instance" {

  ami           = "ami-0ca285d4c2cda3300"
  instance_type = var.instance[0]
}
Enter fullscreen mode Exit fullscreen mode
  • list values start with index 0 which means instance type t2.micro will be selected.
provider "aws" {
  region     = "us-west-2"
  access_key = "your-access-key"
  secret_key = "your-secret-key"
}

resource "aws_instance" "myFirstEc2Instance" {

  ami           = "ami-0ca285d4c2cda3300"
  instance_type = var.instance["delhi"]
}


variable "instance" {
  type = map(any)
  default = {
    "mumbai" = "t2.large"
    "delhi"  = "t2.micro"
  }
}
Enter fullscreen mode Exit fullscreen mode
  • To access the map data type value of the key is used to access its value within []

From a DevOps perspective

  • Count, conditional, and local values are the tools which help to write clean and non-repetitive code, which helps to scale easily when the requirements are changing.

Discussion (0)