IaC/Infrastructure Provisioning

5. Terraform의 다양한 Expression

Somaz 2023. 4. 13. 20:00
728x90
반응형

Overview

2023.04.04 - [Hashicorp] - 1. Terraform 기초 : 설치 및 .tf 파일 설명

2023.04.06 - [Hashicorp] - 2. Terraform 변수 사용법(use-variable)

2023.04.10 - [Hashicorp] - 3. Terraform 다양한 변수(variable, local, data...output, input)

2023.04.12 - [Hashicorp] - 4. Terraform의 다양한 Function(함수)

 

오늘은 테라폼의 다양한 Expression에 대해 공부해보려고 한다.

 


1. Types and Values(유형 및 값)

모든 값에는 해당 값을 사용할 수 있는 위치와 적용할 수 있는 변환을 나타내는 유형이 있다.


Type

  • string: 와 같은 일부 텍스트를 나타내는 일련의 유니코드 문자이다 "hello".
  • number: 숫자 값이다. 유형 은 number와 같은 정수 (15)및 와 같은 소수 값을 모두 나타낼 수 있다 (6.283185.)
  • bool: 부울 값, true또는 false. bool값은 조건 논리에서 사용할 수 있다.
  • list(또는 tuple): 와 같은 일련의 값 ["us-west-1a", "us-west-1c"]. 목록 또는 튜플의 요소는 0부터 시작하여 연속된 정수로 식별된다.
  • map(또는 object): 와 같이 명명된 레이블로 식별되는 값 그룹이다 {name = "Mabel", age = 52}.
  • null : 부재 또는 생략을 나타내는 값이다. 리소스의 인수를 로 설정하면 null Terraform은 인수를 완전히 생략한 것처럼 동작한다. 인수가 있는 경우 인수의 기본값을 사용하고 인수가 필수인 경우 오류를 발생한다.
  • any :  'any' 유형은 변수가 string, number, bool, list, map, object, 등과 같은 모든 유형의 값을 허용할 수 있는 특수 유형 제약 조건이다.

 

리스트(list)와 튜플(tuple)의 차이

 

List

  • list는 동일한 데이터 유형을 가진 값의 모음이다.
  • list 내의 모든 요소가 동일한 데이터 유형을 공유해야 함을 의미하는 동종 데이터 구조이다.
  • 문자열 목록, 숫자 목록, 지도 목록 등을 가질 수 있지만 단일 목록 내에서 다른 데이터 유형을 혼합할 수는 없습다.
  • Terraform에서 list(<type>) 함수를 사용하여 목록을 정의할 수 있다. 
variable "example_list" {
  type    = list(string)
  default = ["value1", "value2", "value3"]
}

 

Tuple

  • 튜플은 다른 데이터 유형을 가질 수 있는 값의 모음이다.
  • 이기종 데이터 구조이다. 즉, 단일 튜플 내에서 데이터 유형이 다른 요소를 가질 수 있다.
  • 튜플의 각 요소에는 특정 유형 제약이 있으며 튜플의 유형은 해당 요소 유형의 정렬된 조합에 의해 결정된다.
  • Terraform에서 tuple(<types>) 함수를 사용하여 튜플을 정의할 수 있다. 
variable "example_tuple" {
  type    = tuple([string, number, bool])
  default = ("value1", 42, true)
}

 


인덱스 및 속성(Indices and Attributes)

elements of lists, tuples, maps, and objects 에 액세스하는 방법을 설명한다.

 

 

 

리스트 / 튜플 인덱스

  • 리스트와 튜플의 요소는 인덱스가 정수인 대괄호 인덱스 표기법을 사용하여 액세스할 수 있다.
  • 인덱싱은 0부터 시작한다
locals {
  example_list = ["apple", "banana", "cherry"]
  example_tuple = ["apple", 42, true]
}

output "list_element" {
  value = local.example_list[1] # Output: "banana"
}

output "tuple_element" {
  value = local.example_tuple[2] # Output: true
}

 

리스트 / 튜플 사용법

locals {
  example_map = {
    apple  = "fruit"
    number = 42
    valid  = true
  }
  example_object = {
    apple  = "fruit"
    number = 42
    valid  = true
  }
}

output "map_element_bracket" {
  value = local.example_map["apple"] # Output: "fruit"
}

output "map_element_dot" {
  value = local.example_map.apple # Output: "fruit"
}

output "object_element_bracket" {
  value = local.example_object["number"] # Output: 42
}

output "object_element_dot" {
  value = local.example_object.valid # Output: true
}

 

대괄호 사용법

값의 type이나 variables.tf 파일에 정의된 방식에 관계없이 값을 대괄호 '[]'로 묶어 단일 요소 목록을 만들 수 있다.

  • 보통 variable 사용시 variable.tf 파일에 type을 지정한다.
  • 리스트나 튜플 타입이 아니더라도 []를 붙이면 리스트나 튜플이 된다.

 

variable.tf

variable "example_string" {
  type    = string
  default = "hello"
}

variable "example_map" {
  type = map(string)
  default = {
    key1 = "value1"
    key2 = "value2"
  }
}

 

main.tf

locals {
  single_element_string_list = [var.example_string] # ["hello"]
  single_element_map_list    = [var.example_map]    # [{"key1"="value1", "key2"="value2"}]
}

output "single_element_string_list" {
  value = local.single_element_string_list
}

output "single_element_map_list" {
  value = local.single_element_map_list
}

 

 


2. Conditional Expressions(조건식)

조건식 은 bool 식의 값을 사용하여 두 값 중 하나를 선택합니다.

 


 

 

Syntax

condition ? true_val : false_val
  • condition이 true이면 값은 true_val 이고 false 이면 false_val 이다.

 

var.a != "" ? var.a : "default-a"
  • var.a가 비어있지않으면 true이고 결과는 var.a이가 나온다. 비어있으면 결과는 "default-a" 이다.

 

두 결과 값은 모든 유형일 수 있지만 Terraform이 조건 값을 모르고 전체 조건식이 반환할 유형을 결정할 수 있도록 둘 다 동일한 유형이여야 한다.

var.example ? tostring(12) : "hello"

 


Practice .tf code

 

variable.tf

variable "create" {
  description = "Determines whether to create Fargate profile or not"
  type        = bool
  default     = true
}

variable "create_iam_role" {
  description = "Determines whether an IAM role is created or to use an existing IAM role"
  type        = bool
  default     = true
}

 

  • variable "create"
    • 해당 변수는 bool 데이터 유형이며 기본값은 true이다. 해당 설명은 Fargate 프로필 생성 여부를 결정하는 데 사용됨을 나타낸다.
  • variable "create_iam_role": 해당 변수에는 bool 데이터 유형이며 기본값은 true이다. 해당 설명은 새 IAM 역할을 생성할지 또는 기존 역할을 사용할지 결정하는 데 사용된다.

 

main.tf

data "aws_iam_policy_document" "assume_role_policy" {
  count = var.create && var.create_iam_role ? 1 : 0

  statement {
    effect  = "Allow"
    actions = ["sts:AssumeRole"]

    principals {
      type        = "Service"
      identifiers = ["eks-fargate-pods.amazonaws.com"]
    }
  }
}
  • data "aws_iam_policy_document" "assume_role_policy": 이 데이터 블록은 역할 가정 정책에 대한 AWS IAM 정책 문서를 생성한다.
  • count 매개변수를 사용하여 create 및 create_iam_role 변수의 값을 기반으로 정책 문서 생성을 제어한다.
  • var.create와 var.create_iam_role이 모두 true이면 count 값이 1이 되고 정책 문서가 생성된다.
  • var.create 또는 var.create_iam_role이 false인 경우 count 값은 0이 되고 정책 문서가 생성되지 않는다.

 

 

Result

Fargate 프로필과 IAM 역할의 생성을 제어하는 ​​두 가지 변수를 정의한다. 또한 Amazon EKS Fargate 서비스가 IAM 역할을 맡도록 허용하는 역할 가정 정책에 대한 AWS IAM 정책 문서를 생성한다.


3. for Expressions

'for' 표현식을 사용하면 입력 컬렉션의 각 요소에 표현식을 적용하여 하나의 컬렉션(목록, 집합 또는 맵)을 다른 컬렉션으로 변환할 수 있다. 이는 새 컬렉션을 만들기 위해 값 컬렉션을 조작하거나 필터링해야 할 때 유용하다.

 


Syntax

 

[for <item> in <collection>: <result_expression>]

ex.)
[for s in var.list : upper(s)]
  • <item>: 입력 <collection>의 각 요소를 나타내는 임시 변수이다.
  • <collection>: 반복하려는 input list, set 또는 map이다.
  • <result_expression>: 새 컬렉션의 요소를 생성하기 위해 각 <item>에 적용되는 표현식이다.

 

if 절을 추가하여 입력 컬렉션을 필터링할 수도 있다.

[for <item> in <collection>: <result_expression> if <condition>]

ex.) 
[for s in var.list : upper(s) if s != ""]

 

Practice .tf code

 

variable.tf

variable "users" {
  type = map(object({
    is_admin = bool
  }))
}
  • type: 이 속성은 변수의 데이터 유형을 지정한다.
  • 이 경우 bool 값을 보유하는 단일 키 "is_admin"이 있는 객체 map이다.
  • 즉, "users" 변수는 각각 고유한 키(이름)와 사용자가 관리자인지 여부를 나타내는 bool 값이 있는 사용자 개체 모음이다.

 

terraform.tvars

users = {
  "user1" = {
    is_admin = true
  }
  "user2" = {
    is_admin = false
  }
  "user3" = {
    is_admin = true
  }
}

 

 

main.tf

locals {
  admin_users = {
    for name, user in var.users : name => user
    if user.is_admin
  }
  regular_users = {
    for name, user in var.users : name => user
    if !user.is_admin
  }
}
  • locals: 이 키워드는 하나 이상의 로컬 값을 정의하는 데 사용된다. "admin_users" 및 "regular_users"라는 두 개의 로컬 값이 정의된다.
  • admin_users: 이 로컬 값은 "users" 변수를 필터링하여 "is_admin" 값이 true로 설정된 사용자만 포함하는 map을 생성한다. 이는 "for" 루프를 사용하여 수행되며 "users" 변수의 각 키-값 쌍(이름, 사용자)을 반복하고 "is_admin" 값이 true인 경우 새 map에 포함한다
  • regular_users: 마찬가지로 이 로컬 값은 "users" 변수를 필터링하여 "is_admin" 값이 false로 설정된 사용자만 포함하는 새 map을 만든다. "for" 루프가 다시 사용되지만 이번에는 "is_admin"이 false로 설정된 사용자를 확인한다.

 

Result

즉, bool 속성이 "is_admin"인 사용자 개체 컬렉션을 보유하는 "users" 변수를 정의한다. 그런 다음 "is_admin" 특성을 기반으로 "users" 변수를 필터링하여 "admin_users" 및 "regular_users"라는 두 개의 로컬 값을 만들고 관리 사용자와 비관리 사용자에 대한 별도의 map을 만든다.

 

admin_users에는 "이름 : user1", "이름 : user3" 인 map이 생성되고,

admin_users = {
  "user1" = {
    is_admin = true
  }
  "user3" = {
    is_admin = true
  }
}

 

regular_users에는 "이름 : user2"인 map이 생성된다.

regular_users = {
  "user2" = {
    is_admin = false
  }
}

4. dynamic Blocks

dynamic 블록은 collection(list, set 또는 map)을 기반으로 리소스 또는 모듈 내에 중첩된 블록을 동적으로 생성하는 데 사용된다. 생성할 중첩 블록의 수가 입력 데이터에 따라 달라지는 경우 특히 유용하며 유연하고 재사용 가능한 구성을 작성할 수 있다.

또한 특정 리소스 또는 모듈에 유효한 블록 유형을 제공해야 한다. 예를 들어 aws_security_group 리소스를 사용하는 경우 ingress, egress 또는 tags와 같은 유효한 블록 유형과 함께 dynamic 블록을 사용할 수 있다.

 


 

Syntax

resource "aws_elastic_beanstalk_environment" "tfenvtest" {
  name = "tf-test-name" # can use expressions here

  setting {
    # but the "setting" block is always a literal block
  }
}

 


 

Practice .tf code

AWS 보안 그룹 리소스가 있고 CIDR 블록 목록이 포함된 변수를 기반으로 여러 수신 규칙을 추가하려고 한다고 가정한다.

 

 

variable.tf

variable "cidr_blocks" {
  type = list(string)
  default = ["10.0.0.0/16", "192.168.1.0/24"]
}

 

 

main.tf

resource "aws_security_group" "example" {
  name = "example"

  dynamic "ingress" {
    for_each = var.cidr_blocks
    content {
      from_port   = 0
      to_port     = 0
      protocol    = "tcp"
      cidr_blocks = [ingress.value]
    }
  }
}

 

 


 

Result

 'dynmaic' 블록은 'var.cidr_blocks' 목록을 반복하고 목록의 각 CIDR 블록에 대해 'ingress' 블록을 생성한다. content 블록은 중첩된 ingress 블록의 구조를 정의하고 ingress.value는 반복의 현재 CIDR 블록을 나타낸다.

 

결과적으로 cidr_blocks 변수의 CIDR 블록 "10.0.0.0/16" 및 "192.168.1.0/24"를 사용하여 aws_security_group.example 리소스에 대해 두 개의 수신 규칙이 생성된다.

 

마지막으로 Terraform의 'dynamic' 블록을 사용하면 컬렉션을 기반으로 리소스 또는 모듈 내에 중첩된 블록을 생성하여 구성을 보다 유연하고 재사용 가능하게 만들 수 있다.

 


Reference

 

terraform expressions

 

terraform type

 

 

 

728x90
반응형