Notice
Recent Posts
Recent Comments
Link
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

Study_note

[Terraform] 반복문, if문 (for, for_each, count, if) 본문

Terraform

[Terraform] 반복문, if문 (for, for_each, count, if)

Study_note 2022. 10. 11. 19:58

테라폼은 선언적 언어로서 실제 배포된 내용을 정확하게 나타내며, 추론하기 쉽지만 특정 유형의 작업을 하기 어렵다.

 

예를 들어 선언적 언어에는 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 표현식 사용

Comments