for_each — “키 기반으로 여러 개 찍어내기”for_each = <map|set> 형태. 키(주소)가 안정적이라 중간 삭제/추가에도 나머지 인스턴스가 흔들리지 않음.each.key / each.value 사용.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 됨(안정 키).
for_each 체인 — “앞 리소스를 다음 리소스의 입력으로”for_each로 만든 리소스 집합은 “키 → 리소스 인스턴스” 맵이 된다.for_each**로 넘겨 재사용 가능.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제거 후apply→ abc/def의 b만 destroy (add/change 없이 destroy만 표시될 수 있음).
list를 set으로 바꿔 for_each 돌리기for_each는 map/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 생성 확인.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)... }의 ...는 같은 키를 리스트로 묶음.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()로 구조를 문자열로 바꿔 파일에 쓴다(그대로 쓰면 타입 에러).
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
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) 하위에 값 리스트로 그룹.dynamic 블록 — “중첩 블록 반복 생성”ingress 같은 중첩 블록을 데이터 기반으로 반복 생성할 때 사용.aws_security_group_rule)을 for_each로 여러 개 생성 — 변경 추적이 더 명확할 때가 많음.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]
}
}
}
null — “조건부 생성/속성”cond ? A : Bnull을 주면 “미설정”으로 취급(옵션 토글에 유용).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
}
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
}
}
terraform init
terraform fmt -check
terraform validate
terraform plan -out=plan.bin
terraform apply plan.bin
terraform output
# 정리
terraform destroy -auto-approve
for_each(키 안정), 단순 개수만이면 counttoset()**으로 바꿔 for_eachfor 표현식dynamic 또는 전용 리소스sensitive/sensitive_content, 상태 백엔드 보안 별도plan -out → apply plan.bin 패턴