3주차 공유 시작하겠습니다.
반복문
- for_each : 반복문, 선언된 key 값 개수만큼 리소스를 생성 - Docs Tutorials 악분 제약사항
- for-each는 반복(for)을 할 때 타입 값에 대해 하나하나 each object로 접근한다는 의미입니다.
- each object는 key, value 2개의 속성을 가지고 있습니다.
- each.key — The map key (or set member) corresponding to this instance.
- each.value — The map value corresponding to this instance. (If a set was provided, this is the same as each.key.)
- 하지만 for_each는 모든 타입에 대해 each object로 접근 할 수 없고 map, set타입만 허용합니다.
- map, set타입이 아닌 expression은 map 또는 set으로 타입변환(toset 등)을 해야합니다.
폴더 생성
mkdir 3.9 && cd 3.9
touch main.tf
- 리소스 또는 모듈 블록에서 for_each에 입력된 데이터 형태가 map 또는 set이면, 선언된 key 값 개수만큼 리소스를 생성하게 된다.
- Map - Link
resource "azurerm_resource_group" "rg" {
for_each = tomap({
a_group = "eastus"
another_group = "westus2"
})
name = each.key
location = each.value
}
Set of strings
resource "aws_iam_user" "the-accounts" {
for_each = toset(["Todd", "James", "Alice", "Dottie"])
name = each.key
}
main.tf 파일 수정 : for_each 값이 있는 반복문 동작 확인
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"]'
ls *.txt
cat a.txt ;echo
cat b.txt ;echo
#
echo "local_file.abc" | terraform console
echo 'local_file.abc["a"]' | terraform console
echo 'local_file.abc["a"].content' | terraform console
echo 'local_file.abc["b"].content' | terraform console
# VSCODDE 에서 terraform.tfstate 파일 확인 혹은 cat terraform.tfstate
...
"instances": [
{
"index_key": "a",
"schema_version": 0,
...
"index_key": "b",
"schema_version": 0,
...
- for_each가 설정된 블록에서는 each 속성을 사용해 구성을 수정할 수 있다
- each.key : 이 인스턴스에 해당하는 map 타입의 key 값
- each.value : 이 인스턴스에 해당하는 map의 value 값
- 생성되는 리소스의 경우 <리소스 타입>.<이름>[<key>], 모듈의 경우 module.<모듈 이름>[<key>]로 해당 리소스의 값을 참조한다.
- 이 참조 방식을 통해 리소스 간 종송석을 정의하기도 하고 변수로 다른 리소스에서 사용하거나 출력을 위한 결과 값으로 사용한다.
- main.tf 파일 수정 : local_file.abc는 변수의 map 형태의 값을 참조, local_file.def의 경우 local_file.abc 도한 결과가 map으로 반환되므로 다시 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
filename = "${path.module}/def-${each.key}.txt"
}
확인
#
terraform apply -auto-approve
terraform state list
terraform state show 'local_file.abc["a"]'
terraform state show 'local_file.def["a"]'
ls *.txt
#
echo "local_file.abc" | terraform console
echo 'local_file.abc["a"]' | terraform console
echo 'local_file.abc["a"].content' | terraform console
# VSCODDE 에서 terraform.tfstate 파일 확인 혹은 cat terraform.tfstate
...
"mode": "managed",
"type": "local_file",
"name": "def",
"provider": "provider[\"registry.terraform.io/hashicorp/local\"]",
"instances": [
{
"index_key": "a",
"schema_version": 0,
...
- key 값은 count의 index와는 달리 고유하므로 중간에 값을 삭제한 후 다시 적용해도 삭제한 값에 대해서만 리소스를 삭제한다.
- main.tf 파일 수정 : count 경우와 유사하게 중간 값을 삭제 후 확인
variable "names" {
default = {
a = "content a"
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
filename = "${path.module}/def-${each.key}.txt"
}
확인
#
terraform apply -auto-approve
terraform state list
local_file.abc["a"]
local_file.abc["c"]
local_file.def["a"]
local_file.def["c"]
ls *.txt
for_each : 데이터 유형
resource "aws_iam_user" "the-accounts" {
for_each = ["Todd", "James", "Alice", "Dottie"]
name = each.key
}
실패
# Jenkins 등에서 CD로 Terraform 수행 시, -no-color 활용
terraform plan -no-color
위에 보며면 map, set의 유형만 사용할 수 있다고 나와있습니다.
그래서
다시 수정
resource "aws_iam_user" "the-accounts" {
for_each = toset(["Todd", "James", "Alice", "Dottie"])
name = each.key
}
동작확인
데이터 유형 실습
- 기본 유형
- string : 글자 유형
- number : 숫자 유형
- bool : true 또는 false
- any : 명시적으로 모든 유형이 허용됨을 표시
- 집합 유형
- list [<유형>]: 인덱스 기반 집합
- map (<유형>): 값 = 속성 기반 집합이며 키값 기준 정렬
- set (<유형>): 값 기반 집합이며 정렬 키값 기준 정렬
- object ({<인수 이름>=<유형>, …})
- tuple ([<유형>, …])
- list와 set은 선언하는 형태가 비슷하지만 참조 방식이 인덱스와 키로 각각 차이가 있고, map와 set의 경우 선언된 값이 정렬되는 특징을 가진다.
main.tf
variable "string_a" {
default = "myString"
}
variable "string_b" {
type = string
default = "myString"
}
# variable "string_c" {
# type = string
# default = myString
# }
variable "number_a" {
default = 123
}
variable "number_b" {
type = number
default = 123
}
variable "number_c" {
default = "123"
}
variable "boolean" {
default = true
}
# (Array) list , set , tuple - value , [ ] 사용
variable "list_set_tuple_a" {
default = ["aaa", "bbb", "ccc"]
}
variable "list_set_tuple_b" {
type = list(string)
default = ["bbb", "ccc", "aaa"]
}
variable "list_set_tuple_c" {
type = set(string)
default = ["bbb", "ccc", "aaa"]
}
variable "list_set_tuple_d" {
default = ["aaa", 1, false]
}
variable "list_set_tuple_e" {
type = tuple([string, number, bool])
default = ["aaa", 1, false]
}
# (Object) map , object - key : value , { } 사용
variable "map_object_a" {
default = {"a" : "aaa", "b" : "bbb" , "c" : "ccc"}
}
variable "map_object_b" {
type = map(string)
default = {"b" : "bbb" , "c" : "ccc", "a" : "aaa"}
}
variable "map_object_c" {
default = {"name" : "gasida", "age" : 27 }
}
variable "map_object_d" {
type = object({ name = string, age = number })
default = {"name" : "gasida", "age" : 27 }
}
연습 시작
Map을 쓰려면 선언을 해줘야한다.
또한 'b'를 보면 키를 기준으로 정렬된다.
for_each : count 와 비교
resource "local_file" "abc" {
count = 3
content = "This is filename abc${count.index}.txt"
filename = "${path.module}/abc${count.index}.txt"
}
확인 : count 는 index 로 몇번에 어떤 내용이 있는지를 알 수 없다, 중간 리소스 삭제나 추가 시 문제 발생 가능.
for_each
- for_each 표현식을 사용하면 리스트 lists, 집합 sets, 맵 maps 를 사용하여 전체 리소스의 여러 복사본 또는 리소스 내 인라인 블록의 여러 복사본, 모듈의 복사본을 생성 할 수 있음
- 먼저 for_each 를 사용하여 리소스의 여러 복사본을 만드는 구문
- COLLECTION 은 루프를 처리할 집합 sets 또는 맵 maps
- 리소스에 for_each 를 사용할 때에는 리스트는 지원하지 않습니다.
- 그리고 CONFIG 는 해당 리소스와 관련된 하나 이상의 인수로 구성되는데 CONFIG 내에서 each.key 또는 each.value 를 사용하여 COLLECTION 에서 현재 항목의 키와 값에 접근할 수 있습니다.
- 예를 들어 다음은 for_each 를 사용하여 3명의 IAM 사용자를 생성하는 방법
- var.user_names 리스트를 집합(set)으로 변환하기 위해 toset 사용. for_each 는 리소스에 사용될 때는 집합과 맵만 지원.
- for_each 가 이 집합을 반복하면 each.value 에서 각 사용자 이름을 사용할 수 있습니다.
- main.tf : 일반적으로는 each.key 는 키/값 쌍 맵에서만 사용가능하지만, 사용자 이름은 each.key 에서도 사용할 수 있습니다.
provider "aws" {
region = "ap-northeast-2"
}
resource "aws_iam_user" "myiam" {
for_each = toset(var.user_names)
name = each.value
}
variable "user_names" {
description = "Create IAM users with these names"
type = list(string)
default = ["gasida", "akbun", "ssoon"]
}
output "all_users" {
value = aws_iam_user.myiam
}
- for_each 를 사용한 후에는 하나의 리소스 또는 count 를 사용한 것과 같은 리소스 배열이 되는 것이 아니리 리소스 맵 list into a set 이 됩니다.
for_each 를 사용해 리소스를 맵으로 처리하면 컬렉션 중간의 항목도 안전하게 제거할 수 있어서, count 로 리소스를 배열 처리보다 이점이 큽니다.
- variables.tf : var.user_names 리스트 중간에 값을 제거하고 plan 으로 확인
provider "aws" {
region = "ap-northeast-2"
}
variable "user_names" {
description = "Create IAM users with these names"
type = list(string)
default = ["gasida", "ssoon"]
}
resource "aws_iam_user" "myiam" {
for_each = toset(var.user_names)
name = each.value
}
output "all_users" {
value = aws_iam_user.myiam
}
- 이제 주변 모든 리소스를 옮기지 않고 정확히 목표한 리소스만 삭제가 됩니다.
- 따라서 리소스의 구별되는 여러 복사본을 만들 때는 count 대신 for_each 를 사용하는 것이 바람직합니다.
삭제
terraform destroy -auto-approve
aws iam list-users | jq
for Expressions : 복합 형식 값의 형태를 변환하는 데 사용 - Docs ← for_each와 다름
- 예를 들어 list 값의 포맷을 변경하거나 특정 접두사 prefix를 추가할 수도 있고, output에 원하는 형태로 반복적인 결과를 표현할 수 도 있다.
- list 타입의 경우 값 또는 인덱스와 값을 반환
- map 타입의 경우 키 또는 키와 값에 대해 반환
- set 타입의 경우 키 값에 대해 반환
- main.tf 파일 수정 : list의 내용을 담는 리소스를 생성, var.name의 내용이 결과 파일에 content로 기록됨
variable "names" {
default = ["a", "b", "c"]
}
resource "local_file" "abc" {
content = jsonencode(var.names) # 결과 : ["a", "b", "c"]
filename = "${path.module}/abc.txt"
}
output "file_content" {
value = local_file.abc.content
}
- 실행 후 확인 : jsonencode Function - 링크
- jsonencode encodes a given value to a string using JSON syntax.
#
terraform apply -auto-approve
terraform state list
terraform output --raw file_content | jq
ls *.txt
cat abc.txt ;echo
# 참고 jsonencode Function
terraform console
-----------------
var.names
type(var.names)
jsonencode(var.names)
jsonencode({"hello"="world"})
exit
-----------------
목표 : var.name 값을 일괄적으로 대문자로 변환!
main.tf 파일 수정 : content의 값 정의에 for 구문을 사용하여 내부 값을 일괄적으로 변경
variable "names" {
default = ["a", "b", "c"]
}
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
}
#
terraform apply -auto-approve
terraform state list
terraform output --raw file_content | jq
ls *.txt
cat abc.txt ;echo
# 참고 jsonencode Function
terraform console
-----------------
jsonencode([for s in var.names : upper(s)])
[for s in var.names : upper(s)]
[for txt in var.names : upper(txt)]
title("hello world")
exit
-----------------
for 구문을 사용하는 몇 가지 규칙은 다음과 같다
- list 유형의 경우 반환 받는 값이 하나로 되어 있으면 값을, 두 개의 경우 앞의 인수가 인덱스를 반환하고 뒤의 인수가 값을 반환
- 관용적으로 인덱스는 i, 값은 v로 표현
- map 유형의 경우 반환 받는 값이 하나로 되어 있으면 키를, 두 개의 경우 앞의 인수가 키를 반환하고 뒤의 인수가 값을 반환
- 관용적으로 키는 k, 값은 v로 표현
- 결과 값은 for 문을 묶는 기호가 **[ ]**인 경우 tuple로 반환되고 **{ }**인 경우 object 형태로 반환
- object 형태의 경우 키와 값에 대한 쌍은 ⇒ 기호로 구분
- { } 형식을 사용해 object 형태로 결과를 반환하는 경우 키 값은 고유해야 하므로 값 뒤에 그룹화 모드 심볼(…)를 붙여서 키의 중복을 방지(SQL의 group by 문 또는 Java의 MultiValueMap과 같은 개념)
- if 구문을 추가해 조건 부여 가능
main.tf
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"]
}
확인
#
terraform apply -auto-approve
terraform state list
#
terraform output
terraform output A_upper_value
terraform output D_with_filter
#
terraform console
-----------------
var.names
type(var.names)
# 리턴 타입 tuple : 값 v
[for v in var.names : upper(v)]
type([for v in var.names : upper(v)])
# 리턴 타입 tuple : 인덱스 i, 값 v
[for i, v in var.names : "${i} is ${v}"]
type([for i, v in var.names : "${i} is ${v}"])
# 리턴 타입 object : object 형태의 경우 키와 값에 대한 쌍은 ⇒ 기호로 구분
{ for v in var.names : v => upper(v) }
type({ for v in var.names : v => upper(v) })
# 조건문 활용 : f 구문을 추가해 조건 부여 가능
[for v in var.names : upper(v) if v != "a"]
type([for v in var.names : upper(v) if v != "a"])
exit
-----------------
main.tf 파일 수정 : map 유형에 대한 for 구문 처리의 몇 가지 예를 확인
variable "members" {
type = map(object({
role = string
}))
default = {
ab = { role = "member", group = "dev" }
cd = { role = "admin", group = "dev" }
ef = { role = "member", group = "ops" }
}
}
output "A_to_tupple" {
value = [for k, v in var.members : "${k} is ${v.role}"]
}
output "B_get_only_role" {
value = {
for name, user in var.members : name => user.role
if user.role == "admin"
}
}
output "C_group" {
value = {
for name, user in var.members : user.role => name...
}
}
확인
#
terraform apply -auto-approve
terraform state list
#
terraform output
terraform output A_to_tupple
terraform output B_get_only_role
terraform output C_group
#
terraform console
-----------------
var.members
type(var.members)
[for k, v in var.members : "${k} is ${v.role}"]
type([for k, v in var.members : "${k} is ${v.role}"])
{for name, user in var.members : name => user.role}
type({for name, user in var.members : name => user.role})
{for name, user in var.members : name => user.role if user.role == "admin"}
# { } 형식을 사용해 object 형태로 결과를 반환하는 경우 키 값은 고유해야 하므로 값 뒤에 그룹화 모드 심볼(…)를 붙여서 키의 중복을 방지
{for name, user in var.members : user.role => name...}
exit
-----------------
[악분님] for expression 실습 추가
- main.tf 파일 수정 : map 유형에 대한 for 구문 처리의 몇 가지 예를 확인
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"
}
확인
#
terraform console
-----------------
var.fruits_set
var.fruits_list
var.fruits_map
type(var.fruits_set)
type(var.fruits_list)
type(var.fruits_map)
#
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})
exit
-----------------
연습 시작
dynamic : 리소스 내부 속성 블록을 동적인 블록으로 생성 - Docs Blog
- count 나 for_each 구문을 사용한 리소스 전체를 여러 개 생성하는 것 이외도 리소스 내에 선언되는 구성 블록을 다중으로 작성해야 하는 경우가 있다.
- 예를 들면 AWS Security Group 리소스 구성에 ingress, egress 요소가 리소스 선언 내부에서 블록 형태로 여러 번 정의되는 경우다.
main.tf 파일 수정 : archive 프로바이더(링크)의 archive_file에 source 블록 선언을 반복 - 링크
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"
}
}
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"
}
}
}
조건문
[Conditional Expressions] 테라폼에서의 조건식은 3항 연산자 형태를 갖는다. 조건은 true 또는 false로 확인되는 모든 표현식을 사용할 수 있다 - 링크
mkdir 3.10 && cd 3.10
touch main.tf
- 일반적으로 비교, 논리 연산자를 사용해 조건을 확인한다.
- 조건식은 ? 기호를 기준으로 왼쪽은 조건이며, 오른쪽은 : 기호를 기준으로 왼쪽이 조건에 대해 true가 반환되는 경우이고 오른쪽이 false가 반환되는 경우다.
- 다음의 예에서 var.a가 빈 문자열이 아니라면 var.a를 나타내지만, 비어 있을 때는 “default-a”를 반환한다
# <조건 정의> ? <옳은 경우> : <틀린 경우>
var.a != "" ? var.a : "default-a"
조건식의 각 조건은 비교 대상의 형태가 다르면 테라폼 실행 시 조건 비교를 위해 형태를 추론하여 자동으로 변환하는데, 명시적인 형태 작성을 권장
# 조건식 형태 권장 사항
var.example ? 12 : "hello" # 비권장
var.example ? "12" : "hello" # 권장
var.example ? tostring(12) : "hello" # 권장
조건식은 단순히 특정 속성에 대한 정의, 로컬 변수에 대한 재정의, 출력 값에 대한 조건 정의 뿐만 아니라 리소스 생성 여부에 응용할 수 있다. count에 조건식을 결합한 경우 다음과 같이 특정 조건에 따라 리소스 생성 여부를 선택할 수 있다.
main.tf 파일 내용
variable "enable_file" {
default = true
}
resource "local_file" "foo" {
count = var.enable_file ? 1 : 0
content = "foo!"
filename = "${path.module}/foo.bar"
}
output "content" {
value = var.enable_file ? local_file.foo[0].content : ""
}
확인
# 변수 우선순위3 : 환경 변수 (TF_VAR 변수 이름)
export TF_VAR_enable_file=false
export | grep TF_VAR_enable_file
#
terraform init && terraform plan && terraform apply -auto-approve
terraform state list
echo "var.enable_file ? 1 : 0" | terraform console
# 환경 변수 삭제
unset TF_VAR_enable_file
export | grep TF_VAR_enable_file
# 재실행
terraform plan && terraform apply -auto-approve
terraform state list
#
echo "local_file.foo[0]" | terraform console
echo "local_file.foo[0].content" | terraform console
echo "var.enable_file ? 1 : 0" | terraform console
환경변수 삭제 후 다시 실행 하면 아래처럼 나온다.
'study > T101 4기' 카테고리의 다른 글
T101 4기 4주차 첫번째 (0) | 2024.06.30 |
---|---|
T101 4기 3주차 두번째 (0) | 2024.06.29 |
T101 4기 2주차 두번째 (0) | 2024.06.22 |
T101 4기 2주차 첫번째 (0) | 2024.06.22 |
T101 4기 1주차 세번째 (0) | 2024.06.15 |