Introduction
According to the Terraform Style Guide, "The for_each and count meta-arguments let you create multiple resources from a single resource block depending on run-time conditions. You can use these meta-arguments to make your code flexible and reduce duplicate resource blocks. If the resources are almost identical, use count. If some of arguments need distinct values that you cannot derive from an integer, use for_each."
Using these meta-arguments with a resource block is straightforward. An example of using these meta-arguments with resources is available in the Terraform count versus for_each article. This guide explains how to use count and for_each with modules.
Use Case
You can configure count or for_each to create multiple instances of an entire module. The following example demonstrates the syntax for each and highlights the difference in how Terraform addresses the resulting resources in the state file. Understanding this difference is important, especially when refactoring code from count to for_each or vice versa, as it requires state manipulation.
Procedure
Example Configuration
This example uses the following file structure.
$ tree
.
├── main.tf
└── module
└── src.tfCreate the main.tf file with module blocks that use count and for_each.
## ./main.tf
# COUNT
module "using_count" {
source = "./module"
count = 2
name = "test-${count.index}"
}
# FOR_EACH
variable "project" {
type = map(any)
default = {
a = {
some_num = 2,
env = "dev"
},
b = {
some_num = 1,
env = "stage"
}
}
}
module "using_foreach" {
source = "./module"
for_each = var.project
name = "test-${each.key}-${each.value.env}"
}Create the module/src.tf file with a resource that the modules will create.
## ./module/src.tf
variable "name" {}
resource "random_pet" "r" {
prefix = var.name
}Applying the Configuration
-
Initialize the Terraform configuration to download providers and configure the modules.
$ terraform init
-
Review the execution plan. Terraform will plan to create five resources (two modules, two
random_petresources, and one null resource from therandom_petprovider).$ terraform plan
The output shows the plan.
Plan: 5 to add, 0 to change, 0 to destroy.
-
Apply the configuration to create the resources. Note the different resource addresses in the output. The module using
countreferences resources with a numeric index, while the module usingfor_eachreferences them with the map key.$ terraform apply
-
countaddress example:module.using_count[0].random_pet.r -
for_eachaddress example:module.using_foreach["a"].random_pet.r
-
Refactoring from count to for_each
If you need to refactor a module that uses count to use for_each, you must perform state surgery because the resource addresses are different. In the example above, the name argument for the random_pet resource is also generated differently, which would cause the resource to be replaced.
To refactor safely, follow these steps.
- Create backups of your configuration files and the
terraform.tfstatefile. - Update your Terraform configuration to use the new
for_eachsyntax. - Remove the old resources from the state file using the
terraform state rmcommand or aremovedblock. - Import the existing resources back into state with their new addresses using the
terraform importcommand or animportblock.