Beginner’s Guide to Dynamic Blocks in Terraform (With Code Examples)

Andrei Dumitrescu
Andrei Dumitrescu
hero image

If you’ve ever found yourself stuck copy-pasting the same Terraform code just to make small changes, you know how frustrating it can be. Even worse, those repetitive updates often lead to mistakes that are hard to catch.

What if you didn’t have to do any of that? Imagine writing just a few lines of code and letting Terraform handle the rest. That’s exactly what dynamic blocks do for you—they eliminate duplication and manual tweaking, making your configurations cleaner, more efficient, and easier to scale.

So whether you're managing security rules or spinning up multiple instances, dynamic blocks help you work smarter, not harder. And by the end of this guide, you’ll know how to use dynamic blocks to streamline your Terraform workflow as a DevOps Engineer.

So, let’s get into it…

Sidenote: Want to dive deeper into learning and using Terraform? Then check out my DevOps Bootcamp for Terraform.

learn terraform and devops

I cover everything from the fundamentals all the way to provisioning real-world cloud infrastructure on AWS. You can go from total beginner to being able to get hired as a DevOps Engineer or System Administrator, so it’s a great resource for any skill level.

With that out of the way, let’s get into this guide…

What are dynamic blocks in Terraform?

Dynamic blocks in Terraform automate repetitive tasks, making your code simpler to manage. Instead of copying and pasting similar blocks over and over, dynamic blocks allow you to loop through a set of values (like a list or map) and automatically generate the necessary configurations.

Dynamic blocks are used exclusively within resource blocks, allowing you to dynamically generate multiple nested configurations inside a resource.

Think of them like a programming for loop. You define a template, and Terraform loops through the data, applying the same logic across multiple resources.

This makes dynamic blocks especially useful for configurations that scale, such as security group rules or multiple instances.

Key parts of a dynamic block

  • for_each: Tells Terraform how many times to repeat the block. It loops through a collection (like a list or map) and creates a block for each entry. for_each iterates over a collection, generating one instance of the content block per item, making it particularly useful for handling multiple resources efficiently.
  • content: Defines the structure of each block. It’s the template Terraform uses during each loop
  • ingress_rules: A list of objects where each object defines parameters such as from_port, to_port, protocol, and cidr_blocks

Let’s break it down with an example of managing AWS security group rules:

dynamic "ingress" {
  for_each = var.ingress_rules

  content {
    from_port   = ingress.value.from_port
    to_port     = ingress.value.to_port
    protocol    = ingress.value.protocol
    cidr_blocks = ingress.value.cidr_blocks
  }
}

In this example, Terraform dynamically generates the ingress block based on the ingress_rules variable. By looping through the list of rules, Terraform applies the configuration for each one, reducing manual repetition and keeping your code consistent.

This is particularly useful when managing complex infrastructure where the same logic applies to multiple resources.

The real benefit of dynamic blocks

Dynamic blocks don’t just save you from repetitive tasks - they simplify infrastructure management, especially at scale.

For example

As your infrastructure grows, manually updating configurations across environments (like development, staging, and production) becomes tedious and error-prone. One small change can mean dozens of manual edits, which opens the door to inconsistencies and mistakes.

Dynamic blocks let you define your infrastructure once and apply it across environments, and you no longer have to worry about manually updating files or chasing down configuration errors.

This is particularly helpful in cloud environments like AWS, where managing multiple security groups, subnets, or EC2 instances across regions is common.

In short, dynamic blocks allow:

  • Consistency: Whether you're managing five instances or fifty, dynamic blocks ensure uniform configurations across environments
  • Effortless scaling: Adjust input variables, and Terraform handles adding or removing instances automatically
  • Fewer mistakes: Dynamic blocks reduce manual intervention, which minimizes the risk of copy-paste errors

Real-world example: How to dynamically create subnets

Now let’s see dynamic blocks in action by creating subnets across multiple availability zones (AZs). Without dynamic blocks, you’d have to manually define each subnet, which becomes error-prone as your infrastructure scales.

But with dynamic blocks, you can automate the creation of subnets by having Terraform loop through a list of subnets and dynamically generate the configuration for each AZ, like so:

variable "subnets" {
  type = list(object({
	cidr_block = string
	az     	= string
  }))
}

resource "aws_subnet" "example" {
  for_each = { for idx, subnet in var.subnets : idx => subnet }
  cidr_block    	= each.value.cidr_block
  availability_zone = each.value.az
  vpc_id        	= var.vpc_id
}

In this example, the for_each loop dynamically creates a subnet for each availability zone in your list.

As your infrastructure grows, you can easily add or remove subnets by adjusting the input list—without having to rewrite your entire configuration. This approach keeps your infrastructure scalable and reduces the chance for errors, especially in production environments.

Best practices for when using dynamic blocks

Dynamic blocks can be a huge asset in Terraform, especially when you’re working on complex infrastructure.

To get the most out of them though, it’s important to follow a few key best practices. These will help ensure your code remains clean, scalable, and easy to manage.

Looping over lists and maps

One of the most common ways to use dynamic blocks is when working with collections like lists or maps. By looping through a collection, you can apply consistent configurations across multiple resources.

This is especially useful when you’re setting up things like security group rules, EC2 instances, or other resources that require similar settings.

For example

Instead of manually defining multiple EC2 instances, dynamic blocks allow you to loop through a list and apply the configuration for each instance:

resource "aws_instance" "example" {
  for_each = { for idx, instance in var.instances : idx => instance }
  instance_type = each.value.instance_type
  ami       	= each.value.ami
}

This not only reduces repetition but also makes scaling your infrastructure easier, as Terraform handles the creation of each resource consistently.

Breaking configurations into reusable modules

Modules are an essential feature in Terraform, and dynamic blocks shine when used inside them. Instead of duplicating similar blocks across different projects or environments, you can create reusable modules that use dynamic blocks to handle collections of resources.

For instance, if you manage multiple environments—like development, staging, and production—you can define a module that provisions resources for all environments, passing environment-specific variables each time you call the module.

This approach helps keep your infrastructure DRY, reduces potential errors, and makes the code easier to maintain.

By using dynamic blocks within reusable modules, you ensure your configurations are scalable and adaptable across different environments.

Keep your configurations simple and readable

Although dynamic blocks can simplify your code, it’s important not to overuse them. A best practice is to keep your dynamic blocks as simple and readable as possible. If adding a dynamic block doesn’t simplify the code or is making it harder to understand, it might be better to use static blocks.

For example, if a resource doesn’t need to be repeated across multiple items in a collection, using a static block will keep the code simpler. Always prioritize readability and simplicity when designing your Terraform configurations.

Document your dynamic blocks for clarity

Another best practice is to document how and why dynamic blocks are used in your configuration. Even though dynamic blocks can automate a lot, they might be unfamiliar to some team members or future readers of your code.

By adding comments that explain how the dynamic block works, what variables it relies on, and what outcomes are expected, you make the code more maintainable and easier for others to follow.

For example

# Dynamic block for ingress rules
# Loops through ingress_rules and applies settings for each
dynamic "ingress" {
  for_each = var.ingress_rules
  content {
	from_port   = ingress.value.from_port
	to_port 	= ingress.value.to_port
	protocol	= ingress.value.protocol
	cidr_blocks = ingress.value.cidr_blocks
  }
}

Proper documentation makes it easier for others (or even your future self) to understand the purpose of the dynamic block and how it functions.

Common mistakes to avoid with dynamic blocks

While dynamic blocks can simplify Terraform configurations, there are several common mistakes you should be aware of.

Avoiding these will help you prevent errors and keep your configurations clean and maintainable.

Overcomplicating dynamic blocks

It’s tempting to use dynamic blocks everywhere because of how they reduce repetitive code. However, sometimes static blocks are the better option, particularly if the dynamic block doesn’t add much benefit and only makes the code harder to read.

Remember that simplicity is key. If using dynamic blocks makes your configuration more complicated than it needs to be, opt for static blocks instead.

Using too much conditional logic

Dynamic blocks combined with too many conditional (if) statements can quickly turn into a maintenance nightmare. Each condition adds complexity, making it harder to debug and maintain the configuration.

If you find yourself adding a lot of conditional logic within dynamic blocks, it might be time to refactor or simplify your configuration by splitting it into smaller, more manageable pieces.

Data type mismatches

One of the most common errors with dynamic blocks is a data type mismatch. For example, if for_each expects a list but you accidentally provide a map, Terraform will throw an error.

To avoid this, always clearly define your variables and ensure the data types are correct.

For example

Here’s how you can define ingress rules with the correct data types:

variable "ingress_rules" {
  type = list(object({
	from_port   = number
	to_port 	= number
	protocol	= string
	cidr_blocks = list(string)
  }))
}

Explicitly defining data types helps avoid mismatches and ensures smooth execution of dynamic blocks.

Not handling empty or null values

If your for_each loop encounters an empty list or a null value, Terraform might skip the dynamic block entirely, which can lead to incomplete configurations.

To prevent this, ensure you handle empty lists or null values in your code by providing a fallback option.

dynamic "ingress" {
  for_each = var.ingress_rules != null ? var.ingress_rules : []
  content {
	from_port   = ingress.value.from_port
	to_port 	= ingress.value.to_port
	protocol	= ingress.value.protocol
	cidr_blocks = ingress.value.cidr_blocks
  }
}

This ensures that even if the list is empty, Terraform doesn’t break or skip necessary parts of the configuration.

Skipping terraform plan

Before applying any changes, always run terraform plan. This command shows you what Terraform is about to do, helping you catch potential mistakes early.

If you find yourself stuck on an error, verbose logging can be helpful for tracking down the issue:

TF_LOG=DEBUG terraform apply

This will give you detailed logs that can help pinpoint where things went wrong, especially when dealing with complex dynamic blocks.

What’s next?

By now, you should have a solid understanding of how dynamic blocks can make your life easier. Whether you’re spinning up subnets, managing security groups, or scaling instances, dynamic blocks help you reduce repetition, avoid mistakes, and keep your code clean and flexible.

The best part is, they save you time and headaches as your infrastructure grows.

Just remember though to always follow best practices, watch out for common pitfalls like type mismatches, and always preview your changes with terraform plan. Do that, and dynamic blocks will quickly become one of your go-to tools in Terraform.

Now it’s time to put what you’ve learned into practice. Take a look at your Terraform setup and see where dynamic blocks can simplify things. The more you use them, the more efficient your infrastructure management will be.

P.S.

Don’t forget! If you want to dive deeper into learning and using Terraform, check out my DevOps Bootcamp for Terraform.

learn terraform and devops

I cover everything from the fundamentals all the way to provisioning real-world cloud infrastructure on AWS and getting hired!

Once you join, you'll have the opportunity to ask questions in our private Discord community from me, other students and working professionals.


Not only that, but as a member - you’ll also get access to every course in the Zero To Mastery library!

More from Zero To Mastery

Top 5 Benefits Of Using Terraform preview
Top 5 Benefits Of Using Terraform

Are you a DevOps Engineer who wants to automate your tasks and streamline your workflow? If so, then Terraform may just be the tool for you!

Top 5 In-Demand Tech Jobs For 2024 (+ How To Land A Job In Each!) preview
Top 5 In-Demand Tech Jobs For 2024 (+ How To Land A Job In Each!)

Want to get hired in a tech job in 2024? Pick one of these 5 if you want: 1) High salary 2) Jobs available now 3) Can learn the skills as a complete beginner.

53 Terraform Interview Questions and Answers preview
53 Terraform Interview Questions and Answers

About to sit for a DevOps technical interview with Terraform? Learn the most common questions and answers that you need to know to ace your interview!