When you define a resource block in Terraform, by default, this specifies one resource that will be created. To manage several of the same resources, you can use either count or for_each, which removes the need to write a separate block of code for each one. Using these options reduces overhead and makes your code neater.
Count is what is known as a ‘meta-argument’ defined by the Terraform language. Meta-arguments help achieve certain requirements within the resource block.
In Terraform, meta-arguments are special arguments that add another layer of configuration, allowing you to define dependencies, manage resource count, and customize behavior beyond the standard resource-specific options. Meta-arguments influence the overall configuration or resource behavior. They are applied outside the resource block itself, often using a specific syntax like meta = { ... }.
Commonly used meta-arguments
The "count" meta-argument allows you to create a fixed number of resource instances based on an integer value. The "count" meta-argument is useful when you need a specific number of identical resource instances and don't require the flexibility of for_each.
The "for_each" meta-arguments creates multiple instances of a resource or module based on a collection of values (a set or map). It makes sure each resource is unique and can be managed on its own.
The depends_on meta-argument is used to explicitly define dependencies between resources, ensuring the order in which they are created or updated. The primary purpose of depends_on is to specify that a particular resource depends on the completion of another resource. It is especially useful when creating resources where the order of resource creation matters, and certain resources need to be in place before others can be successfully created or updated.
The "provider" meta-argument is used in Terraform configurations to specify the provider(AWS, Google Cloud, Azure, and many others) that should be used to manage a particular set of resources.
Using meta-arguments in Terraform helps you write code that's easy to understand, saves time by reusing code, and makes your infrastructure flexible and adaptable to various scenarios.
Count
The count meta-argument accepts a whole number and creates the number of instances of the resource specified. When each instance is created, it has its own distinct infrastructure object associated with it, so each can be managed separately. When the configuration is applied, each object can be created, destroyed, or updated as appropriate.
Example:
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.16"
}
}
required_version = ">= 1.2.0"
}
provider "aws" {
region = "us-east-1"
}
resource "aws_instance" "server" {
count = 4
ami = "ami-0c7217cdde317cfec"
instance_type = "t2.micro"
tags = {
Name = "Server ${count.index}"
}
}
for_each
Like the count argument, the for_each meta-argument creates multiple instances of a module or resource block. However, instead of specifying the number of resources, the for_each meta-argument accepts a map or a set of strings. This is useful when multiple resources are required that have different values. Consider our Active directory groups example, with each group requiring a different owner.
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.16"
}
}
required_version = ">= 1.2.0"
}
provider "aws" {
region = "us-east-1"
}
locals {
ami_ids = toset([
"ami-0b0dcb5067f052a63",
"ami-08c40ec9ead489470",
])
}
resource "aws_instance" "server" {
for_each = local.ami_ids
ami = each.key
instance_type = "t2.micro"
tags = {
Name = "Server ${each.key}"
}
}
Multiple key value iteration
locals {
ami_ids = {
"linux" :"ami-0b0dcb5067f052a63",
"ubuntu": "ami-08c40ec9ead489470",
}
}
resource "aws_instance" "server" {
for_each = local.ami_ids
ami = each.value
instance_type = "t2.micro"
tags = {
Name = "Server ${each.key}"
}
}
Task
- Create the above Infrastructure as code and demonstrate the use of Count and for_each.
The count meta-argument: Create a main.tf file with the following.
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.16"
}
}
required_version = ">= 1.2.0"
}
provider "aws" {
region = "us-east-1"
}
locals {
ami_ids = toset([
"ami-0c7217cdde317cfec",
"ami-0e9107ed11be76fde",
])
}
resource "aws_instance" "server" {
for_each = local.ami_ids
ami = each.key
instance_type = "t2.micro"
tags = {
Name = "Server ${each.key}"
}
}
Run terraform init
to initialize terraform
Run terraform plan
Run terraform apply
to create the 4 instances.
Navigate to the AWS EC2 console to verify the 4 newly created instances (server 0, server 1, server 2, server 3).
for_each
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.16"
}
}
required_version = ">= 1.2.0"
}
provider "aws" {
region = "us-east-1"
}
locals {
ami_ids = toset({
"ami-0e9107ed11be76fde",
"ami-0c7217cdde317cfec",
})
}
resource "aws_instance" "server" {
for_each = local.ami_ids
ami = each.value
instance_type = "t2.micro"
tags = {
Name = "Server ${each.key}"
}
}
Run terraform apply
to create the 2 servers.
Navigate to the AWS EC2 console to verify the 2 newly created instances.
- Multiple key value iteration
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.16"
}
}
required_version = ">= 1.2.0"
}
provider "aws" {
region = "us-east-1"
}
#Multiple key value iteration
locals {
ami_ids = {
"linux" = "ami-0e9107ed11be76fde",
"ubuntu" = "ami-0c7217cdde317cfec",
}
}
resource "aws_instance" "server" {
for_each = local.ami_ids
ami = each.value
instance_type = "t2.micro"
tags = {
Name = "Server ${each.key}"
}
}
Run terraform apply
to create the 2 instances that was just renamed.
Thank you.