해당 내용은 cloudNet@ 팀의 가시다 님이 진행하는 테라폼 스터디 T101 4기에서 다룬 내용과 "테라폼으로 시작하는 IaC" (한빛미디어) 저서 내용을 정리한 것입니다.
9. 반복문
for_each
- for-each는 반복(for)을 할 때 타입 값에 대해 하나하나 each object로 접근한다는 의미입니다.
- each object는 key, value 2개의 속성을 가지고 있습니다.
아래 속성은 for_each가 선언된 블록에서만 사용할 수 있다.
- each.key
- each.value
- map, set 타입에 대해서만 each object로 접근할 수 있다.
map, set 타입이 아닌 expression은 toset, tomap 등을 사용해서 타입 변환을 해야한다. - 예제코드
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 && terraform apply -auto-approve
terraform state list
terraform state show 'local_file.abc["a"]'
terraform state show 'local_file.abc["b"]'
for_each : count 와 비교
- 예제 코드
variable "users" {
default = [
"smlim",
"yjna",
"jwlee",
"nykim"
]
}
resource "local_file" "t1014" {
count = length(var.users)
content = "This is file name is ${count.index}-${var.users[count.index]}.txt"
filename = "${path.module}/${count.index}-${var.users[count.index]}.txt"
}
- count 값을 var.users의 길이로 참조하였고, 인덱스로 변수에 접근을 하면, 0-smlim.txt / 1-yjna.txt / 2-jwlee.txt / 3-nykim.txt 파일이 생성된다.
- 이 때 1-yjna.txt 파일만 삭제하고 싶어서 variable 에서 yjna를 삭제하게 되면 jwlee, nykim 의 인덱스도 변경이 되어서 파일명이 유지가 되지 않는다.
- 이 때 variable 을 object 형태로 선언한 후 for_each 를 사용한다.
variable "users" {
default = {
"1": "smlim",
"2": "yjna",
"3": "jwlee",
"4": "nykim"
}
}
resource "local_file" "t1014" {
for_each = tomap(var.users)
content = "This file name is ${each.value}.txt"
filename = "${path.module}/${each.key}-${each.value}.txt"
}
- 이 때 variable 에서 2 : "yjna" 를 삭제하면 인덱스 값을 유지한 상태에서 특정 파일만 삭제할 수 있다.
for Expression
- 예를 들어 list 값의 포맷을 변경하거나 특정 접두사 prefix를 추가할 수도 있고, output에 원하는 형태로 반복적인 결과를 표현할 수 도 있다.
- list 타입의 경우 값 또는 인덱스와 값을 반환
- map 타입의 경우 키 또는 키와 값에 대해 반환
- set 타입의 경우 키 값에 대해 반환
- 예제 코드
variable "names" {
default = ["a", "b", "c"]
}
# variable 에 있는 값들을 모두 대문자로 파일에 쓰는 것이 목표
resource "local_file" "abc" {
content = jsonencode([for s in var.names : upper(s)]) # 결과 : ["A", "B", "C"]
filename = "${path.module}/abc.txt"
}
output "file_content" {
value = local_file.abc.content
}
- for 구문을 사용하는 몇 가지 규칙은 다음과 같다
- list 유형의 경우 반환 받는 값이 하나로 되어 있으면 값을, 두 개의 경우 앞의 인수가 인덱스를 반환하고 뒤의 인수가 값을 반환
- 관용적으로 인덱스는 i, 값은 v로 표현
- map 유형의 경우 반환 받는 값이 하나로 되어 있으면 키를, 두 개의 경우 앞의 인수가 키를 반환하고 뒤의 인수가 값을 반환
- 관용적으로 키는 k, 값은 v로 표현
- 결과 값은 for 문을 묶는 기호가 **[ ]**인 경우 tuple로 반환되고 **{ }**인 경우 object 형태로 반환
- object 형태의 경우 키와 값에 대한 쌍은 ⇒ 기호로 구분
- { } 형식을 사용해 object 형태로 결과를 반환하는 경우 키 값은 고유해야 하므로 값 뒤에 그룹화 모드 심볼(…)를 붙여서 키의 중복을 방지(SQL의 group by 문 또는 Java의 MultiValueMap과 같은 개념)
- if 구문을 추가해 조건 부여 가능
- list 유형의 경우 반환 받는 값이 하나로 되어 있으면 값을, 두 개의 경우 앞의 인수가 인덱스를 반환하고 뒤의 인수가 값을 반환
variable "names" {
type = list(string)
default = ["a", "b"]
}
output "A_upper_value" {
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"]
}
- 악분님 for expression 실습
variable "fruits_set" {
type = set(string)
default = ["apple", "banana"]
description = "fruit example"
}
variable "fruits_list" {
type = list(string)
default = ["apple", "banana"]
description = "fruit example"
}
variable "fruits_map" {
type = map(string)
default = {"first": "apple", "second": "banana"}
description = "fruit example"
}
for item in var.fruits_set: item
[for item in var.fruits_set: item]
type([for item in var.fruits_set: item])
{for item in var.fruits_set: item}
{for key,value in var.fruits_set: key => value}
type({for key,value in var.fruits_set: key => value})
#
for item in var.fruits_list: item
[for item in var.fruits_list: item]
type([for item in var.fruits_list: item])
{for item in var.fruits_list: item}
{for key,value in var.fruits_list: key => value}
{for i, v in var.fruits_list: i => v}
type({for i, v in var.fruits_list: i => v})
#
for item in var.fruits_map: item
[for item in var.fruits_map: item]
type([for item in var.fruits_map: item])
{for item in var.fruits_map: item}
{for key,value in var.fruits_map: key => value}
{for k, v in var.fruits_map: k => v}
type({for k, v in var.fruits_map: k => v})
dynamic
리소스 내부 속성 블록을 동적인 블록으로 생성
- count 나 for_each 구문을 사용한 리소스 전체를 여러 개 생성하는 것 이외도 리소스 내에 선언되는 구성 블록을 다중으로 작성해야 하는 경우가 있다.
- 예를 들면 AWS Security Group 리소스 구성에 ingress, egress 요소가 리소스 선언 내부에서 블록 형태로 여러 번 정의되는 경우다.
resource "aws_security_group" "example" {
name = "example-security-group"
description = "Example security group"
vpc_id. = aws_vpc.main.id
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
ipv6_cidr_blocks = ["::/0"]
}
}
- 리소스 내의 블록 속성(Attributes as Blocks)은 리소스 자체의 반복 선언이 아닌 내부 속성 요소 중 블록으로 표현되는 부분에 대해서만 반복 구문을 사용해야 하므로, 이때 dynamic 블록을 사용해 동적인 블록을 생성 할 수 있다.
- dynamic 블록을 작성하려면, 기존 블록의 속성 이름을 dynamic 블록의 이름으로 선언하고 기존 블록 속성에 정의되는 내용을 content 블록에 작성한다.
- 반복 선언에 사용되는 반복문 구문은 for_each를 사용한다. 기존 for_each 적용 시 each 속성에 key, value가 적용되었다면 dynamic에서는 dynamic에 지정한 이름에 대해 속성이 부여된다.
- 예제 코드
data "archive_file" "dotfiles" {
type = "zip"
output_path = "${path.module}/dotfiles.zip"
source {
content = "hello a"
filename = "${path.module}/a.txt"
}
source {
content = "hello b"
filename = "${path.module}/b.txt"
}
source {
content = "hello c"
filename = "${path.module}/c.txt"
}
}
반복되는 source 선언을 dynamic 블록을 사용해서 간소화
variable "names" {
default = {
a = "hello a"
b = "hello b"
c = "hello c"
}
}
data "archive_file" "dotfiles" {
type = "zip"
output_path = "${path.module}/dotfiles.zip"
dynamic "source" {
for_each = var.names
content {
content = source.value
filename = "${path.module}/${source.key}.txt"
}
}
}
'DevOps' 카테고리의 다른 글
[T1014-이론] 4장 프로바이더 (0) | 2024.07.07 |
---|---|
[T1014-이론] 3장 기본 사용법 (6) (0) | 2024.06.30 |
[T1014-이론] 3장 기본 사용법 (4) (0) | 2024.06.22 |
[T1014-이론] 3장 기본 사용법 (3) (0) | 2024.06.20 |
[T1014-이론] 3장 기본 사용법 (2) (0) | 2024.06.19 |