for_each
Tutorial with Practical ExamplesTerraform is a popular infrastructure-as-code tool (IaC) that allows you to define, provision, and manage cloud infrastructure using declarative configuration. As your infrastructure grows, so does the need to efficiently manage multiple similar resources, whether you're creating storage buckets, firewall rules, or IAM accounts.
One of Terraform's most powerful constructs for handling such scenarios is for_each
. This meta-argument
lets you dynamically create resources using maps, sets, or objects.
Compared to more basic count
, for_each
provides enhanced control and flexibility,
especially when working with named resources with some configuration variations.
In this tutorial, you'll learn how to use for_each
through a series of practical,
gradually more advanced examples. We'll focus on Google Cloud Platform (GCP) resources that are either free
or very low-cost, so you can follow along without incurring unnecessary charges.
You'll learn:
for_each
works and when to use itfor_each
and count
for_each
with more complex input structures
By the end of this tutorial, you should be able to use for_each
in your own Terraform projects to reduce
code repetition and improve maintainability.
Before you begin, ensure you have the following:
gcloud auth application-default login
, orNote: Most examples in this tutorial use GCP's free tier services. Still, keep an eye on your GCP billing and always delete unnecessary resources to avoid unexpected charges.
for_each
in TerraformWhen managing infrastructure with Terraform, you often need to create multiple resources of the same type, for example, several GCP storage buckets or service accounts. While you could use separate resource blocks for each instance, such approach quickly becomes inefficient and error-prone.
Terraform's for_each
meta-argument solves this problem by letting you define a single code block
(or a module) that can provision multiple resources, each with its own unique configuration defined by an input
collection. This helps you write cleaner, more scalable infrastructure code.
for_each
Syntax Overview
When for_each
is added to a resource block, Terraform loops over the corresponding collection,
which could be either a set, map, or map of objects, and creates one resource instance for each element in the collection.
How it Works:
for_each = <collection>
accepts a set
, map
, or map(object)
as input.
each.key
assumes the current key from the map
or the element itself if using a set
.each.value
holds the value for the current element. If a set
was provided, this is the same as
each.key
.resource_type.name["key"]
.
Examples of input collections that can be used with for_each
:
a simple set of strings - set(string)
:
a key-value mapping - map(string)
:
a more complex structure for advanced use cases - map(object)
:
Note: If you have a list, you must convert it to a set with toset(var.my_list)
.
for_each
vs count
Terraform provides two ways to create multiple resources: count
and for_each
. While they
can produce similar results, count
and for_each
have important differences:
Feature | count |
for_each |
---|---|---|
Input Type | list or integer |
map or set |
Item Reference | count.index |
each.key , each.value |
Resource Addressing | Index-based (e.g., resource[0] ) |
Named keys (e.g., resource["web-server"] ) |
Ordering | Yes (based on list index) | No (map/set keys are independent) |
for_each
Concepts to Rememberfor_each
:
map
or a set
for_each
, each resource instance is addressed with its key -
resource_type.name["key"]
count
and for_each
in the same resource block.set(string)
)
Before diving into cloud-specific resources, let's begin with a very simple example that illustrates the
behavior of for_each
using Terraform's local_file
resource. This resource block writes a file
to your local machine and does not require any cloud credentials, making it perfect for learning and
experimentation.
Goal: Create multiple local .txt
files from a list of names using for_each
.
Terraform Code:
Explanation:
file_names
variable is a set
of strings, which ensures that each file name is unique.for_each = var.file_names
tells Terraform to create one local_file
resource per each
item in file_names
.path.module
is a Terraform built-in variable that refers to the directory where the
current .tf
file (module) is located.each.key
retrieves the current file name from the set
.each.value
contains the same value as each.key
.report.txt
, summary.txt
,
and notes.txt
.Result:
After running
terraform init
terraform apply
Terraform will create the following files in your working directory:
report.txt
summary.txt
notes.txt
Cleaning Up:
To delete the files, simply run terraform destroy
. Confirm when prompted, and Terraform will delete
all the created resources.
Summary:
for_each
loops over a set
and creates multiple local_file
resources.set
value
with each.key
.for_each
before applying it to
more complex cloud resources.map(string)
)
In this example, we'll use for_each
with a map(string)
to create multiple IAM service accounts
in Google Cloud. This approach allows you to assign each account a unique ID and a descriptive display name.
Using a map
instead of a set
gives us a key-value pair structure, enabling easy reference to
individual resources by their logical names, and making the configuration more readable.
Goal: Create multiple GCP IAM service accounts, each with:
Terraform Code:
Explanation:
service_accounts
is a map(string)
where:
for_each = var.service_accounts
tells Terraform to create one service account for each map entry.each.key
provides access to the keys from the input map.each.value
references the corresponding service account display name from the input map.output
block returns configuration details for the created accounts.for
expression iterates over the resource list google_service_account.sa
to
retrieve account details.Result:
After running
terraform init
terraform apply
Terraform will perform the following actions:
google_service_account.sa["app-engine-sa"]
google_service_account.sa["billing-sa"]
google_service_account.sa["monitoring-sa"]
Cleaning Up:
To delete the accounts, simply run terraform destroy
. Confirm when prompted, and Terraform will delete
all the created resources.
Summary:
In this example we demonstrated how to use for_each
with a map(string)
to create uniquely
named resources with distinct configurations and display the resource parameters with the help of a for
loop.
map(object)
)
In this example, we'll use for_each
with a map(object)
to dynamically create multiple Google
Cloud Storage (GCS) buckets.
Each bucket will have a unique name and configurable attributes, such as location, storage class, and versioning, all
defined in a single input variable.
This pattern is ideal for batch provisioning cloud resources of the same type with individualized configurations.
Goal: Create multiple GCS buckets in different regions, with configurable storage classes and versioning options, using a single input variable.
Terraform Code:
Explanation:
gcs_buckets
is a map of objects, where each:
for_each = var.gcs_buckets
loops over the map and creates one bucket per entry.each.key
refers in turn to each bucket name (key) from the map.each.value.location
, each.value.storage_class
, and each.value.versioning
refer the corresponding bucket configuration properties.${...}-${var.project_id}
appends the project ID to ensure globally unique bucket names.force_destroy = true
allows you to delete a bucket that contains objects (to simplify cleanup process).output
block returns configuration details for the created buckets.for
expression iterates over the resource list google_storage_bucket.buckets
to retrieve
bucket details.Result:
After running
terraform init
terraform apply
Terraform will perform the following actions:
my-logs-bucket-YOUR_PROJECT_ID
in US-EAST1 with STANDARD storage class and versioning enabledmy-archive-bucket-YOUR_PROJECT_ID
in US-WEST1 with NEARLINE storage class and versioning disabledCleaning Up:
To delete the buckets, simply run terraform destroy
. Confirm when prompted, and Terraform will delete
all the created resources.
Summary:
In this example we demonstrated how to use for_each
with a map(object)
to create uniquely named
resources with distinct configurations and output the resource parameters with the help of a for loop.
Cannot use for_each
and count
together
count
and for_each
on the same resource.
Changing the for_each
key map can cause resource recreation
for_each
map (e.g., rename a key), Terraform will destroy and recreate
the resource, even if the underlying configuration is the same.
Avoid using set()
keys in resource references
set(string)
with for_each
, Terraform automatically uses each set
item as the key for the corresponding resource instance. But because sets may not be stable, these auto-generated keys
aren't ideal for referencing specific resource instances later in your code.
for_each
in Terraform is very useful, but it's important to follow some best practices and understand
its limitations to ensure your configurations are maintainable, predictable,
and scalable.
Prefer for_each
over count
for complex or keyed data
for_each
gives better control and more readable plans when working with maps or sets of strings.google_service_account.sa["monitoring"]
, rather than
an index google_service_account.sa[0]
(with count
), which helps avoid issues if items
are reordered or changed.Use Maps for Stable Resource Identifiers
Combine outputs using a for
expression
Leverage complex objects for multi-property resources
map(object({ ... }))
when each resource needs distinct parameters.
This allows granular control and clean data modeling.
Use Modules with for_each
for_each
at the module level.
The for_each
meta-argument in Terraform is a powerful feature that allows you to manage multiple
resources efficiently and predictably. By leveraging maps and sets as input, for_each
gives you
control over resource creation, naming, and referencing, making your infrastructure code cleaner, more
scalable, and easier to maintain.
In this tutorial, we explored:
for_each
meta-argument.for_each
.
Understanding how to structure input data properly is key to using for_each
effectively. As your
infrastructure grows in complexity, these patterns become essential for writing modular, dynamic, and
robust Terraform code.
See Also:
Handling Terraform State in Multi-Environment Deployments
Understanding Terraform Variable Precedence
Terraform Value Types Tutorial
Terraform count
Explained with Practical Examples
Exploring Terraform dynamic
Blocks with GCP Examples
Working with External Data in Terraform
Handling Sensitive and Ephemeral Data in Terraform
Terraform Modules FAQ