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
(1)."
With a resource block this is quite straightforward; an example can be found here (2). What happens when these are used with a module?
Use case
Users configure count
or for_each
when they want multiple instances of a resource or an entire module. Below is an extremely simple example to get started and to see the difference in resource address. It is important to discuss the different syntax when considering refactoring code to use count
instead of for_each
and vice versa. The resource addresses recorded in state are not the same and the user will need to rename the resources.
Procedure
Example
Filesystem
$ tree
.
├── main.tf
└── module
└── src.tf
./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}" }
./module/src.tf
variable "name" {} resource "random_pet" "r" { prefix = var.name }
- Execute
terraform init
to initialize providers and these 2 modules. - Run
terraform plan
and see the outputPlan: 5 to add, 0 to change, 0 to destroy.
- Execute
terraform apply
and notice the different resource addresses. The module using count will reference the newly created resources using an index:module.using_count[0].random_pet.r
while the module usingfor_each
will reference them using the key provided:module.using_foreach["a"].random_pet.r
.
Considerations
What if you have a module that used count
and now you want to refactor to use for_each
? This would require a considerable amount of work. In the above example, the resource addresses are different and the name argument for random_pet
is generated using different information: count
uses the index and for_each
uses key/value.
In this case, your best course of action is to
- Make backups of your configuration files and terraform.tfstate file.
- Adjust your terraform configuration to use the new syntax.
- Remove the incorrect/old resources from the state file using
terraform state rm ADDRESS
(3) or the remove block (4). - Import them into state using the correct information and either the command
terraform import ADDRESS <value>
(5) or import block (6).
Additional info
- The count Meta-Argument
- The for_each Meta-Argument
- Terraform count versus for_each as it relates to resources.
- When to use count instead of for_each
- Error: Module is incompatible with count, for_each, and depends_on
Hyperlinks
(1) https://developer.hashicorp.com/terraform/language/style#dynamic-resource-count
(3) https://developer.hashicorp.com/terraform/cli/commands/state/rm
(4) https://developer.hashicorp.com/terraform/language/resources/syntax#removing-resources
(5) https://developer.hashicorp.com/terraform/cli/import
(6) https://developer.hashicorp.com/terraform/language/import