๐กcloudNet@ ํ์ ๊ฐ์๋ค ๋์ด ์งํํ๋ Terraform 101 Study 4๊ธฐ 3์ฃผ์ฐจ ๋ด์ฉ์ผ๋ก,
[ํ ๋ผํผ์ผ๋ก ์์ํ๋ IaC] ๋์๋ฅผ ์ฐธ์กฐํ์์ต๋๋ค. ๊ฐ์ฌํฉ๋๋ค..
1. ๋ฐ๋ณต๋ฌธ
1.1 for_each ๋ฐ๋ณต๋ฌธ
for_each๋ ๋ฐ๋ณต(for)์ ํ ๋ ํ์ ๊ฐ์ ๋ํด ํ๋ํ๋ each object๋ก ์ ๊ทผํ๋ค๋ ์๋ฏธ์ด๋ค.
์ฆ, ๋ฆฌ์์ค ๋๋ ๋ชจ๋์์ for_each์ ์ ๋ ฅ๋ ๋ฐ์ดํฐ ํํ๊ฐ map ๋๋ set์ด๋ฉด, ์ ์ธ๋ key ๊ฐ ๊ฐ์๋งํผ ๋ฆฌ์์ค๋ฅผ ์์ฑํ๊ฒ ๋๋ค.
for_each : ๋ฐ๋ณต๋ฌธ์ผ๋ก, ์ ์ธ๋ key ๊ฐ ๊ฐ์๋งํผ ๋ฆฌ์์ค๋ฅผ ์์ฑํจ
๋ค์๊ณผ ๊ฐ์ด tfํ์ผ์ ์์ฑํ์ฌ apply๋ฅผ ์คํผํด๋ณด๊ฒ ๋ค.
# main.tf
resource "local_file" "abc" {
**for_each** = {
**a = "content a"
b = "content b"**
}
**content** = **each.value**
filename = "${path.module}/**${each.key}**.txt"
}
for_each๊ฐ ์ค์ ๋ ๋ธ๋ก์์๋ each ์์ฑ์ ์ฌ์ฉํด ๊ตฌ์ฑ์ ์์ ํ ์ ์๋ค.
- each.key : ์ด ์ธ์คํด์ค์์ ํด๋นํ๋ map ํ์ ์ key ๊ฐ
- each.value : ์ด ์ธ์คํด์ค์์ ํด๋นํ๋ map ํ์ ์ value ๊ฐ
์์ฑ๋๋ ๋ฆฌ์์ค์ ๊ฒฝ์ฐ <๋ฆฌ์์ค ํ์ >.<์ด๋ฆ>[], ๋ชจ๋์ ๊ฒฝ์ฐ module.<๋ชจ๋ ์ด๋ฆ>[]๋ก ํด๋น ๋ฆฌ์์ค์ ๊ฐ์ ์ฐธ์กฐํ๋ค. ์ด ์ฐธ์กฐ ๋ฐฉ์์ ํตํด ๋ฆฌ์์ค ๊ฐ ์ข ์์ฑ์ ์ ์ํ๊ธฐ๋ ํ๊ณ ๋ค๋ฅธ ๋ฆฌ์์ค์์ ์ฌ์ฉํ๊ฑฐ๋ ์ถ๋ ฅ์ ์ํ ๊ฒฐ๊ณผ ๊ฐ์ผ๋ก ์ฌ์ฉํ๋ค.
๋ค์์ผ๋ก local_file.abc๋ ๋ณ์์ map ํํ์ ๊ฐ์ ์ฐธ์กฐํ๊ณ , local_file.def์ ๊ฒฝ์ฐ local_file.abc ๋ํ ๊ฒฐ๊ณผ๊ฐ map์ผ๋ก ๋ฐํ๋๋ฏ๋ก ๋ค์ for_each ๊ตฌ๋ฌธ์ ์ฌ์ฉํ ์ ์๋ค.
# main.tf
**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"
key ๊ฐ์ count์ index์ ๋ฌ๋ฆฌ ๊ณ ์ ํ๊ธฐ ๋๋ฌธ์ ์ค๊ฐ์ ๊ฐ์ ์ญ์ ํ ํ ๋ค์ ์ ์ฉํด๋ ์ญ์ ํ ๊ฐ์ ๋ํด์๋ง ๋ฆฌ์์ค๋ฅผ ์ญ์ ํ๋ค.
์ฆ๊ฐ ๊ฐ์ ์ญ์ ํ ๋ณ์๋ฅผ ์ ์ฉํ์ฌ ํ์ธํด๋ณด๊ฒ ๋ค.(count์ ์ ์ฌํ๊ฒ..)
# main.tf
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"
}
๋ฐฐ์ด๋ก ์ธ๋ฑ์ค๋ฅผ ์ง์ ํ๋ count์ ๋ฌ๋ฆฌ for_each์ ๊ฒฝ์ฐ ๊ณ ์ ํ key์ ํด๋น๋๋ ๋ฆฌ์์ค๋ก ์ง์ ํ ์ ์์ด ์ค๊ฐ ๊ฐ์ ์ญ์ ํ๊ณ ๋ค์ ์ ์ฉํด๋ ์ญ์ ๋์์ ๋ํด์๋ง ์ญ์ ๋๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
1.2 for_each ๋ฐ์ดํฐ ์ ํ
for_each ๋ฐ์ดํฐ ์ ํ์ ๋ํด ํ์ธํด๋ณด๊ฒ ๋ค.
# main.tf
resource "aws_iam_user" "the-accounts" {
for_each = ["Todd", "James", "Alice", "Dottie"]
name = each.key
}
๋ฐ์ดํฐ ํ์์ด ์๋ชป๋๋ค๊ณ ๋ํ๋๋ค.
๋ค์๊ณผ ๊ฐ์ด ์์ ํ ํ ์คํํ๋ค.
# main.tf
resource "aws_iam_user" "the-accounts" {
for_each = toset(["Todd", "James", "Alice", "Dottie"])
name = each.key
}
๋ค์๊ณผ ๊ฐ์ด ์ ์์ ์ผ๋ก ์คํ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
Terraform will perform the following actions:
# aws_iam_user.the-accounts["Alice"] will be created
+ resource "aws_iam_user" "the-accounts" {
+ arn = (known after apply)
+ force_destroy = false
+ id = (known after apply)
+ name = "Alice"
+ path = "/"
+ tags_all = (known after apply)
+ unique_id = (known after apply)
}
# aws_iam_user.the-accounts["Dottie"] will be created
+ resource "aws_iam_user" "the-accounts" {
+ arn = (known after apply)
+ force_destroy = false
+ id = (known after apply)
+ name = "Dottie"
+ path = "/"
+ tags_all = (known after apply)
+ unique_id = (known after apply)
}
# aws_iam_user.the-accounts["James"] will be created
+ resource "aws_iam_user" "the-accounts" {
+ arn = (known after apply)
+ force_destroy = false
+ id = (known after apply)
+ name = "James"
+ path = "/"
+ tags_all = (known after apply)
+ unique_id = (known after apply)
}
# aws_iam_user.the-accounts["Todd"] will be created
+ resource "aws_iam_user" "the-accounts" {
+ arn = (known after apply)
+ force_destroy = false
+ id = (known after apply)
+ name = "Todd"
+ path = "/"
+ tags_all = (known after apply)
+ unique_id = (known after apply)
}
๋ฒ์ธ. ๋ฐ์ดํฐ ์ ํ ํ์ธ ์ค์ต
๊ธฐ๋ณธ ์ ํ | ์งํฉ ์ ํ |
|
|
list์ set์ ์ ์ธํ๋ ํํ๊ฐ ๋น์ทํ์ง๋ง ์ฐธ์กฐ ๋ฐฉ์์ด ์ธ๋ฑ์ค์ ํค๋ก ๊ฐ๊ฐ ์ฐจ์ด๊ฐ ์๊ณ , map์ set์ ๊ฒฝ์ฐ ์ ์ธ๋ ๊ฐ์ด ์ ๋ ฌ๋๋ ํน์ง์ ๊ฐ์ง๋ค.
์์ธ ๋ด์ญ์ ๋ค์๊ณผ ๊ฐ๋ค.
์์ธ ๋ด์ญ ํ์ธ
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 }
}
์คํ๊ฒฐ๊ณผ
#
terraform plan
#
terraform console
-----------------
# ๊ธฐ๋ณธ
type(12)
type("12")
type(a)
type("a")
type(true)
type("true")
# string
var.string_a
var.string_b
type(var.string_a)
type(var.string_b)
# number
var.number_a
var.number_b
var.number_c
type(var.number_a)
type(var.number_b)
type(var.number_c)
# boolean
var.boolean
type(var.boolean)
# list , set , tuple - 'value'
var.list_set_tuple_a
var.list_set_tuple_b
var.list_set_tuple_c
var.list_set_tuple_d
var.list_set_tuple_e
type(var.list_set_tuple_a)
type(var.list_set_tuple_b)
type(var.list_set_tuple_c)
type(var.list_set_tuple_d)
type(var.list_set_tuple_e)
var.list_set_tuple_a[0]
type(var.list_set_tuple_a[0])
var.list_set_tuple_b[0]
type(var.list_set_tuple_b[0])
var.list_set_tuple_d[0]
var.list_set_tuple_d[1]
var.list_set_tuple_d[2]
type(var.list_set_tuple_d[0])
type(var.list_set_tuple_d[1])
type(var.list_set_tuple_d[2])
var.list_set_tuple_e[0]
type(var.list_set_tuple_e[0])
# map , object - 'key : value'
var.map_object_a
var.map_object_b
var.map_object_c
var.map_object_d
type(var.map_object_a)
type(var.map_object_b)
type(var.map_object_c)
type(var.map_object_d)
var.map_object_a["a"]
type(var.map_object_a["a"])
var.map_object_b["a"]
type(var.map_object_b["a"])
var.map_object_c["name"]
type(var.map_object_c["name"])
var.map_object_d["age"]
type(var.map_object_d["age"])
# tuple > list > set
type(["a","b"])
type(tolist(["a","b"]))
type(toset(["a","b"]))
# object > map
type({a="a", b="b"})
type({a="a", b=1})
type(tomap({a="a", b="b"}))
type(tomap({a="a", b=1}))
exit
-----------------
1.3 for_each, count ๋ฐ๋ณต๋ฌธ ๋น๊ต
count๋ index๋ก ๋ช๋ฒ์ ์ด๋ค ๋ด์ฉ์ด ์๋์ง ํ์ธํ ์ ์๋ค. ์ฆ, ์ค๊ฐ ๋ฆฌ์์ค ์ญ์ ๋ ์ถ๊ฐ ์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์๋ค.
resource "local_file" "abc" {
count = 3
content = "This is filename abc${count.index}.txt"
filename = "${path.module}/abc${count.index}.txt"
}
๋ฐฐ์ด(Array) ์ ๋ฆฌ์คํธ(List) ์ ์ฐจ์ด์ ์ ๋ฌด์์ผ๊น?
ํฐ ์ฐจ์ด์ ์ ๋ฐฐ์ด์ ์ฐ์๋๊ณ ๋ฆฌ์คํธ๋ ๋น ์ฐ์์ ์ด๋ ๊ฒ์ด๋ค.
๋ฐฐ์ด์ ๊ฐ์ ํน์ฑ์ ๊ฐ์ง๋ ์์๋ค์ ์์๋๋ก ๊ตฌ์ฑํ ์ ํ ์๋ฃ ๊ตฌ์กฐ์ด๊ณ , ๋ฆฌ์คํธ๋ ๋ฐฐ์ด์ ์ธ๋ฑ์ค๋ฅผ ๋ฒ๋ฆฌ๊ณ ๋นํ์์ด ๋ฐ์ดํฐ๋ฅผ ์ ์ฌํ๋(๋ฉ๋ชจ๋ฆฌ ๋ญ๋น๊ฐ ๋ํจ) ์ฐ์์ ์ผ๋ก ์ ์ฅํ์ง ์๋ ์ฐ๊ฒฐ ๋ฆฌ์คํธ ๊ตฌ์กฐ์ด๋ค.
for_each ํํ์์ ์ฌ์ฉํ๋ฉด lists, sets, maps๋ฅผ ์ฌ์ฉํ์ฌ ์ ์ฒด ๋ฆฌ์์ค์ ์ฌ๋ฌ ๋ณต์ฌ๋ณธ ๋๋ ๋ฆฌ์์ค ๋ด ์ธ๋ผ์ธ ๋ธ๋ก์ ์ฌ๋ฌ ๋ณต์ฌ๋ณธ, ๋ชจ๋์ ๋ณต์ฌ๋ณธ์ ์์ฑํ ์ ์๋ค.
๋ค์์ for_each๋ฅผ ์ฌ์ฉํด ๋ฆฌ์์ค์ ์ฌ๋ฌ ๋ณต์ฌ๋ณธ์ ๋ง๋๋ ๊ตฌ๋ฌธ์ด๋ค.
resource "<PROVIDER>_<TYPE>" "<NAME>" {
for_each = <COLLECTION>
[CONFIG ...]
}
(COLLECTION์ ๋ฃจํ๋ฅผ ์ฒ๋ฆฌํ sets ๋๋ maps)
๋ฆฌ์์ค์ for_each๋ฅผ ์ฌ์ฉํ ๋์๋ ๋ฆฌ์คํธ๋ ์ง์ํ์ง ์๋๋ค. ๊ทธ๋ฆฌ๊ณ CONFIG๋ ํด๋น ๋ฆฌ์์ค์ ๊ด๋ จ๋ ํ๋ ์ด์์ ์ธ์๋ก ๊ตฌ์ฑ๋๋๋ฐ CONFIG ๋ด์์ each.key ๋๋ each.value๋ฅผ ์ฌ์ฉํ์ฌ COLLECTION์์ ํ์ฌ ํญ๋ชฉ์ ํค์ ๊ฐ์ ์ ๊ทผํ ์ ์๋ค.
์๋ก ๋ค์์ for_each๋ฅผ ์ฌ์ฉํ์ฌ 3๊ฐ์ IAM ์ฌ์ฉ์๋ฅผ ์์ฑํ๋ ๋ฐฉ๋ฒ์ด๋ค.
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
}
var.user_names ๋ฆฌ์คํธ๋ฅผ set์ผ๋ก ๋ณํํ๊ธฐ ์ํด toset๋ฅผ ์ฌ์ฉํ๋ค.(for_each๋ ๋ฆฌ์์ค์ ์ฌ์ฉ๋ ๋๋ set๊ณผ map๋ง ์ง์)
for_each๊ฐ ์ด ์งํฉ์ ๋ฐ๋ณตํ๋ฉด each.value์์ ๊ฐ ์ฌ์ฉ์ ์ด๋ฆ์ ์ฌ์ฉํ ์ ์๋ค.
์ผ๋ฐ์ ์ผ๋ก๋ each.key๋ ํค/๊ฐ ์ ๋งต์์๋ง ์ฌ์ฉ ๊ฐ๋ฅํ์ง๋ง, ์ฌ์ฉ์ ์ด๋ฆ์ each.key ์์๋ ์ฌ์ฉํ ์ ์๋ค.
for_each๋ฅผ ์ฌ์ฉํ ํ์๋ ํ๋์ ๋ฆฌ์์ค ๋๋ count๋ฅผ ์ฌ์ฉํ ๊ฒ๊ณผ ๊ฐ์ ๋ฆฌ์์ค ๋ฐฐ์ด์ด ๋๋ ๊ฒ์ด ์๋ ๋ฆฌ์์ค ๋งต(list into a set)์ด ๋๋ค.
terraform init -upgrade
**terraform plan && terraform apply -auto-approve**
# ํ์ธ
**terraform state list**
**terraform output**
aws iam list-users | jq
****# ๋ฐฐํฌ ๊ฒฐ๊ณผ content๋ง ํํฐ๋ง ํ์ธ
cat terraform.tfstate | grep -e key -e name -e "content:"
all_users ์ถ๋ ฅ ๋ณ์๊ฐ for_each์ ํค ์ฆ, ์ฌ์ฉ์ ์ด๋ฆ์ ๊ฐ์ง๋ฉฐ ๊ฐ์ด ํด๋น ๋ฆฌ์์ค์ ์ ์ฒด ์ถ๋ ฅ์ธ ๋งต์ ํฌํจํ๋ค.
๋ฌด์๋ณด๋ค for_each๋ฅผ ์ฌ์ฉํ์ด ๋ฆฌ์์ค๋ฅผ ๋งต์ผ๋ก ์ฒ๋ฆฌํ๋ฉด ์ปฌ๋ ์ ์ค๊ฐ์ ํญ๋ชฉ๋ ์์ ํ๊ฒ ์ ๊ฑฐํ ์ ์์ด count๋ก ๋ฆฌ์์ค๋ฅผ ๋ฐฐ์ด ์ฒ๋ฆฌํ๋ ๊ฒ ๋ณด๋ค ํด๋น ๋ถ๋ถ์์ ์ด์ ์ด ํฌ๋ค.
์ ๋ฆฌ : for_each๋ ์ฃผ๋ณ ๋ชจ๋ ๋ฆฌ์์ค๋ฅผ ์ฎ๊ธฐ์ง ์๊ณ ์ ํํ ๋ชฉํํ ๋ฆฌ์์ค๋ง ์ญ์ ํ๋ค. ๋ฐ๋ผ์ ๋ฆฌ์์ค์ ๊ตฌ๋ณ๋๋ ์ฌ๋ฌ ๋ณต์ฌ๋ณธ์ ๋ง๋ค ๋๋ count ๋์ for_each๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ๋ฐ๋์งํ๋ค.
1.4 for Expressions (for ๋ฐ๋ณต๋ฌธ)
count | for_each | for expression | |
์ฌ์ฉ๋ชฉ์ | ๋ฐ๋ณต ์กฐํ | ๋ฐ๋ณต ์กฐํ | ๋ฐ๋ณต ๋ณํ |
for๋ฌธ์ ๋ณตํฉ ํ์ ๊ฐ์ ํํ๋ฅผ ๋ณํํ๋ ๋ฐ ์ฌ์ฉ๋๋ค. ์๋ฅผ ๋ค์ด list ๊ฐ์ ํฌ๋งท์ ๋ณ๊ฒฝํ๊ฑฐ๋ ํน์ ์ ๋์ฌ๋ฅผ ์ถ๊ฐํ ์๋ ์๊ณ , output์ ์ํ๋ ํํ๋ก ๋ฐ๋ณต์ ์ธ ๊ฒฐ๊ณผ๋ฅผ ํํํ ์ ์๋ค.
- list ํ์ ์ ๊ฒฝ์ฐ ๊ฐ ๋๋ ์ธ๋ฑ์ค์ ๊ฐ์ ๋ฐํ
- map ํ์ ์ ๊ฒฝ์ฐ ํค ๋๋ ํค์ ๊ฐ์ ๋ํด ๋ฐํ
- set ํ์ ์ ๊ฒฝ์ฐ ํค ๊ฐ์ ๋ํด ๋ฐํ
ํ ์คํธ๋ฅผ ์ํด ๊ธฐ๋ณธ ๊ตฌ์ฑ์ ์งํํด๋ณด๊ฒ ๋ค. list์ ๋ด์ฉ์ ๋ด๋ ๋ฆฌ์์ค๋ฅผ ์์ฑํ๋ค. var.name์ ๋ด์ฉ์ด ๊ฒฐ๊ณผ ํ์ผ์ content๋ก ๊ธฐ๋ก๋๋ค.
# main.tf
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
}
์ถ๊ฐ! var.name ๊ฐ์ ์ผ๊ด์ ์ผ๋ก ๋๋ฌธ์๋ก ๋ณํ!
ํ์ผ ๋ด์ฉ์ ๋ค์๊ณผ ๊ฐ์ด ์์ ํ๋ค. 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
}
jsonencode Function - ๋งํฌ
jsonencode
encodes a given value to a string using JSON syntax.
๋ค์ for ๊ตฌ๋ฌธ์ผ๋ก ๋์ด๊ฐ์๋ฉด, for ๊ตฌ๋ฌธ์ ์ฌ์ฉํ๋ ๊ท์น์ด ์กด์ฌํ๋๋ฐ ์ด๋ ๋ค์๊ณผ ๊ฐ๋ค.
- list ์ ํ์ ๊ฒฝ์ฐ ๋ฐํ ๋ฐ๋ ๊ฐ์ด ํ๋๋ก ๋์ด ์์ผ๋ฉด ๊ฐ์, ๋ ๊ฐ์ ๊ฒฝ์ฐ ์์ ์ธ์๊ฐ ์ธ๋ฑ์ค๋ฅผ ๋ฐํํ๊ณ ๋ค์ ์ธ์๊ฐ ๊ฐ์ ๋ฐํ
- ๊ด์ฉ์ ์ผ๋ก๋ ์ธ๋ฑ์ค๋ i, ๊ฐ์ v๋ก ํํ
- map ์ ํ์ ๊ฒฝ์ฐ ๋ฐํ ๋ฐ๋ ๊ฐ์ด ํ๋๋ก ๋์ด ์์ผ๋ฉด ํค๋ฅผ, ๋ ๊ฐ์ ๊ฒฝ์ฐ ์์ ์ธ์๊ฐ ํค๋ฅผ ๋ฐํํ๊ณ ๋ค์ ์ธ์๊ฐ ๊ฐ์ ๋ฐํ
- ๊ด์ฉ์ ์ผ๋ก๋ ํค๋ k, ๊ฐ์ v๋ก ํํ
- ๊ฒฐ๊ณผ ๊ฐ์ for ๋ฌธ์ ๋ฌถ๋ ๊ธฐํธ๊ฐ [ ] ์ธ ๊ฒฝ์ฐ tuple๋ก ๋ฐํ๋๊ณ { } ์ธ ๊ฒฝ์ฐ object ํํ๋ก ๋ฐํ
- object ํํ์ ๊ฒฝ์ฐ ํค์ ๊ฐ์ ๋ํ ์์ ⇒ ๊ธฐํธ๋ก ๊ตฌ๋ถ
- { } ํ์์ ์ฌ์ฉํด object ํํ๋ก ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ๋ ๊ฒฝ์ฐ ํค ๊ฐ์ ๊ณ ์ ํด์ผ ํ๋ฏ๋ก ๊ฐ ๋ค์ ๊ทธ๋ฃนํ ๋ชจ๋ ์ฌ๋ณผ(…)๋ฅผ ๋ถ์ฌ์ ํค์ ์ค๋ณต์ ๋ฐฉ์ง
- SQL์ group by ๋ฌธ ํน์ Java์ MultiValueMap๊ณผ ๊ฐ์ ๊ฐ๋
- IF ๊ตฌ๋ฌธ์ ์ถ๊ฐํด ์กฐ๊ฑด ๋ถ์ฌ ๊ฐ๋ฅ
list ์ ํ์ ๋ํ for ๊ตฌ๋ฌธ ์ฒ๋ฆฌ์ ๋ช ๊ฐ์ง ์๋ฅผ ํ์ธํ๊ธฐ ์ํด ๋ค์๊ณผ ๊ฐ์ด ํ์ผ์ ์์ฑํ๊ณ output์ ํ์ธํ๋ค.
# 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"]
}
๋ค์์ map ์ ํ์ ๋ํ for ๊ตฌ๋ฌธ ์ฒ๋ฆฌ ์์๋ฅผ ํ์ธํด๋ณด๊ฒ ๋ค.
# main.tf
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...
}
}
for expression์ผ๋ก s3 ์ด๋ฆ ์ผ๊ด ์์ (์คํฐ๋ ์ ๋ถ๋ ์ ๊ณต)
๋ค์ S3 ์์ฑ ์์ ๋ฅผ ์ฐธ๊ณ ํ์ฌ ๋ฏธ๋ฆฌ S3๋ฅผ ๊ตฌ์ฑํ์๋ค. ์ดํ ๋ค์๊ณผ ๊ฐ์ด ๋ด์ฉ์ ์์ ํ์ฌ s3 ์ด๋ฆ ๋ณ๊ฒฝ์ ์งํํ์๋ค.
# main.tf
variable "cloud-stady" {
type = set(string)
default = ["dev","ops"]
description = "devChanWoo"
}
variable "postfix" {
type = string
default = "test"
description = "postfix"
}
provider "aws" {
region = "ap-northeast-2"
}
resource "aws_s3_bucket" "woo-test" {
for_each = toset([for cloud-stady in var.cloud-stady : format("%s-%s", cloud-stady, var.postfix)])
bucket = "devwoo-tf-test-${each.key}"
tags = {
Name = "My bucket"
Environment = "Dev"
}
}
1.5 dynamic
ํ ๋ผํผ ๊ตฌ์ฑ์ ์์ฑํ๋ค ๋ณด๋ฉด count๋ for_each ๊ตฌ๋ฌธ์ ์ฌ์ฉํ์ฌ ๋ฆฌ์์ค ์ ์ฒด๋ฅผ ์ฌ๋ฌ ๊ฐ ์์ฑํ๋ ๊ฒ ์ธ์๋ ๋ฆฌ์์ค ๋ด์์ ์ ์ธ๋๋ ๊ตฌ์ฑ ๋ธ๋ก์ ๋ค์ค์ผ๋ก ์์ฑํด์ผ ํ๋ ๊ฒฝ์ฐ๊ฐ ์๋ค.
์๋ฅผ ๋ค๋ฉด, AWS์ Security Group ๋ฆฌ์์ค ๊ตฌ์ฑ์ ingress, egress ์์๊ฐ ๋ฆฌ์์ค ์ ์ธ ๋ด๋ถ์์ ๋ธ๋ก ํํ๋ก ์ฌ๋ฌ ๋ฒ ์ ์๋๋ ๊ฒฝ์ฐ์ด๋ค.
# AWS_Security_group_ex.tf
**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์ ์ง์ ํ ์ด๋ฆ์ ๋ํด ์์ฑ์ด ๋ถ์ฌ๋๋ค.
dynamic ๋ธ๋ก ํ์ฉ ์์
์ผ๋ฐ์ ์ธ ๋ธ๋ก ์์ฑ ๋ฐ๋ณต ์ ์ฉ ์ | dynamic ๋ธ๋ก ์ ์ฉ ์ |
resource "provider_resource" "name" { name = "some_resource" some_setting { key = a_value } some_setting { key = b_value } some_setting { key = c_value } some_setting { key = d_value } } |
resource "provider_resource" "name" { name = "some_resource" dynamic "some_setting" { for_each = { a_key = a_value b_key = b_value c_key = c_value d_key = d_value } content { key = some_setting.value } } } |
ํ๋ฒ archive ํ๋ก๋ฐ์ด๋์ archive_file์ source ๋ธ๋ก ์ ์ธ์ ๋ฐ๋ณตํ๋ ๊ฒฝ์ฐ terraform apply๋ฅผ ์ํํด ํ์ธํด๋ณด๊ฒ ๋ค. (์๋ก์ด ํ๋ก๋ฐ์ด๋๊ฐ ์ถ๊ฐ๋๋ ๊ฒฝ์ฐ terraform init ๋ช ๋ น์ด ํ์ํจ)
# main.tf
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 ๋ธ๋ก์ผ๋ก ์ฌ๊ตฌ์ฑํด์ฑํด์ ํ์ธํด๋ณด๊ฒ ๋ค.
# main.tf
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"
}
}**
}
์คํํด๋ณด๋ ๋์ผํ ๊ฒฐ๊ณผ๊ฐ ๊ธฐ๋๋์ด ๋ณ๊ฒฝ ์ฌํญ์ด ์๋ ๊ฒ์ ํ์ธํ ์ ์๋ค!
2. ์กฐ๊ฑด์
ํ ๋ผํผ์์ ์กฐ๊ฑด์์ 3ํญ ์ฐ์ฐ์ ํํ๋ฅผ ๊ฐ๋๋ค. ์กฐ๊ฑด์ true ๋๋ false๋ก ํ์ธ๋๋ ๋ชจ๋ ํํ์์ ์ฌ์ฉํ ์ ์๋ค.
์ผ๋ฐ์ ์ผ๋ก ๋น๊ต, ๋ ผ๋ฆฌ ์ฐ์ฐ์๋ฅผ ์ฌ์ฉํด ์กฐ๊ฑด์ ํ์ธํ๋ค. ์กฐ๊ฑด์์ ? ๊ธฐํธ๋ฅผ ๊ธฐ์ค์ผ๋ก ์ผ์ชฝ์ ์กฐ๊ฑด์ด๋ฉฐ ์ค๋ฅธ์ชฝ์ : ๊ธฐํธ๋ฅผ ๊ธฐ์ค์ผ๋ก ์ผ์ชฝ์ด ์กฐ๊ฑด์ ๋ํด 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. ํจ์
ํ ๋ผํผ์ ํ๋ก๊ทธ๋๋ฐ ์ธ์ด์ ์ธ ํน์ฑ์ ๊ฐ์ง๊ณ ์๋ค. ๊ทธ๋์ ๊ฐ์ ์ ํ์ ๋ณ๊ฒฝํ๊ฑฐ๋ ์กฐํฉํ ์ ์๋ ๋ด์ฅ ํจ์๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
๋จ, ๋ด์ฅ๋ ํจ์ ์ธ์ ์ฌ์ฉ์๊ฐ ๊ตฌํํ๋ ๋ณ๋์ ์ฌ์ฉ์ ์ ์ ํจ์๋ฅผ ์ง์ํ์ง ์๋๋ค. ํจ์ ์ข ๋ฅ์๋ ์ซ์, ๋ฌธ์์ด, ์ปฌ๋ ์ , ์ธ์ฝ๋ฉ, ํ์ผ ์์คํ , ๋ ์ง/์๊ฐ, ํด์/์ํธํ, IP ๋คํธ์ํฌ, ์ ํ ๋ณํ์ด ์๋ค.
ํ ๋ผํผ ์ฝ๋์์ ํจ์๋ฅผ ์ ์ฉํ๋ฉด ๋ณ์, ๋ฆฌ์์ค ์์ฑ, ๋ฐ์ดํฐ ์์ค ์์ฑ, ์ถ๋ ฅ ๊ฐ ํํ ์ ์์ ์ ๋์ ์ด๊ณ ํจ๊ณผ์ ์ ์ํํ ์ ์๋ค.
๊ฐ๋จํ ๋ด์ฅ ํจ์๋ฅผ ์ฌ์ฉํด๋ณด๊ฒ ๋ค.
# main.tf
resource "local_file" "foo" {
content = **upper**("foo! bar!")
filename = "${path.module}/foo.bar"
}
์ถ์ฒ์ค์ต
count + ํจ์ cidrhost ๋ก EC2์ ENI ์ 10๊ฐ์ ip๋ฅผ ์ฅ์ฐฉ
์ถ๊ฐ ์์