Study_note
[Terraform] 반복문, if문 (for, for_each, count, if) 본문
테라폼은 선언적 언어로서 실제 배포된 내용을 정확하게 나타내며, 추론하기 쉽지만 특정 유형의 작업을 하기 어렵다.
예를 들어 선언적 언어에는 for 반복문이 없다 -> 같은 리소스를 여러 개 만드는 반복 처리에 어려움이 있다
테라폼은 다행히도 특정 유형을 반복하거나 if문을 사용할 수 있도록 count 메타 변수, for each, for 표현식 등 다양한 함수 등을 제공하고 있다.
반복문은 아래와 같다.
count 매개 변수 : 리소스를 반복
for_each 표현식 : 리소스 내에서 리소스 및 인라인 블록 반복
for 표현식 : 리스트와 맵을 반복
for 문자열 지시어 : 문자열 내에서 리스트와 맵을 반복
count 매개 변수
count는 테람폼의 가장 오래되고 단순하며 ㅈ제한된 반복 구조로써 count에 생성하고자 하는 리소스 사본수만 정의된다.
# 만약 neo라는 사용자를 3개 생성하고 싶다면 아래와 같이 리소스를 생성하면된다.
resource "aws_iam_user" "example" {
count = 3
name = "neo"
}
----------------------------------------------------
하지만 위 처럼 구축한다면 같은 이름의 사용자는 생성이 안되기 때문에 에러가 발생한다.
그렇기 때문에 아래와 같이 count.index를 사용하여 반복문 안에 있는 각각의 반복을 가리키는 인덱스를 사용
resource "aws_iam_user" "example" {
count = 3
name = "neo.${count.index}"
}
-> neo.0, neo.1, neo.2 같은 결과 값이 생성되는것을 확인 가능
----------------------------------------------------
아래와 같이 리스트 형식에 입력 변수를 생성하고 디폴값을 정의했을 경우
variable "user_names" {
description = "Create IAM users with these names"
type = list(string)
default = ["neo", "trinity", "morpheus"]
}
디폴값을 정의한 값들을 사용자 이름으로 삽일할려면 아래와 같다
resource "aws_iam_user" "example" {
count = length(var.user_names) # length는 주어진 값의 항목수를 반환하는 함수
name = var.user_names[count.index] # count 값 하드 코딩하지 말고 변수 값 호출
}
-> neo, trinity, morpheus 값 생성되는것을 확인 가능
----------------------------------------------------
count를 사용하면 하나의 리소스가 아니라 리소스의 배열이 된다.
생성된 배열을 호출할려면 아래와 같다.
<provider>_<type>.<name>[index].attribute
output "first_arn" {
value = aws_iam_user.example[0].arn # 위와 같은 예시
description = "The ARN for the first user"
}
output "all_arns" {
value = aws_iam_user.example[*].arn # 생성한 배열에 모든 값을 적용
description = "The ARNs for all users"
}
----------------------------------------------------
하지만 count 매개 변수에는 유용성을 저해하는 두 가지 제약이 있다.
1. 전체 리소스를 반복할 수는 있지만 리소스 내에서 인라인 블록을 반복할 수는 없다.
2. 변경할 때 문제 발생
첫 번쨰 제약 예
아래와 같이 오토스케일링 그룹을 생성할때 인스턴스의 이름 테그를 따로 지정 시
count는 인라인 블록내에서 사용을 지원하지 않아 다르게 설정할 수 없다.
resource "aws_autoscaling_group" "example" {
launch_configuration = X
vpc_zone_identifier = X
target_group_arns = X
health_check_type = "X"
min_size = X
max_size = X
tag {
key = "Name"
value = var.cluster_name
propagate_at_launch = true
}
}
----------------------------------------------------
두 번쨰 제약 예
aws_iam_user.eaxample[0] = neo
aws_iam_user.eaxample[1] = trinity
aws_iam_user.eaxample[2] = morpheus
이 구조에서 aws_iam_user.eaxample[1] = trinity 삭제 시
aws_iam_user.eaxample[2] = morpheus가 aws_iam_user.eaxample[1] =morpheus 가 된다.
즉 중간에서 삭제 시 해당 항목 뒤에있는 리소스를 삭제한다음 해당 리소르를 처음부터 만든다.
그래서 기존에 [2]를 가리키던 코드들은 삭제 되고 [1]의 코드들은 남아 있는 상태가 된다.
for_each
for_each를 사용하면 리스트, 집합, 맵을 사용 하여 리소스 내 인라인 블록의 여러 복사본을 생성할 수 있습니다.
또한 리소스를 맵으로 처리하면 컬렉션 중간의 항목도 안전하게 제거할 수 있어 count를 사용해 리소를 배열로 처리하는 것보다 이점이 크다.
for_each의 표현식은 아래와 같다.
for_each = <COLLECTION> # COLLECTION은 루프를 처리할 집합 또는 맵
[CONFIG....]
for_each의 값으로는 리스트를 사용할 수 없다.
resource "aws_iam_user" "example" {
for_each = toset(var.user_names)
name = each.value
}
리스트인 var.user_names를 집합으로 변환하기 위해 toset을 사용
for_each를 반복하면 each.value에서 각 사용자 이름을 사용할 수 있다.
또한 for_each를 사용한 후에는 하나의 리소스 또는 배열이 되는 것이 아니라 리소스 맵이 된다
---------------------------
아래와 같이 생성한 배열 값이 아닌 집합으로 모든 사용자의 값을 출력 하면
리소스의 전체 출력인 맵을 포함 한다.
output "all_users" {
value = aws_iam_user.example
}
---------------------------
# 위에서 다음과 같은 입력변수를 생성했었다.
# variable "user_names" {
# description = "Create IAM users with these names"
# type = list(string)
# default = ["neo", "trinity", "morpheus"]
# }
---------------------------------
아래와 같이 커스텀 테그라는 입렵 변수 생성
variable "custom_tags" {
description = "Custom tags to set on the Instances in the ASG"
type = map(string)
default = {}
}
------------------------------
위에 오토스케일링 그룹을 생성했던것과 같이 구문에서 인라인 블록을 동적으로 생성하는 구문은 아래와 같다
dynamic "<var_name>" { # 원하는 변수 이름 입력
for_each = <collection> # collection은 생성할 정보를 전달받고 그 수만큼 block 이 생성
}
content { # <var_name>.key, <var_name>.value를 사용해 입력 값 설정
[config..]
}
}
하지만 동적 블록을 과도하게 사용하면 구성을 읽고 유지하기 어렵게 만들 수 있음
------------------------------
tag {
key = "Name"
value = var.cluster_name
propagate_at_launch = true
}
dynamic "tag" {
for_each = var.custom_tags
}
content {
key = tag.key
value = tag.value
propagate_at_launch = true
}
}
---------------
모듈 사용하는 파일에 입력 변수 입력
custom_tags = {
Owner = "team-foo"
ManagedBy = "terraform"
}
-> 결과 값으로 tag외 동적으로 추가한 값들도 생성된것을 확인
tag {
key = "Owner"
value = "team-foo"
propagate_at_launch = true
}
tag {
key = "ManagedBy"
value = "terraform"
propagate_at_launch = true
}
for 표현식을 이용한 반복문
리소스와 인라인 블록을 반복하는 것이 아닌 단일 값을 생성하기 위해 반복이 필요할 때 사용
리스트 반복 구문 형식은 아래와 같다
for <item> in <list> : <output>
아래와 같이 리스트 형식의 변수를 생성하고 출력해보면
variable "names" {
description = "A list of names"
type = list(string)
default = ["neo", "trinity", "morpheus"]
}
output "upper_names" {
value = [for name in var.names : upper(name)]
}
결과 값이 아래와 같이 생성 확인 가능
upper_names = [
"NEO",
"TRINITY",
"MORPHEUS",
]
-----------------------------
맵 반복 구문 형식은 아래와 같다
for <key>, <value> in <map> : <output>
아래와 같이 맵 형식의 변수를 생성하고 출력해보면
variable "hero_thousand_faces" {
description = "map"
type = map(string)
default = {
neo = "hero"
trinity = "love interest"
morpheus = "mentor"
}
}
output "bios" {
value = [for name, role in var.hero_thousand_faces : "${name} is the ${role}"]
}
결과 값이 아래와 같이 생성 확인 가능
bios = [
"morpheus is the mentor",
"neo is the hero",
"trinity is the love interest",
]
조건문
조건문을 설정하는 여러 방법이 있으며 아래와 같다
count 매개 변수 -> 조건부 리소스에서 사용
for_each와 for 표현식 -> 리소스 내의 조건부 리소스 및 인라인 블록에 사용
if 문자열 지시자 -> 문자열 내의 조건문에서 사용
우선적으로 count 매개 변수 조건문은 bool을 사용하여 true or false에 따른 결과 값을 다르게 설정한다
아래와 같이 bool 값의 변수 값을 생성
variable "enable_autoscaling" {
description = "If set to true, enable auto scaling"
type = bool
}
조건문 구문은 아래와 같다
count = <condition> ? <true_val> : <false_val> # 컨디션의 bool 값의 변수 넣는다
모듈에 다음 count 매개변수 조건문 추가
resource "aws_autoscaling_schedule" "scale_in_at_night" {
count = var.enable_autoscaling ? 1 : 0
모듈을 사용할 파일에서 bool 입력 변수 값이 true 즉 1 값으이고 false는 0값으로 지정했기 때문에
입력 값에 따라 생성된다.
-----------------------------------
하지만 bool 값이 문자열 등의 더 복잡한 비교의 결과인 경우 아래와 같다.
아래에 bool 값이 아닌 문자열 입력 변수가 있을때
variable "instance_type" {
description = "The type of EC2 Instances to run (e.g. t2.micro)"
type = string
}
아래와 같이 format 함수를 사용하여 var.instance_type에서 첫 번쨰 문자만 추출하여
해당 문자가 "t"일 경우 count를 1로 설정하고 "t"가 아닐 경우 count가 0이 되어 생성되지 않는다.
resource "aws_cloudwatch_metric_alarm" "low_cpu_credit_balance" {
count = format("%.1s", var.instance_type) == "t" ? 1 : 0
-----------------------------------
조금 더 효율적으로 사용하면 if-else 문을 생성할수 있다.
아래와 같이 t계열이면 low_cpu_credit_balance가 생성되고 t가 아니라면 high_cpu_credit_balance 생성
resource "aws_cloudwatch_metric_alarm" "low_cpu_credit_balance" {
count = format("%.1s", var.instance_type) == "t" ? 1 : 0
[config....]
resource "aws_cloudwatch_metric_alarm" "high_cpu_credit_balance" {
count = format("%.1s", var.instance_type) == "t" ? 0 : 1
[config....]
key => upper(value)
if key != "Name"
}
content {
key = tag.key
value = tag.value
propagate_at_launch = true
}
}
for_each와 for 표현식과 if 문자열 지시자 조건문도 있지만 생략한다.
일반적으로 리소스를 조건부로 생성할 때는 count를 사용하고 그 외 모든 유형의 반복문 및 조건문에서는 for_each를 사용하기 때문
즉
일반적으로 리소스 조건 생성할 때 -> count 매개 변수
이 외의 경우(인라인 블록 등)에서 반복 생성할 때 -> for_each와 for 표현식 사용
'Terraform' 카테고리의 다른 글
EKS Upgrade 1.22 -> 1.23 version (0) | 2023.03.13 |
---|---|
[etc] systemd 및 service 등록 (0) | 2023.03.09 |
[Terraform] 모듈 (입력 변수, 지역 변수, 출력 변수, 파일 경로, 인라인 블록) (0) | 2022.10.05 |
[Terraform] 상태 파일, 원격 Backend, remote_state (2) (1) | 2022.09.29 |
[Terraform] Terraform up & running 학습 및 기본 문법 (1) (0) | 2022.09.27 |