Terraform Dynamic Blocks

Search for a command to run...

No comments yet. Be the first to comment.
In this short series, we will include some articles to go through how to write your own terraform code.
hands on terraform
Technologies come and go, but data is here to grow! Introduction Data is the core of every modern application, driving insights, automation, and user experiences. Over time, technologies have evolved

Introduction In this guide, we’ll walk through creating an Amazon EMR cluster in a private subnet using Terraform. By placing your EMR cluster in a private subnet, you prevent it from being directly accessible from the internet — improving both secur...

Introduction Apache Spark, a powerful distributed processing engine, relies heavily on memory management for optimal performance.The Unified Memory Manager (UMM) — introduced in Spark 1.6 — plays a ke

Introduction In this series, we’ll focus on Serverless Framework, one of the fastest ways to build and deploy serverless applications. If you’ve already worked with Terraform, you know how great it is

Once you’ve created an S3 bucket, you’ll likely need to transfer large files — sometimes in the order of gigabytes or even terabytes. AWS S3 allows object sizes of up to 5 TB, but a single PUT upload is limited to 5 GB. To handle larger uploads, S3 o...

In this part, we’ll explore some advanced features of Terraform. You’ll learn about variable types, constructs like for_each, and how to use dynamic blocks to make your Terraform code cleaner and less verbose.
Just like other programming languages, Terraform’s HCL (HashiCorp Configuration Language) supports variables and data types.
Modules and resource blocks often accept arbitrary user inputs. The best practice is to define variables in a terraform.tfvars file, which supplies values to Terraform during runtime.
Let’s start with a simple example.
We’ll create a Terraform project that provisions an S3 bucket. This project will include the following files:
.
├── backend.tf
├── main.tf
├── terraform.tfvars
└── variables.tf
main.tfThis file defines the S3 bucket and uses variables via the syntax var.variable_name.
provider "aws" {
profile = var.profile
region = var.region
}
resource "aws_s3_bucket" "testbucket" {
bucket = var.bucket_name
}
variables.tfHere we declare the variables used in the project:
variable "region" {
type = string
default = "us-east-1"
}
variable "profile" {
type = string
default = "profile-name-to-use"
}
variable "bucket_name" {
type = string
description = "Name of S3 bucket to create"
}
You can also mark variables as sensitive or add validation rules for stricter control.
terraform.tfvarsThis file assigns values to the declared variables:
bucket_name = "test_bucket"
Here’s a typical Terraform workflow:
terraform init # Initialize backend and providers
terraform fmt # Format code
terraform validate # Validate for syntax errors
terraform plan # Preview changes
terraform apply # Apply changes
terraform destroy # Destroy resources
Terraform supports two broad categories of variables:
1. Primitives:string, boolean, number
2. Complex:set, map, object, tuple, list, any
Here’s an example of a complex variable definition:
variable "igress_params" {
type = map(object({
port = number
proto = string
cidr = list(string)
}))
default = {
"rule1" = {
port = 22
proto = "tcp"
cidr = ["0.0.0.0/0"]
},
"rule2" = {
port = 80
proto = "tcp"
cidr = ["1.2.3.4/32"]
}
}
}
This map(object) variable defines ingress rules that you can later reference while creating security groups or VPCs.
for_eachThe for_each construct is useful when you want to create multiple resources of the same type with varying values. For example, creating three S3 buckets for dev, uat, and prod.
provider "aws" {
profile = "aws-profile-to-use"
region = "us-east-1"
}
variable "s3_buckets" {
type = set(string)
description = "Name of S3 buckets to create"
default = ["test-dev", "test-uat", "test-prod"]
}
resource "aws_s3_bucket" "testbucket" {
for_each = var.s3_buckets
bucket = each.value
}
Terraform will iterate through s3_buckets and create three separate S3 buckets.
dynamic KeywordWhen defining resources like security groups, you might need multiple ingress or egress rules. Manually declaring them can get verbose and hard to maintain.
resource "aws_security_group" "my-sg" {
vpc_id = aws_vpc.my-vpc.id
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
}
This approach quickly becomes messy for many rules.
We can make this clean and reusable using the dynamic keyword.
variable "igress_params" {
type = map(object({
port = number
proto = string
cidr = list(string)
}))
default = {
"rule1" = {
port = 22
proto = "tcp"
cidr = ["0.0.0.0/0"]
},
"rule2" = {
port = 80
proto = "tcp"
cidr = ["1.2.3.4/32"]
}
}
}
resource "aws_vpc" "my-vpc" {
cidr_block = "10.0.0.0/16"
}
resource "aws_security_group" "my-sg" {
vpc_id = aws_vpc.my-vpc.id
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
dynamic "ingress" {
for_each = var.igress_params
content {
from_port = ingress.value["port"]
to_port = ingress.value["port"]
protocol = ingress.value["proto"]
cidr_blocks = ingress.value["cidr"]
}
}
}
Terraform will iterate through each map entry in igress_params and dynamically create multiple ingress blocks — one for each rule.
Key benefit: Your Terraform files stay concise, and you can easily scale your configuration by just editing variable definitions.
Dynamic blocks and constructs like for_each bring programmability and flexibility into Terraform — reducing code repetition while improving readability and scalability.