테라폼 코드 레포
GitHub - seatbelt92/learn-terraform: for learning terraform
for learning terraform. Contribute to seatbelt92/learn-terraform development by creating an account on GitHub.
github.com
1. 반복문
테라폼에서 반복문은 다음과 같은 요소들로 표현할 수 있다.
- count 매개 변수: 리소스 반복
- for_each 표현식: 리소스 내에서 리소스 및 인라인 블록 반복
- for 표현식: 리스트와 맵을 반복
- for 문자열 지시어: 문자열 내에서 리스트와 맵을 반복
1.1. count
count 매개 변수는 리소스를 반복해서 생성할 때 사용한다. 이때, 생성되는 리소스는 배열이 된다. count 없이 생성된 매개변수의 경우, id, arn 등의 속성에 접근할 때 <PROVIDER><TYPE>.<NAME>.<ATTRIBUTE> 이지만, count를 사용할 때는 배열이 되므로 <PROVIDER><TYPE>.<NAME>[INDEX].<ATTRIBUTE>로 접근해야 한다.
IAM 유저를 생성하는 경우로 사용법을 살펴보자. 여러 명의 유저를 생성할 때는 count와 생성된 인덱스의 인덱스틀 나타내는 count.index를 사용할 수 있다.
resource "aws_iam_user" "count_3" {
count = 3
name = "neo.${count.index}"
}
만약 이름을 개별적으로 지정하고 싶다면, list 타입의 변수를 설정하고, 이것의 길이를 length 함수로 표현하여 count에 넣고, count.index로 각 이름을 지정할 수 있다.
variable "user_names" {
description = "IAM user name list"
type = list(string)
default = ["neo", "trinity", "morpheus"]
}
resource "aws_iam_user" "var_count_3" {
count = length(var.user_names)
name = var.user_names[count.index]
}
앞서 언급했던 것처럼 count를 통해 생성된 리소스는 배열이므로, output을 나타낼 때는 index를 나타내준 뒤, 속성을 가져올 수 있다. 애스터리스트(*)를 사용할 경우, 해당 속성만으로 이루어진 배열을 가지고 올 수 있다.
output "neo_arn" {
value = aws_iam_user.var_count_3[0].arn
description = "ARN for neo"
}
output "all_arns" {
value = aws_iam_user.var_count_3[*].arn
description = "ARN for all users"
}
* 제한사항
- count는 인라인 블록에서는 사용할 수 없다.
- 변경 시 처음 값과 매칭이 되지 않는다.예를 들어, 3명의 사용자 중 중간에 있는 사용자를 삭제하는 경우, 삭제되기 기대했던 두 번째 유저가 아니라 세 번째 유저가 삭제되고, 두 번째 유저에게 세 번째 유저의 이름이 할당되는 결과를 얻게 되는 것이다.
- 배열 형태이므로, 인덱스를 기준으로 리소스가 매칭되기 때문에 기대한 것과 다른 결과를 얻을 수 있다.
이런 제한 사항을 해결하기 위해 테라폼은 for_each를 도입했다.
1.2. for_each
for_each를 사용하면 리스트, 집합, 맵을 사용하여 전체 리소스의 여러 복사본 또는 리소스 내 인라인 블록의 여러 본사본을 생성할 수 있다.
앞서 예시를 들었던 IAM 사용자 생성을 for_each로 변경해서 살펴보겠다. 리스트인 var.user_names를 집합(set)으로 변환하기 위해 toset을 사용했다. 그리고 for_each를 사용하면 집합의 값에 each.value로 접근할 수 있다.
resource "aws_iam_user" "for_each_3" {
for_each = toset(var.user_names)
name = each.value
}
for_each를 통해 생성된 결과는 리소스 배열이 아니라 리소스 맵이 된다. 출력을 하면, 설정한 리소스의 이름을 키값으로 갖는 맵 형태의 리소스를 확인할 수 있다. 만약 arn 등의 속성만 추출하고 싶다면, 맵에서 값만 반환하는 내장 함수 values를 사용해야 한다. 더불어, 맵 형태이므로, 리소스를 변경했을 때도 그에 해당하는 리소스에 변경 사항이 적용된다.
output "all_users" {
value = aws_iam_user.for_each_3
description = "Set of all users"
}
## output 출력값
all_users = {
"morpheus" = {...}
"neo" = {...}
"trinity" = {...}
}
# arn 추출
output "all_users_arn" {
value = values(aws_iam_user.for_each_3)[*].arn
description = "ARN for all users"
}
dynamic을 사용해서 인라인 블록을 동적을 생성하는 것도 가능하다. dynamic에 이어서 붙인 이름이 반복 변수(코드에서 'tag')이므로 key, value를 입력할 때도 each가 아니라 설정된 반복 변수 tag를 입력한다.
variable "custom_tags" {
type = map(string)
default = {}
}
resource "aws_autoscaling_group" "example" {
...
dynamic "tag" {
for_each = var.custom_tags
content {
key = tag.key
value = tag.value
propagate_at_launch = true
}
}
}
1.3 for 표현식
for 문의 기본 표현식은 다음과 같다. <LIST>는 반복할 리스트, <ITEM>은 리스트의 항목을 나타내는 지역 변수, <OUTPUT>은 ITEM을 변환하는 표현식이다.
[for <ITEM> in <LIST> : <OUTPUT>]
예를 들어, 유저 이름을 대문자로 나타내는 것은 다음과 같이 표현 가능하다.
output "upper_names" {
value = [for name in var.user_names : upper(name)]
}

조건을 지정하여 결과값을 필터링할 수도 있다. 아래는 글자수가 5 미만인 이름을 필터링하는 예시다.
output "short_upper_names" {
value = [for name in var.user_names : upper(name) if length(name) < 5]
}
대상이 map일 경우, 키벨류를 이용하여 맵을 반복할 수도 있다.
variable "user_bios" {
description = "map"
type = map(string)
default = {
neo = "hero"
morpheus = "mentor"
trinity = "shooter"
}
}
output "user_bios" {
value = [for name, role in var.user_bios : "${name} is the ${role}"]
}

1.4. 문자열 지시자
백분율 기호(%)를 사용하여 문자열 지시자를 사용할 수 있다. 기본 형태와 사용법은 아래와 같다. 물결표(~)는 문자열 지시자 앞이나 뒤에 붙여서 줄바꿈 등의 공백을 제거한다.
# 기본형태
%{for <ITEM> in <COLLECTION>}<BODY>%{endfor}
# 적용
output "for_directive" {
value = <<EOF
%{~for name in var.user_names}
${name}
%{~endfor}
EOF
}
2. 조건문
2.1. count
count 매개 변수가 0일 경우 리소스가 만들어지지 않는다. 그리고 테라폼에서는 삼항 연산자를 사용할 수 있다. 이를 활용하면 아래와 같은 코드를 구성할 수 있다.
variable "enable" {
type = bool
}
resource "aws_iam_user" "iam_user" {
count = var.enable ? 1 : 0
name = "neo"
}
# 문자열의 첫 번째 값 비교
resource "aws_iam_user" "start_with_n" {
count = format("%.1s", var.user_name) == "n" ? 1 : 0
name = var.user_name
}
if-else 문법도 count를 통해 표현할 수 있다. 리소스 2개를 마련하고 각각을 동일한 삼항 연산자의 결과로 다른 count를 도출하게 하는 것이다.
variable "is_developer" {
type = bool
}
resource "aws_iam_user" "user_devloper" {
count = var.is_developer ? 1 : 0
name = "neo"
tags = {
developer = "true"
}
}
resource "aws_iam_user" "user_general" {
count = var.is_developer ? 0 : 1
name = "neo"
tags = {
developer = "false"
}
}
2.2. for_each와 for 표현식
앞서 dynamic을 사용해서 인라인 블록을 동적으로 생성한 케이스에서, for 표현식을 사용하면 결과 형태 및 필터링까지 조합할 수도 있다.
resource "aws_autoscaling_group" "example" {
...
dynamic "tag" {
for_each = {
for key, value in var.custom_tags:
key => upper(value)
if key != "Name"
}
content {
key = tag.key
value = tag.value
propagate_at_launch = true
}
}
}
2.3. If 문자열 지시자
문자열 지시자를 통해 if 조건문, if-else 조건문을 표현할 수 있다.
variable "name" {
description = "A name to render"
type = string
default = ""
}
output "if_directive" {
value = "Hello, %{if var.name != ""}${var.name}%{endif}"
}
output "if_else_directive" {
value = "Hello, %{if var.name != ""}${var.name}%{else}{unnamed}%{endif}"
}

3. 테라폼의 주의 사항
3.1. count와 for_each의 제한 사항
count나 for_each는 plan 단계 전에 계산할 수 있어야 하므로, random_integer 리소스처럼 정해지지 않은 값은 넣을 수 없다. 아래처럼 구성하면 에러가 난다.
resource "random_integer" "num_instance" {
min = 1
max = 3
}
resource "aws_instance" "example" {
count = random_integer.num_instance.result
ami = "ami-0d5bb3742db8fc264"
instance_type = "t2.micro"
}
모듈 구성 내에서도 count, for_each 사용은 가능하나, 리소스 출력값이 배열 또는 맵이므로 module 값을 사용할 때는 형식에 맞는지 주의해서 사용해야 한다.
3.2. 유효한 plan의 실패
테라폼은 상태 파일만 검증하므로, 실제로 클라우드에 존재하는 리소스와 이름이 겹치는 케이스 등에서 충돌이 발생할 수 있다. 이를 방지하기 위해서는 테라폼을 사용했으면, 테라폼만 사용하고 기존 인프라가 있는 경우 import를 해서 상태 파일을 업데이트 해야 한다.
3.4. 리팩터링의 까다로움
항상 plan 사용해서 리소스 변경사항 체크하고, 리소스를 삭제하기 전에 생성부터 하는 create_before_destroy 수명주기 설정을 해준다. 파일에서 식별자를 변경한다면 상태 파일의 이름을 변경하는 terraform state mv <기존> <변경> 명령어를 사용하고, terraform plan으로 변경사항이 없는지 확인한다. 또한 일부 매개변수는 변경이 불가하기 때문에, 삭제 후 생성해야 한다. terraform plan으로 무엇이 삭제되는지 파악하고 진행하자.
참고자료
- https://registry.terraform.io/providers/hashicorp/aws/latest/docs
- Terraform UP&Running, 예브게니 브릭만, 루비페이퍼
'Dev > 인프라' 카테고리의 다른 글
AWS SAM 사용 시, 테라폼으로 인프라 구성하기 (0) | 2025.05.22 |
---|---|
[Terraform] 테라폼 기본 사용법 (0) | 2025.05.10 |
OpenVPN으로 AWS RDS 접속하기 (0) | 2025.05.01 |
아임웹과 AWS를 연결해서 사용하기 & Route53 이관 (0) | 2025.04.25 |
DNS 이해하기 (0) | 2025.04.23 |