Skip to main content

Command Palette

Search for a command to run...

Terraform meta-arguments (Part 4)

Updated
Terraform meta-arguments (Part 4)
H

Enthusiastic about DevOps tools like Docker, Kubernetes, Maven, Nagios, Chef, and Ansible and currently learning and gaining experience by doing some hands-on projects on these tools. Also, started learning about AWS and GCP (Cloud Computing Platforms).

Meta-arguments in Terraform are special arguments that can be used with resource blocks and modules to control their behavior or influence the infrastructure provisioning process. They provide additional configuration options beyond the regular resource-specific arguments.

Meta Arguments

Count: Controls resource instantiation by setting the number of instances created based on a given condition or variable.

for-each: Allows creating multiple instances of a resource based on a map or set of strings. Each instance is created with its unique key-value

depends on: Specifies dependencies between resources. It ensures that one resource is created or updated before another resource.

lifecycle: Defines lifecycle rules for managing resource updates, replacements, and deletions.

Count

Let’s first create a recap through creating 4 instances in AWS

Create a folder

mkdir terraform-meta
cd terraform-meta

Now write the HCL file for the provisioning

# vi main.tf

terraform {
    required_providers {
        aws = {
            source = "hashicorp/aws"
            version = "~> 4.16"
}
}
    required_version = ">= 1.2.0"
}

provider "aws" {
    region = "ap-south-1"
}

resource "aws_instance" "terraform" {
    count = 4
    ami = "ami-0dee22c13ea7a9a67"
    instance_type = "t2.micro"
    tags = {
        Name = "terraform-instance"
    } 
}

But if we apply this code this create 4 instances with same name which is a problem between developers and tester. To solve this problem let’s use count index.

# vi main.tf

resource "aws_instance" "terraform" {
    count = 4
    ami = "ami-0dee22c13ea7a9a67"
    instance_type = "t2.micro"
    tags = {
        Name = "terraform-instance-${count.index}"
    } 
}

In order to connect to your AWS account and terraform, you need the access keys and secret access keys exported to your machine.

export AWS_ACCESS_KEY_ID=<access key>
export AWS_SECRET_ACCESS_KEY=<secret access key>

Terraform init

Terraform plan

terraform plan

Terraform apply

You can use this de-referencing variable in between a string also like

# vi main.tf
tags = {
        Name = "terraform-${count.index}-instance"
    }

For-Each

Now if someone say to you that I will provide you a list of names and you have to use them to name the instances, then you can use for-each to get multiple de-referencing.

# vi main.tf

locals {   # we have to declare it as a local variable
    instance = toset(["Harshit","Aakib","Vijay","Rishabh"])
}

resource "aws_instance" "terraform" {
    for_each = local.instance
    ami = "ami-0dee22c13ea7a9a67"
    instance_type = "t2.micro"
    tags = {
        Name = each.key
    } 
}
Why use toset in locals?
Many of you might think that why we use toset function, basically a list is collection of same data elements, so in list values can be replicate also and for_each works only on unique values that’s why we converted it in set.
Terraform Locals
Terraform Locals are named values which can be assigned and used in your code. It mainly serves the purpose of reducing duplication within the Terraform code. When you use Locals in the code, since you are reducing duplication of the same value, you also increase the readability of the code.

Terraform apply

Terraform Locals vs Variables

How does Terraform local differ from a Terraform variable?

The first difference can be pointed towards the scope. A Local is only accessible within the local module vs a Terraform variable, which can be scoped globally.

Another thing to note is that a local in Terraform doesn’t change its value once assigned. A variable value can be manipulated via expressions. This makes it easier to assign expression outputs to locals and use that throughout the code instead of using the expression itself at multiple places.

MAP IN LOCALS
Now if someone challenges us to make different name instances with different AMI using a single resource only. Then we must accept his challenge because we can do this using map data structure in locals.

# vi main.tf

locals {   # we have to declare it as a local variable
    instance = {"Harshit":"ami-0aebec83a182ea7ea","Aakib":"ami-0dee22c13ea7a9a67","Vijay":"ami-0aebec83a182ea7ea","Rishabh":"ami-0dee22c13ea7a9a67"}
}

resource "aws_instance" "terraform" {
    for_each = local.instance
    ami = each.value
    instance_type = "t2.micro"
    tags = {
        Name = each.key
    } 
}

Terraform plan

terraform plan

Terraform apply

terraform apply --auto-approve

Depends_on

When a resource is dependent on other than we generally use this via depends_on= <resource-name>.

Using Terraform for_each to create subnets in AWS VPC

The two main drawbacks of using count are :
- Can’t be used to loop over inline blocks
- Difficult to remove entry from a list because it changes the index and those Terraform may want to destroy the resource because it has a different index

Below is an example of the variables used to create subnets within AWS VPCs and the main file with the for_each. The variables contain a map of subnets maps with cidr and az (availability zone) attributes. The for_each loop over the map of subnets maps to create the subnets.

variables.tf

variable "tag_name" {
   default = "main-vpc"
}

variable "vpc-cidr" {
   default = "10.0.0.0/16"
}

variable "basename" {
   description = "Prefix used for all resources names"
   default = "nbo"
}

#map of maps for create subnets
variable "prefix" {
   type = map
   default = {
      sub-1 = {
         az = "use2-az1"
         cidr = "10.0.198.0/24"
      }
      sub-2 = {
         az = "use2-az2"
         cidr = "10.0.199.0/24"
      }
      sub-3 = {
         az = "use2-az3"
         cidr = "10.0.200.0/24"
      }
   }
}

main.tf

resource "aws_vpc" "main-vpc" {
  cidr_block = var.vpc-cidr

  tags = {
    Name = var.tag_name
  }
}

resource "aws_subnet" "main-subnet" {
  for_each = var.prefix

  availability_zone_id = each.value["az"]
  cidr_block = each.value["cidr"]
  vpc_id     = aws_vpc.main-vpc.id

  tags = {
    Name = "${var.basename}-subnet-${each.key}"
  }
}

You can find the output of the terraform plan/apply, the terraform.state and the others tf files in the below links.

Terraform: Infrastructure As Code

Part 4 of 6

Explore my Terraform blog series to master Infrastructure as Code (IaC). From resource provisioning and configuration to advanced integrations with AWS, each blog offers concise, actionable insights for automating cloud deployments efficiently.

Up next

Terraform State Locking (Part 5)

States Terraform uses state to keep track of the infrastructure it manages. To use terraform effectively, you must keep your state accurate and secure. State is necessary for requirements for Terraform to function. It is often asked if it is possibl...