TNS
VOXPOP
As a JavaScript developer, what non-React tools do you use most often?
Angular
0%
Astro
0%
Svelte
0%
Vue.js
0%
Other
0%
I only use React
0%
I don't use JavaScript
0%
NEW! Try Stackie AI
Cloud Services / Infrastructure as Code / Operations

How To Use Terraform’s ‘for_each’, With Examples

Learn how to use Terraform’s for_each to dynamically create multiple resources with clean, maintainable code. Simplify infrastructure with maps, sets, and reusable patterns.
May 5th, 2025 8:02am by
Featued image for: How To Use Terraform’s ‘for_each’, With Examples
Feature image via Pixabay.  

Terraform is a powerful Infrastructure as Code (IaC) tool. It lets you define your cloud configuration in a succinct, reproducible way and does the heavy lifting of creating, updating, or deleting resources on your behalf. Whether provisioning a basic EC2 instance or orchestrating an entire multiregion infrastructure, Terraform lets you do it all with code — your infrastructure becomes predictable and version-controlled.

If you’ve used Terraform for a bit, you’ve probably been in the position where you needed to create more than one instance of the same type of resource — e.g., several EC2 instances or tens of the same security groups. Having to write out individual blocks for each of them soon becomes annoying and unmanageable. That’s exactly where Terraform’s for_each enters.

In this post, we’ll break down what for_each really does, how it works, and how you can make your Terraform code more beautiful, dynamic, and maintainable. Whether you’re building multiple resources in a single environment or dealing with multiple environments at once, for_each  might be your new BFF.

Let’s dive in!

Introduction to Terraform Meta-Arguments

Terraform offers other commands you can use in your resource blocks to control the creation of resources. Those are called meta-arguments. Some of the most common are count, provider, depends_on, lifecycle, and of course — for_each.

All of them do some particular thing. While count enables you to create many instances of some resource through index numbers, for_each enables you to use a map or set of strings with finer control and legibility.

What Is Terraform for_each

So what is for_each then?

It’s kind of like a loop in code. Instead of writing five individual EC2 blocks, you can write one and loop through a map or set to dynamically create them. It is super useful when each resource has a unique name or configuration.

For example, assume you need to create three EC2 instances with different instance types and tags. Instead of repeating code, you can pass a map and let for_each do its magic.

for_each vs. count: Key Differences

You might think — “wait, isn’t this the same as count?”

Not quite. Why not?

Prior to the bullet points, let’s quickly look at the larger picture. Count does simple index-based iteration. It’s great when all resources pretty much equal. But where you want finer-grain control, for_each lets you use meaningful keys and structured data.

Let us see how to use count for repeated content using an example below. First of all, we are declaring a list variable as follows, which we want to use it for our EC2 instances.


Now, we can use these variables in our aws_instance block to provision the required EC2 instances with Name tag automatically as follows.


So, here’s a quick comparison between count and for_each

  • count uses index numbers (0, 1, 2, …)
  • for_each uses keys from maps or sets
  • Deleting a resource with count can mess up indexes and force recreation
  • for_each is safer because it tracks resources by their unique keys

So if your resources differ slightly — or you want more predictable diffs — for_each is your go-to.

How for_each Works in Terraform

If you need to create lots of the same thing in Terraform, like a few EC2s, numerous security groups, or a list of IAM roles — you don’t need to painstakingly copy and paste each by hand. For_each is your solution, which gives each resource a unique key so they can be easily referenced and controlled individually. So instead of “create three resources,” you are authoring “create one resource for each item on this list or map.” That is clearer, less problematic, and readable within your code, especially in instances where working with complex or named configurations exists.

We shall explore in depth what sorts of data you’re able to play with via the use of for_each and its application within actual scenarios.

Syntax and Supported Data Types

For_each supports:

  • Maps (most common and powerful).
  • Sets of strings (basic but useful).

Here’s a sample syntax using a map:


Simple, right? Each item in the map becomes its own instance.

Understanding each.key and each.value

When you use for_each, Terraform gives you two helpers:

  • each.key – the name or label (like web1).
  • each.value – the data associated with it (like t2.micro).

Want to get fancier and need to handle complicated scenarios? You can make the value a map with multiple attributes:


In the above map, we are using different configurations for different EC2 instances for our applications such as instance type, disk volume size and the requirement of public IP address.

Now, we can use the map in EC2 instance creation time as follows.


Now we’re talking!

Common Mistakes and How to Avoid Them

Let’s get real — for_each can be a real lifesaver, but it also has some “oops” moments if you’re not careful. Here are some of the common pitfalls individuals run into, and how to avoid them:

  • Avoid using computed values as keys: It might be tempting to use something like timestamp() or a resource’s ID as part of your for_each map or set, but Terraform needs to know all keys before it runs the plan. Computed or dynamically generated values won’t be available at that stage, and Terraform will throw errors or behave unexpectedly. Stick to static or known values wherever possible.
  • Don’t mix count and for_each on the same resource: Sure, this might be a cool hack, but Terraform does not support both count and for_each on one resource. You have to pick one. Use count when your input is a list and order matters, and use for_each when you’re working with sets or maps and must label it with distinct keys.
  • Never put sensitive data in keys or values being sent to for_each: It’s not just bad form — it can leak secrets accidentally too. If you include passwords, tokens, or any sensitive values as input as part of your input, Terraform can accidentally expose them in logs, plan output, or even error messages. Secrets always go into secure inputs such as variables with sensitive = true.

Finally, all of these issues arise because Terraform is trying to do a lot of things ahead of time. So when you’re designing your for_each logic, think like Terraform: “Can I do this ahead of time?” If no, it’s time to rethink your input.

Examples of Using for_each in Terraform

Let’s walk through a few real-world examples with AWS, so you can understand how for_each really shines in real-world usage. Whether you’re deploying multiple security groups, provisioning EC2 instances with different configurations, or applying reusable modules across environments, for_each gives you granular control and flexibility.

The goal here isn’t just to reveal syntax — but to help you understand how and why you’d be using for_each in different use cases.

Example 1. Using for_each With AWS Security Groups

Suppose you need to create multiple security groups for different services (web, db, cache). Instead of repeating yourself, here’s a neat way:


Simple and readable.

Example 2. Creating Multiple EC2 Instances With for_each

We already looked at this above, but it’s a great example, so let’s quickly revisit it:


In the above map, we are using different configurations for different EC2 instances for our applications such as instance type, disk volume size and the requirement of public IP address.

Now, we can use the map in EC2 instance creation time as follows.


Clean and dynamic.

Example 3. Using for_each in a Terraform Module

Modules support for_each too! Let’s say you have a reusable EC2 module. You can do this:


This pattern scales well and keeps your code DRY.

Common Use Cases for for_each

Then when should you actually reach for for_each in your Terraform projects? It’s not just about creating multiple copies of a resource — it’s about doing it intelligently and in a way that is still readable and maintainable. for_each shines when you are working with dynamic sets of resources, need environment-specific configuration, or want your code to stay DRY (Don’t Repeat Yourself). Rather than duplicative code blocks or dealing with convoluted logic, you can look to use for_each in order to ease life and make more scalable, simpler infrastructure. Let’s cover some real-world application examples whereby it can simplify life.

Managing Multiple Resources Dynamically

When you’re dealing with cloud infrastructure, you’ll often find yourself wanting to define several resources of the same type — like several S3 buckets for different teams, EC2 instances for different environments, or IAM roles with varying permissions. Defining each of those resources manually can quickly turn into a mess, not to mention a headache to update. That’s where for_each is a lifesaver.

With for_each iterating over a map or a set, you can dynamically create resources on the fly based on input values. You can keep, for example, a plain old map of bucket configurations and team names and let for_each provision one by one S3 buckets for each. The benefit? Smaller code to read, less copying-pasting incorrectly, and a much more manageable config to change later on. Five or fifty resources — you pick — is still within for_each’s reach.

Using for_each for Multi-Environment Deployments

When you’re deploying to more than one environment — e.g., dev, staging, and prod — things can begin to get a little repetitive. More often than not, the resources are the same kind (e.g., EC2 instances or security groups), but the underlying values — e.g., instance size, number of instances, tags, etc. — are all slightly different between environments.

Try to imagine now having to manage all of that with hardcoded blocks or nested conditionals. It would be a nightmare, wouldn’t it?

This is where for_each comes to the rescue. You can keep your environment-specific values in a map and then pass the same to your resource block using for_each. Terraform will then loop through each of those values and create the respective setup based on the current workspace or based on an environment variable you might want to pick.

Let us attempt an example for AWS EC2 instances.

You can create a variable like this in your variables.tf:


Next, use for_each to create multiple instances dynamically:


Here’s what’s happening:

  • We’re creating keys dynamically like dev-0, dev-1, etc., based on the instance_count.
  • Each example will use the environment-specific config.
  • Tags are merged such that every instance is identifiable and environment-aware.

Combining for_each With Terraform Modules

Using for_each with Terraform modules is a great way to keep your infrastructure clean, modular, and scalable. Instead of repeating the same code blocks for each environment, application, or team, you can define a reusable module once and then use it multiple times with different values through for_each. Not only does it reduce code duplication but also your configurations are much easier to work with and update in bulk.

For example, imagine you have a module to start an EC2 instance. You can use a map to create dev, staging, and prod environment configs and then use for_each to deploy the module for every environment automatically as you learned in the previous section. It helps bigger teams work together too — every project or team can have its own config, and the same module can handle it all behind the scenes. In short, this blend is ideal when your infrastructure starts to grow and you need flexibility as well as consistency.

Best Practices for Using for_each

Let’s run over some good recommendations to get most use out of for_each. Although for_each can make your Terraform code more clean and dynamic, it comes with its own flaws. It’s easy to get excited and turn your config into something unflaggingly perplexing or tough to debug in the future. That’s why it’s important to follow some best practices in using for_each — not just to avoid mistakes, but to make Terraform code that’s readable, reusable, and reliable.

In the sections below, we’ll look at how to choose between for_each and count, how to avoid common pitfalls when using lists, and how to keep your configuration neat and maintainable — especially when working in a team or managing a growing infrastructure.

Choosing Between count and for_each

Selecting between count and for_each is an eternal conundrum when writing Terraform code — and getting it right can be worth it in loads of confusion saved in the future. Use count when you’re provisioning lots of similar resources and don’t care which ones they are, just how many. Such as booting up three similar EC2 servers with no difference between them? Count =3 does it perfectly. Terraform will label them numeric indexes like aws_instance.example[0], aws_instance.example[1], etc.

But if every resource is slightly different — like EC2 instances with unique names, tags, or types — it’s more appropriate to use for_each. It enables you to declare resources in terms of a map or a list of strings, which means every one of them has an explicitly stated identity mapped to a specific key. This makes your code more declarative and less prone to errors, particularly when updating or deleting, since Terraform can match resources by their keys instead of indexes, which might change if the count does. So, a simple rule of thumb: if the resources are homogeneous and interchangeable, use count. If they’re heterogeneous and must be tracked individually, use for_each.

Avoiding Errors When Using for_each With Lists

When using for_each with lists, remember that lists do not have unique keys. This lack of uniqueness can cause issues, especially when deleting or inserting items. Terraform relies on keys to track and manage resources, and when you’re working with a plain list, the order of items becomes important, which can lead to errors or bizarre behavior. To avoid this, you can convert the list to a map or set of unique keys. For example, you can use a for expression to create a map, binding each item to a unique key. This will render your configuration more stable and deterministic, especially when updating or modifying resources over time.

Ensuring Readability and Maintainability in Terraform Code

With the use of for_each, it’s easy to get lost in complex logic, especially in the case of multiple resources or configurations. For the sake of readability and not being confusing, a good practice is to divide your code into smaller and more digestible parts with locals or Terraform modules. For instance, if you have a complex set or map that you’re iterating using for_each, you can define it once using locals to declare it outside and give it an explicit name. This makes anyone reading (even you in the future) crystal clear on the structure and intent behind each segment. Lastly, always use descriptive key and value names that define the purpose of each resource or variable. This is especially important when teaming up, as good documentation and good comments will make your code so much easier to read. By providing context to the reasoning behind your for_each use, you make your Terraform code easy to maintain, scale, and adapt as your infrastructure evolves and expands.

Debugging and Troubleshooting for_each Issues

Just like with anything in life, when applying infrastructure as code, it does not always turn out perfectly as planned, and for_each is no exception. It can be a typo, a misunderstanding of data types, or a resource dependency problem, so having a solid debugging workflow in place is important. While Terraform is great at providing feedback via error messages, sometimes the initial reason is not obvious at first glance. Here, we will look at some of the common issues with for_each and how you can best fix them. Understanding how to read error messages, review the plan output, and use Terraform’s built-in tools will make it simple to troubleshoot quickly and keep your infrastructure running efficiently. Let’s walk through some of the common issues and solutions.

Common Errors and Fixes

  • “Invalid for_each argument”: Usually means your input isn’t a valid map or set.
  • “Cannot use computed value”: You’re probably using something like a resource ID as a key — avoid that.
  • “for_each on a resource with computed keys”: This error occurs when you try to use a resource that has computed values (like timestamps or dynamically generated IDs) as keys in for_each. Since Terraform can’t predict these values during the plan phase, it results in an error. Always use static, predefined values for keys.
  • “for_each cannot be used with dynamic blocks”: This happens if you’re trying to use for_each inside a dynamic block, which is not supported. Consider restructuring your code to move the for_each outside the dynamic block or use count if the situation allows.

Debugging for_each With Terraform Plan

Your friend is Terraform plan when applying for_each. It gives you an explicit preview of what Terraform will create, update, or delete — before you do anything at all. With for_each, especially with dynamic sets or maps, this command helps you visualize whether your data is being processed as you intend.

For example, when you’re passing a map of EC2 instance configs, terraform plan will show you every resource that will be planned, along with the extremely specific keys it’s being used with. This is ridiculously helpful in catching misconfigurations early — like non-unique keys, absent values, or when Terraform doesn’t get your input format.

In short, don’t wait until terraform apply to find out something’s wrong. Use terraform plan a lot throughout development — it’ll save you time, confusion, and maybe costly mistakes.

Understanding Terraform State With for_each

State files track your resources using the keys you defined. So if you change a key, Terraform will destroy and recreate that resource. Rename keys with caution.

Conclusion

Key Takeaways on Using for_each

Terraform’s for_each is game-changing when you’re dealing with infrastructure at scale. It helps you move away from boilerplate resource blocks and brings a newer, more dynamic way of handling multiple instances. Whatever it may be — EC2s, IAM roles, or S3 buckets — you can actually manage what’s being deployed and how, without having to unnecessarily bloat your code.

It is especially useful when you are dealing with maps or sets, where each element has some specific key or name. It gives you granular control over each resource, making your Terraform code functional, as well as readable and maintainable.

And the bonus? Your code turns DRY (Don’t Repeat Yourself). Instead of copy-pasting bunches of similar resources with modifications, for_each lets you define a pattern and fill it with changing data. Cleaner code, fewer bugs, more sleep.

When to Use for_each in Terraform Projects

You should consider using for_each when your infrastructure needs go beyond just spinning up identical resources. If you’re:

  • Deploying multiple resources with different configs (like a dev and prod EC2 with different tags and instance types).
  • Wanting to assign meaningful identifiers (instead of relying on numeric indexes).
  • Trying to avoid headaches with count, especially where the order or deletion of resources causes issues.

Then for_each is your friend. It shines in production environments where you want more predictability and control, and especially when your project grows in size and complexity. If you’re already using count and hitting limitations, then it’s probably time to refactor with for_each.

Next Steps: Experimenting With for_each in Your Infrastructure

So, what’s next?

Try to take one of your existing resource blocks — maybe a security group with a few rules or a list of EC2 instances — and reimplement it using for_each. You’ll likely find it more intuitive once you get used to driving your resources from maps or sets.

Start with something simple, test regularly with terraform plan, and see your infrastructure code get more readable and more flexible. Once you’re comfortable, try combining it with modules for even more reusability.

And if you hit a hitch or something doesn’t behave the way you’d intuitively expect it to, well — that’s all part of learning. Drop a comment or give a yell. Happy Terraforming!

Note: The example Terraform code used in this article is available in the Git repo for reference.

Created with Sketch.
TNS DAILY NEWSLETTER Receive a free roundup of the most recent TNS articles in your inbox each day.