When should you use for_each and count?
Introduction
Thank you for clicking through to my arcticle. I've been a DevOps engineer for 2 years in dev-team of 7 engineers.
My name is MINSEOK, LEE, but I use Unchaptered as an alias on the interenet. So, you can call me anythings "MINSEOK, LEE" or "Unchaptered" to ask something.
Topics
What is the difference for_each and count
When should you use for_each and count?
What is the difference for_each and count?
Jen's Space - [Terraform] count vs for_each Meta-Argument 차이점 - 변수 list(object) 타입 사용
You shouldn't use "count" if the length of array is likely to change.
Generally, you may to use "for_each".
For more information, see "count" case 1, 2 and "for_each" case 1, 2.
The "count" case 1
When length is 3 and you change 2nd value, terraform recreate only 1 resource.
> Plan : 1 to add, 0 to change, 1 to destroy
AS-IS
variable "subnets" { type = list(object({ name = string cidr_block = string })) default = [ { name = "subnet-1" cidr_block = "30.0.1.0/24" }, { name = "subnet-2" cidr_block = "30.0.2.0/24" # ✅ }, { name = "subnet-3" cidr_block = "30.0.3.0/24" } ] }
TO-BE
variable "subnets" { type = list(object({ name = string cidr_block = string })) default = [ { name = "subnet-1" cidr_block = "30.0.1.0/24" }, { name = "subnet-2" cidr_block = "30.0.4.0/24" # ✅ }, { name = "subnet-3" cidr_block = "30.0.3.0/24" } ] }
Plan : 1 to add, 0 to change, 1 to destroy
The "count" case 2
When length is 3 and you remove 2nd element, terraform recreate only 1 resource and destroy 1 resource more.
> Plan : 1 to add, 0 to change, 2 to destroy
AS-IS
variable "subnets" { type = list(object({ name = string cidr_block = string })) default = [ { name = "subnet-1" cidr_block = "30.0.1.0/24" }, { name = "subnet-2" cidr_block = "30.0.2.0/24" # ✅ }, { name = "subnet-3" cidr_block = "30.0.3.0/24" } ] }
TO-BE
variable "subnets" { type = list(object({ name = string cidr_block = string })) default = [ { name = "subnet-1" cidr_block = "30.0.1.0/24" }, { name = "subnet-3" cidr_block = "30.0.3.0/24" } ] }
Plan : 1 to add, 0 to change, 2 to destroy
Changes to Outputs: ~ aws_subnet_ids = [ "subnet-abcd1234efgh5678i", - "subnet-abcd1234efgh5678i", - "subnet-abcd1234efgh5678i", + (known after apply), ]
The "for_each" case 1
When length is 3 and you remove "subnet-2", terrafrom remove 1 resource.
> Plan: 0 to add, 0 to change, 1 to destroy.
AS-IS
# [Variable] variable "profile" { type = string default = "eksprac" } variable "subnets" { type = list(object({ name = string cidr_block = string })) default = [ { name = "subnet-1" cidr_block = "30.0.1.0/24" }, { name = "subnet-2" cidr_block = "30.0.2.0/24" }, { name = "subnet-3" cidr_block = "30.0.3.0/24" } ] } # [Provider] provider "aws" { profile = var.profile } # [Resource] resource "aws_vpc" "aws_vpc" { cidr_block = "30.0.0.0/16" } resource "aws_subnet" "aws_subnets" { for_each = { for subnet in var.subnets : subnet.name => subnet.cidr_block } vpc_id = aws_vpc.aws_vpc.id cidr_block = each.value tags = { Name = each.key } } # [Output] output "aws_subnet_ids" { value = { for k, v in aws_subnet.aws_subnets : v.tags.Name => v.id } }
TO-BE
# [Variable] variable "profile" { type = string default = "eksprac" } variable "subnets" { type = list(object({ name = string cidr_block = string })) default = [ { name = "subnet-1" cidr_block = "30.0.1.0/24" }, { name = "subnet-3" cidr_block = "30.0.3.0/24" } ] } # [Provider] provider "aws" { profile = var.profile } # [Resource] resource "aws_vpc" "aws_vpc" { cidr_block = "30.0.0.0/16" } resource "aws_subnet" "aws_subnets" { for_each = { for subnet in var.subnets : subnet.name => subnet.cidr_block } vpc_id = aws_vpc.aws_vpc.id cidr_block = each.value tags = { Name = each.key } } # [Output] output "aws_subnet_ids" { value = { for k, v in aws_subnet.aws_subnets : v.tags.Name => v.id } }
Plan: 0 to add, 0 to change, 1 to destroy.
Changes to Outputs: ~ aws_subnet_ids = { - subnet-2 = "subnet-05d79581d967cd923" # (2 unchanged attributes hidden) }
The "for_each" case 2
When length is 3 and you change "subnet-2" to "subnet-5", terrafrom recreate 1 resource.
> Plan: 1 to add, 0 to change, 1 to destroy.
AS-IS
# [Variable] variable "profile" { type = string default = "eksprac" } variable "subnets" { type = list(object({ name = string cidr_block = string })) default = [ { name = "subnet-1" cidr_block = "30.0.1.0/24" }, { name = "subnet-2" # 😊 cidr_block = "30.0.2.0/24" }, { name = "subnet-3" cidr_block = "30.0.3.0/24" } ] } # [Provider] provider "aws" { profile = var.profile } # [Resource] resource "aws_vpc" "aws_vpc" { cidr_block = "30.0.0.0/16" } resource "aws_subnet" "aws_subnets" { for_each = { for subnet in var.subnets : subnet.name => subnet.cidr_block } vpc_id = aws_vpc.aws_vpc.id cidr_block = each.value tags = { Name = each.key } } # [Output] output "aws_subnet_ids" { value = { for k, v in aws_subnet.aws_subnets : v.tags.Name => v.id } }
TO-BE
# [Variable] variable "profile" { type = string default = "eksprac" } variable "subnets" { type = list(object({ name = string cidr_block = string })) default = [ { name = "subnet-1" cidr_block = "30.0.1.0/24" }, { name = "subnet-5" # 😊 cidr_block = "30.0.2.0/24" }, { name = "subnet-3" cidr_block = "30.0.3.0/24" } ] } # [Provider] provider "aws" { profile = var.profile } # [Resource] resource "aws_vpc" "aws_vpc" { cidr_block = "30.0.0.0/16" } resource "aws_subnet" "aws_subnets" { for_each = { for subnet in var.subnets : subnet.name => subnet.cidr_block } vpc_id = aws_vpc.aws_vpc.id cidr_block = each.value tags = { Name = each.key } } # [Output] output "aws_subnet_ids" { value = { for k, v in aws_subnet.aws_subnets : v.tags.Name => v.id } }
Plan: 1 to add, 0 to change, 1 to destroy.
Changes to Outputs: ~ aws_subnet_ids = { - subnet-2 = "subnet-05d79581d967cd923" + subnet-5 = (known after apply) # (2 unchanged attributes hidden) }
When should you use for_each and count?
However, both for_each and count have the issue that the resource is dependent on the array. So for resources that you won't touch once created, it might be better to create them manually.