Overview
In this blog post, we will discuss how to use Terraform's for_each
loop to deploy three subnets in three availability zones along with their respective CIDR blocks and tags.
One of the most powerful and useful features of Terraform is the ability to use loops, which help you create and manage multiple resources with similar configurations.
Prerequisites
Before diving in, ensure that you have the following prerequisites:
- Terraform installed
- An AWS account with access keys configured
- AWS CLI installed and configured
Now, let's create a directory for our Terraform project and initialize it.
$ mkdir for_each
$ cd for_each
$ terraform init
As I mentioned before, our ultimate goal here is to deploy three subnets across three AZs using for_each
loop. There are multiple ways to achieve it, let's look at a couple of ways to do it. My preference is Option 1.
Option - 1
Here is the Terraform configuration file and let's break it down.
provider "aws" {
region = "eu-west-1"
}
locals {
subnets = {
"subnet-1" = { cidr_block = "10.10.1.0/24", availability_zone = "eu-west-1a", tag_name = "private-subnet-1a" }
"subnet-2" = { cidr_block = "10.10.2.0/24", availability_zone = "eu-west-1b", tag_name = "private-subnet-2b" }
"subnet-3" = { cidr_block = "10.10.3.0/24", availability_zone = "eu-west-1c", tag_name = "private-subnet-3c" }
}
}
resource "aws_vpc" "vpc_test" {
cidr_block = "10.10.0.0/16"
tags = {
Name = "vpc_test-vpc"
}
}
resource "aws_subnet" "private" {
for_each = local.subnets
cidr_block = each.value.cidr_block
vpc_id = aws_vpc.vpc_test.id
availability_zone = each.value.availability_zone
tags = {
Name = each.value.tag_name
}
}
Provider and Region - We start by specifying the AWS provider and the region eu-west-1
where we intend to deploy our resources.
Locals - We then define a local variable called subnets
that contains a map with the desired subnet configurations. Each key is a subnet name (subnet-1), and each value is a map with the cidr_block
, availability_zone
, and tag_name
for the subnet.
VPC - Next, we create a VPC with a CIDR block of 10.10.0.0/16
and a name tag of example-vpc
Subnets - The final step is to use the for_each
loop to create multiple subnets by iterating over the subnets
local variable. For each subnet, we assign the cidr_block
, availability_zone
, and tag_name
values from the local variable.
The for_each
loop iterates over the keys and values in the local.subnets
map. For each iteration, each.key
represents the current key (subnet name), and each.value
represents the corresponding value (a map with cidr_block
, availability_zone
, and tag_name
).
On the first iteration, the key will be subnet-1
and the value will be cidr_block = "10.10.1.0/24", availability_zone = "eu-west-1a", tag_name = "private-subnet-1a"
If you want to access the tag_name
, you can use each.value.tag_name
which will result in private-subnet-1a
The aws_subnet
resource block sets the cidr_block
, vpc_id
, availability_zone
, and tags
attributes using the values from the each.value
map. This way, Terraform automatically creates the desired subnets with their respective configurations.
Option - 2
If you want to get creative and don't want to define tags inside the map, this option is for you.
provider "aws" {
region = "eu-west-1"
}
locals {
subnets = {
"eu-west-1a" = "10.10.1.0/24"
"eu-west-1b" = "10.10.2.0/24"
"eu-west-1c" = "10.10.3.0/24"
}
}
resource "aws_vpc" "test-vpc" {
cidr_block = "10.10.0.0/16"
tags = {
Name = "test-vpc"
}
}
resource "aws_subnet" "private-subnet" {
for_each = local.subnets
cidr_block = each.value
vpc_id = aws_vpc.test-vpc.id
availability_zone = each.key
tags = {
Name = "private-subnet-1${substr(each.key, -1, 1)}"
}
}
Everything is self-explanatory at this point except for the tags. Let's break it down, shall we?
The expression Name = "private-subnet-1${substr(each.key, -1, 1)}"
is used to generate a subnet name based on the Availability Zone key in the for_each
loop.
each.key
- This is a reference to the current key of the loop iteration, which in this example is the Availability Zones (eu-west-1a or eu-west-1b or eu-west-1c)substr(each.key, -1, 1)
- Thesubstr
function is used to extract a portion of a string. It takes three arguments: the input string, the starting index, and the length of the substring. In this case, we passeach.key
as the input string,-1
as the starting index, and1
as the length. The negative starting index-1
means that we start counting from the end of the string, so this expression extracts the last character of the Availability Zone name ('a', 'b', or 'c')"private-subnet-1${substr(each.key, -1, 1)}"
- This is a string interpolation that combines the stringprivate-subnet-1
with the last character of the AZ name extracted in the previous step. The resulting string will beprivate-subnet-1a
orprivate-subnet-1b
orprivate-subnet-1c
depending on the currenteach.key
value.
Conclusion
Using Terraform's for_each
loop simplifies the deployment of multiple resources in AWS and makes the code more readable.