Terraform - 반복·컬렉션·조건

도은호·2025년 9월 23일

terraform

목록 보기
15/32

1) for_each — “키 기반으로 여러 개 찍어내기”

이론

  • for_each = <map|set> 형태. 키(주소)가 안정적이라 중간 삭제/추가에도 나머지 인스턴스가 흔들리지 않음.
  • 블록 내부에서 each.key / each.value 사용.

예제 (map로 파일 2개 생성)

main.tf

resource "local_file" "abc" {
  for_each = {
    a = "content a"
    b = "content b"
  }
  content  = each.value
  filename = "${path.module}/${each.key}.txt"
}

확인

terraform init
terraform plan -out=plan.bin
terraform apply plan.bin
# 결과 확인
# mac/linux
ls -1 *.txt && cat a.txt && cat b.txt
# windows (PowerShell)
Get-ChildItem *.txt; Get-Content .\a.txt; Get-Content .\b.txt

b를 지우고 다시 apply 하면 b.txt만 destroy 됨(안정 키).


2) for_each 체인 — “앞 리소스를 다음 리소스의 입력으로”

이론

  • for_each로 만든 리소스 집합은 “키 → 리소스 인스턴스” 맵이 된다.
  • 그 맵을 **다음 리소스의 for_each**로 넘겨 재사용 가능.

예제 (code 3-55 스타일)

variable "names" {
  default = {
    a = "content a"
    b = "content b"
    c = "content c"
  }
}

resource "local_file" "abc" {
  for_each = var.names
  content  = each.value
  filename = "${path.module}/abc-${each.key}.txt"
}

resource "local_file" "def" {
  for_each = local_file.abc        # 앞 리소스 맵 그대로 반복
  content  = each.value.content    # abc의 content를 재사용
  filename = "${path.module}/def-${each.key}.txt"
}

확인(예상 결과)

abc-a.txt → content a
abc-b.txt → content b
abc-c.txt → content c
def-a.txt → content a
def-b.txt → content b
def-c.txt → content c

names에서 b 제거 후 applyabc/def의 b만 destroy (add/change 없이 destroy만 표시될 수 있음).


3) listset으로 바꿔 for_each 돌리기

이론

  • for_eachmap/set 권장. 리스트는 키가 없어 인덱스 변동으로 재생성 위험.
  • 리스트를 쓴다면 **toset(list)**로 변환해 키=값 셋으로 만든다.

예제

resource "local_file" "abc" {
  for_each = toset(["a","b","c"])  # list → set
  content  = each.value            # set는 key==value
  filename = "${path.module}/abc-${each.key}.txt"
}

확인

  • abc-a.txt / abc-b.txt / abc-c.txt 생성 확인.

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

이론

  • 리스트 변환: [for v in xs : expr]
  • 필터: [for v in xs : expr if cond]
  • 맵 만들기: { for k,v in m : newk => newv }
  • 그룹핑: { for k,v in m : key(v) => value(v)... }...같은 키를 리스트로 묶음.

예제 A) 리스트 대문자 변환을 파일에 JSON으로 저장

variable "names" { default = ["a","b","c"] }

resource "local_file" "abc" {
  content  = jsonencode([for s in var.names : upper(s)])
  filename = "${path.module}/abc.json"
}

jsonencode()로 구조를 문자열로 바꿔 파일에 쓴다(그대로 쓰면 타입 에러).

예제 B) 출력으로 눈으로 확인

variable "names" { type = list(string); default = ["a","b"] }

output "A_upper" {
  value = [for v in var.names : upper(v)]
}

output "B_index_and_value" {
  value = [for i, v in var.names : "${i} is ${v}"]
}

output "C_make_object" {
  value = { for v in var.names : v => upper(v) }
}

output "D_with_filter" {
  value = [for v in var.names : upper(v) if v != "a"]
}

확인

terraform apply -auto-approve
terraform output

5) map/object 컴프리헨션 — “현실 데이터 가공”

이론

  • map(object(...))를 받아 특정 필드만 추리거나, 조건으로 필터하거나, 역으로 그룹핑 가능.

예제

variable "members" {
  type = map(object({
    role  = string
    group = string
  }))
  default = {
    ab = { role = "member", group = "dev" }
    cd = { role = "admin",  group = "dev" }
    ef = { role = "member", group = "ops" }
  }
}

output "A_to_tuple" {
  value = [for k, v in var.members : "${k} is ${v.role}"]
}

output "B_only_admin" {
  value = {
    for name, user in var.members : name => user.role
    if user.role == "admin"
  }
}

output "C_grouped_by_role" {
  value = {
    for name, user in var.members : user.role => name...
  }
}

포인트

  • ...을 쓰면 동일 키(role) 하위에 값 리스트로 그룹.

6) dynamic 블록 — “중첩 블록 반복 생성”

이론

  • 보안그룹의 ingress 같은 중첩 블록을 데이터 기반으로 반복 생성할 때 사용.
  • 대안: 전용 리소스 타입(예: aws_security_group_rule)을 for_each로 여러 개 생성 — 변경 추적이 더 명확할 때가 많음.

예제(개념용, AWS 필요)

locals {
  ingress_rules = {
    ssh  = { port = 22,  cidr = "0.0.0.0/0" }
    http = { port = 80,  cidr = "0.0.0.0/0" }
    tls  = { port = 443, 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]
    }
  }
}

7) 조건식 & null — “조건부 생성/속성”

이론

  • 삼항: cond ? A : B
  • 속성에 null을 주면 “미설정”으로 취급(옵션 토글에 유용).

예제

variable "enabled" { type = bool; default = true }
variable "use_user_data" { type = bool; default = false }

resource "aws_eip" "nat" {
  count  = var.enabled ? 1 : 0      # 0/1 생성 토글
  domain = "vpc"
}

resource "aws_instance" "web" {
  # ...
  user_data = var.use_user_data ? file("${path.module}/init.sh") : null
}

8) sensitive & 프로비저너 — “노출 최소화, 최후의 수단”

이론

  • sensitive = true / sensitive()콘솔·로그 마스킹(암호화 아님 → 상태 백엔드 보안은 별도로)
  • local-exec 등 프로비저너는 로그에 비밀이 찍힐 수 있음 → 가능하면 UserData/이미지/구성관리로 대체.

예제(로컬)

variable "secret" {
  type      = string
  sensitive = true
  default   = "top-secret"
}

resource "local_file" "foo" {
  filename = "${path.module}/foo.txt"
  content  = upper(var.secret)

  provisioner "local-exec" {
    command = "echo Deleting ${self.filename}"
    when    = destroy
  }
}

9) 실습 공통 명령 모음

terraform init
terraform fmt -check
terraform validate
terraform plan -out=plan.bin
terraform apply plan.bin
terraform output
# 정리
terraform destroy -auto-approve

10) 체크리스트

  • 여러 개 만들 땐 for_each(키 안정), 단순 개수만이면 count
  • 리스트는 **toset()**으로 바꿔 for_each
  • 값 가공/필터는 for 표현식
  • 중첩 블록 반복은 dynamic 또는 전용 리소스
  • 비밀은 sensitive/sensitive_content, 상태 백엔드 보안 별도
  • 적용은 항상 plan -out → apply plan.bin 패턴
profile
`•.¸¸.•´´¯`••._.• 🎀 𝒸𝓇𝒶𝓏𝓎 𝓅𝓈𝓎𝒸𝒽💞𝓅𝒶𝓉𝒽 🎀 •._.••`¯´´•.¸¸.•`

0개의 댓글