본문 바로가기
Infrastructure as Code/Terraform

[T101 Study 5주차] 반복문 & if문

by 코인선물로부자된다 2022. 11. 26.
반응형

https://github.com/idealistchanu/study-T101Study/tree/main/CHAPTER5

[GitHub - idealistchanu/study-T101Study

Contribute to idealistchanu/study-T101Study development by creating an account on GitHub.

github.com](https://github.com/idealistchanu/study-T101Study/tree/main/CHAPTER5)

테라폼은 유형을 반복 Loops, if 문 If-Statements을 사용하거나 무중단 배포를 할 수 있도록 count 메타 변수, for_each 표현식, create_before_destroy 생명 주기 블록 등 함수를 제공

  • 반복문 Loops
  • 조건문 Conditionals
  • 무중단 배포 Zero-downtime deployment
  • 테라폼의 주의 사항 Terraform gotchas

반복문

테라폼이 제공하는 반복문 구성 looping constructs

  • count 매개변수 parameter : 리소스와 모듈의 반복
  • for_each 표현식 expressions : 리소스 내에서 리소스 및 인라인 블록, 모듈을 반복
  • for 표현식 expressions : 리스트 lists 와 맵 maps 을 반복
  • for 문자열 지시어 string directive : 문자열 내에서 리스트 lists 와 맵 maps 을 반복

count 매개 변수

단점

  • 인라인 블록을 반복 불가
  • 목록 중간 항목을 제거시, 전체 삭제 후, 재생성
provider "aws" {
  region = "ap-northeast-2"
}

resource "aws_iam_user" "myiam" {
  count = 3
  name  = "$NICKNAME.${count.index}"
}

aws iam list-users | jq

variable "user_names" {
  description = "Create IAM users with these names"
  type        = list(string)
  default     = ["gasida", "akbun", "fullmoon"]
}
  • 테라폼에서 count 와 함께 배열 조회 구문과 length 함수를 사용해서 사용자들 생성 가능
    • 배열 조회 구문 Array lookup syntax
      • ARRAY[]
      • 예를 들어 다음은 var.user_names 의 인덱스 1에서 요소를 찾는 방법
      • var.user_names[1]
    • length (내장) 함수 built-on function
      • length()
      • 주어진 ARRAY 의 항목 수를 반환하는 함수. 문자열 및 맵을 대상으로도 동작
provider "aws" {
  region = "ap-northeast-2"
}

resource "aws_iam_user" "myiam" {
  count = length(var.user_names)
  name  = var.user_names[count.index]
}

variable "user_names" {
  description = "Create IAM users with these names"
  type        = list(string)
  default     = ["gasida", "akbun", "fullmoon"]
}

output "all_arns" {
  value       = aws_iam_user.myiam[*].arn
  description = "The ARNs for all users"
}

for_each

장점

  • 리스트 lists, 집합 sets, 맵 maps 를 사용하여 전체 리소스의 여러 복사본 또는 리소스 내 인라인 블록의 여러 복사본, 모듈의 복사본을 생성
  • for_each 를 사용해 리소스를 맵으로 처리하면 컬렉션 중간의 항목도 안전하게 제거할 수 있어서, count 로 리소스를 배열 처리보다 이점이 큼
  • 리소스 내에서 여러 개의 인라인 블록을 만들 수 있다는 점
resource "<PROVIDER>_<TYPE>" "<NAME>" {
  for_each = <COLLECTION>

  [CONFIG ...]
}
  • COLLECTION 은 루프를 처리할 집합 sets 또는 맵 maps
  • 리소스에 for_each 를 사용할 때에는 리스트는 지원하지 않습니다.
  • 그리고 CONFIG 는 해당 리소스와 관련된 하나 이상의 인수로 구성되는데 CONFIG 내에서 each.key 또는 each.value 를 사용하여 COLLECTION 에서 현재 항목의 키와 값에 접근할 수 있습니다.

for_each 를 사용해 인라인 블록을 동적으로 생성하는 구문

dynamic "<VAR_NAME>" {
  for_each = <COLLECTION>

  content {
    [CONFIG...]
  }
}

dynamic "tag" {
    for_each = var.custom_tags

    content {
      key                 = tag.key
      value               = tag.value
      propagate_at_launch = true
    }
  }
  • 여기서 VAR_NAME 은 각 ‘반복’의 값을 저장할 변수에 사용할 이름이고, COLLECTION 은 반복되는 리스트 또는 맵이며, content 블록은 각 반복에서 생성되는 항목입니다.
  • content 블록 내에서 <VAR_NAME>.key 및 <VAR_NAME>.value 를 사용해 COLLECTION 에 있는 현재 항목의 키와 값에 각각 액세스할 수 있습니다.
  • for_each 를 리스트와 함께 사용하는 경우 key는 인덱스가 되고 value는 해당 인덱스 목록에 있는 항목이 된다는 점을 기억합니다.

dynamic의 경우에 오류가 발생한다.

https://developer.hashicorp.com/terraform/language/expressions/dynamic-blocks

[Dynamic Blocks - Configuration Language | Terraform | HashiCorp Developer

Dynamic blocks automatically construct multi-level, nested block structures. Learn to configure dynamic blocks and understand their behavior.

developer.hashicorp.com](https://developer.hashicorp.com/terraform/language/expressions/dynamic-blocks)

for 표현식

단일 값을 생성하기 위해 반복이 필요한 경우

혹은 반복하여 단일 값을 수정해야하는 경우

[for <ITEM> in <LIST> : <OUTPUT>]

variable "names" {
  description = "A list of names"
  type        = list(string)
  default     = ["gasida", "akbun", "fullmoon"]
}

output "upper_names" {
  value = [for name in var.names : upper(name)]
}

output "short_upper_names" {
  value = [for name in var.names : upper(name) if length(name) < 6]
}


[for <KEY>, <VALUE> in <MAP> : <OUTPUT>]

variable "names" {
  description = "A list of names"
  type        = list(string)
  default     = ["gasida", "akbun", "fullmoon"]
}

output "upper_names" {
  value = [for name in var.names : upper(name)]
}

output "short_upper_names" {
  value = [for name in var.names : upper(name) if length(name) < 5]
}

variable "hero_thousand_faces" {
  description = "map"
  type        = map(string)
  default     = {
    gasida    = "hero"
    akbun     = "love interest"
    fullmoon  = "mentor"
  }
}

output "bios" {
  value = [for name, role in var.hero_thousand_faces : "\${name} is the \${role}"]
}
# 리스트를 반복하고 맵을 출력 Loop over a list and output a map
{for <ITEM> in <LIST> : <OUTPUT_KEY> => <OUTPUT_VALUE>}

# 맵을 반복하고 리스트를 출력 Loop over a map and output a map
{for <KEY>, <VALUE> in <MAP> : <OUTPUT_KEY> => <OUTPUT_VALUE>}

문자열 지시자

  • 문자열 지시자를 사용하면 문자열 보간과 유사한 구문으로 문자열 내에서 for 반복문, if문 같은 제어문을 사용할 수 있습니다.
  • 다만 달러 부호와 중괄호 ${..} 대신 백분율 부호 %{}.. 를 사용한다는 차이가 있습니다.
  • 테라폼은 두 가지 유형의 문자열 지시자, for 반복문과 조건문을 지원합니다.
  • %{ for <ITEM> in <COLLECTION> }<BODY>%{ endfor }
  • COLLECTION 은 반복할 리스트 또는 맵이고 ITEM은 COLLECTION 의 각 항목에 할당할 로컬 변수의 이름이며 BODY는 ITEM을 참조할 수 있는 각각의 반복을 렌더링하는 대상
variable "names" {
  description = "Names to render"
  type        = list(string)
  default     = ["gasida", "akbun", "fullmoon"]
}

output "for_directive" {
  value = "%{ for name in var.names }\${name}, %{ endfor }"
}

output "for_directive_index" {
  value = "%{ for i, name in var.names }(\${i}) \${name}, %{ endfor }"
}

조건문 Conditionals

  • count 매개 변수 parameter : 조건부 리소스에서 사용
  • for_each 와 for 표현식 expressions : 리소스 내의 조건부 리소스 및 인라인 블록에 사용
  • If 문자열 지시자 if string directive : 문자열 내의 조건문에 사용

count 매개 변수

  1. 리소스에 count 를 1로 설정하면 해당 리소스의 사본 하나를 얻습니다. count 를 0으로 설정하면 해당 리소스가 만들어지지 않습니다.
  2. 테라폼은 ? <TRUE_VAL> : <FALSE_VAL> 형식의 조건 표현식 conditional expression 을 지원합니다.
count = var.enable_autoscaling ? 1 : 0

for_each 와 for 표현식

  • for_each 표현식을 빈 컬렉션으로 전달하면 0개의 리소스 또는 0개의 인라인 블록을 생성합니다.
  • 비어 있지 않은 컬렉션을 전달하면 하나 이상의 리소스 또는 인라인 블록을 만듭니다.
dynamic "tag" {
    for_each = {
      for key, value in var.custom_tags:
      key => upper(value)
      if key != "Name"
    }

    content {
      key                 = tag.key
      value               = tag.value
      propagate_at_launch = true
    }
  }
  • 중첩된 for 표현식은 일관성을 위해 var.custom_tags 를 반복하며 각 값을 대문자로 변환하고 모듈이 이미 자체 Name 태그를 설정했으므로,
    • for 표현식의 조건을 사용하여 key 집합을 Name 으로 필터링합니다.
  • for 표현식에서 값을 필터링하여 임의 조건부 논리를 구현할 수 있습니다.
  • 리소스의 복사본을 여러 개 만들 때는 count 보다 for_each 를 사용하는 것이 더 낫지만,
    • 조건 논리의 경우 비어 있지 않은 컬렉션에 for_each 를 설정하는 것보다 count 를 0 또는 1로 설정하는 것이 간단합니다.
  • 즉, 리소스를 조건부로 생성할 때는 count 를 사용할 수 있지만, 그 외 모든 유형의 반복문 또는 조건문에는 for_each 를 사용합니다.

if 문자열 지시자

%{ if <CONDITION> }<TRUEVAL>%{ endif }
%{ if <CONDITION> }<TRUEVAL>%{ else }<FALSEVAL>%{ endif }

CONDITION은 boolean 으로 평가되는 표현식이고, TRUEVAL은 CONDITION이 True로 평가되면 렌더링할 표현식입니다.

variable "names" {
  description = "Names to render"
  type        = list(string)
  default     = ["gasida", "akbun", "fullmoon"]
}

output "for_directive" {
  value = "%{ for name in var.names }\${name}, %{ endfor }"
}

output "for_directive_index" {
  value = "%{ for i, name in var.names }(\${i}) \${name}, %{ endfor }"
}

output "for_directive_index_if" {
  value = <<EOF
%{ for i, name in var.names }
  \${name}%{ if i < length(var.names) - 1 }, %{ endif }
%{ endfor }
EOF
}

문자열 지시자 시작에 공백이 있으며 지시자 앞에, 문자열 지시자 끝에 공객이 있으면 지시자 뒤에 물결표를 사용합니다.

output "for_directive_index_if_strip" {
  value = <<EOF
%{~ for i, name in var.names ~}
  \${name}%{ if i < length(var.names) - 1 }, %{ endif }
%{~ endfor ~}
EOF
}
output "for_directive_index_if_else_strip" {
  value = <<EOF
%{~ for i, name in var.names ~}
  \${name}%{ if i < length(var.names) - 1 }, %{ else }.%{ endif }
%{~ endfor ~}
EOF
}