terraform 반복문-count, for_each, for, dynamic

도은호·2025년 9월 22일

terraform

목록 보기
14/32
  • 여러 개 리소스 찍어내기: count, for_each
  • 값 가공/필터링: for 표현식(list/map 컴프리헨션)
  • 중첩 블록 반복: dynamic 블록
  • countfor_each같은 블록에서 동시에 못 씀

1) count — “N개 찍어내기”(인덱스 기반)

# N개 퍼블릭 서브넷 (AZ 개수만큼)
variable "az_count" { default = 2 }

resource "aws_subnet" "public" {
  count                   = var.az_count
  vpc_id                  = aws_vpc.main.id
  availability_zone       = local.azs[count.index]     # 0,1,...
  cidr_block              = cidrsubnet(var.vpc_cidr, 8, count.index)
  map_public_ip_on_launch = true
}
# 참조: aws_subnet.public[0].id, aws_subnet.public[1].id
  • 장점: 간단, 개수만 알면 됨
  • 단점: 인덱스가 바뀌면 재생성(churn) 위험

조건부 생성(0 또는 1): count = var.enabled ? 1 : 0


2) for_each — “키 기반 반복”(맵/셋 권장)

# AZ별 CIDR을 키로 관리 → 변경에 강함
locals {
  public_subnets = {
    "ap-northeast-2a" = "10.0.1.0/24"
    "ap-northeast-2c" = "10.0.2.0/24"
  }
}

resource "aws_subnet" "public" {
  for_each                = local.public_subnets
  vpc_id                  = aws_vpc.main.id
  availability_zone       = each.key
  cidr_block              = each.value
  map_public_ip_on_launch = true
}
# 참조: aws_subnet.public["ap-northeast-2a"].id
  • 장점: 키가 안정적 → 항목 추가/삭제에도 재생성 최소화
  • 입력: map(...) 또는 set(string) 권장 (listtoset(list)로 변환해 사용)

모듈/데이터소스에도 동일하게 사용 가능:

module "svc" { for_each = var.services  name = each.key  spec = each.value }
data "aws_ami" "sel" { for_each = toset(var.names)  # ... }

3) for 표현식 — 값 가공/필터링(컴프리헨션)

# 리소스 목록 → ID만 추출 (count 케이스)
output "public_ids" {
  value = [for s in aws_subnet.public : s.id]
}

# for_each(맵) 케이스: values()로 값만 뽑아 다시 for
output "public_cidrs" {
  value = [for s in values(aws_subnet.public) : s.cidr_block]
}

# 필터 포함(for ... if ...)
locals {
  public_http_cidrs = [
    for r in var.rules : r.cidr
    if r.public && r.port == 80
  ]
}
  • 리스트/맵 → 다른 구조로 변환할 때 최고
  • if 절로 필터링 가능

4) dynamic 블록 — 중첩 블록 반복 생성

중첩 블록(예: SG의 ingress)을 데이터로부터 반복 생성.

locals {
  ingress_rules = {
    ssh  = { port = 22,  cidr = "0.0.0.0/0" }
    http = { port = 80,  cidr = "0.0.0.0/0" }
  }
}

resource "aws_security_group" "web" {
  name   = "web-sg"
  vpc_id = aws_vpc.main.id

  dynamic "ingress" {
    for_each = local.ingress_rules
    content {
      from_port   = ingress.value.port
      to_port     = ingress.value.port
      protocol    = "tcp"
      cidr_blocks = [ingress.value.cidr]
    }
  }
}
  • 언제? 같은 종류의 중첩 블록을 여러 개 만들어야 할 때
  • 대안: 해당 리소스가 중첩 블록용 전용 리소스 타입을 제공하면(aws_security_group_rule) 그걸 for_each로 돌리는 편이 더 명확할 때도 있음

5) 함께 쓰는 패턴

A) locals로 룩업 테이블 만들고 for_each

locals {
  itype_by_env = { dev = "t3.micro", prod = "t3.medium" }
  itype        = local.itype_by_env[var.env]
}
resource "aws_instance" "web" {
  instance_type = local.itype
  # ...
}

B) 조건부 속성 제거(루프는 아니지만 자주 동반)

resource "aws_instance" "web" {
  # ...
  user_data = var.enable_user_data ? file("${path.module}/init.sh") : null
  # null → 해당 인자 미설정으로 간주
}

C) 출력 모으기(리스트/맵 컴프리헨션)

output "subnet_ids" {
  value = [for s in aws_subnet.public : s.id]
}
output "subnet_by_az" {
  value = { for az, s in aws_subnet.public : az => s.id } # for_each 맵 그대로 재구성
}

6) 자주 하는 실수 & 베스트 프랙티스

  • countfor_each를 같은 블록에 동시 사용 → 불가
  • count 인덱스 변경으로 재생성 → ✅ for_each + 안정된 키(맵/셋) 사용
  • for_each에 리스트 직접 전달 → ✅ toset(list) 또는 으로 변환
  • splat(*)만 믿고 쓰기 → ✅ 0.12+에선 for 표현식이 더 명확
  • ✅ 반복 전 locals에 데이터 구조를 먼저 정리(가독성↑, 재사용↑)

7) 요약 표

목적방법포인트
N개 리소스count = Ncount.index, 인덱스 변경 주의
키 기반 여러 개for_each = map/seteach.key, each.value, 변경 안정
값 변환/필터[for x in xs : ... if ...] / {for k,v in m : ...}컴프리헨션
중첩 블록 반복dynamic "<block>" { for_each = ... }SG ingress 등
조건부 생성count = cond ? 1 : 00/1 토글
조건부 속성attr = cond ? val : nullnull이면 미설정

결론

여러 개 만들 땐 for_each(키 안정), 단순 개수count,
가공/필터링for 표현식, 중첩 블록dynamic으로!”

profile
`•.¸¸.•´´¯`••._.• 🎀 𝒸𝓇𝒶𝓏𝓎 𝓅𝓈𝓎𝒸𝒽💞𝓅𝒶𝓉𝒽 🎀 •._.••`¯´´•.¸¸.•`

0개의 댓글