Overview
오늘은 Terraform에서 활용할 수 있는 다양한 함수(Function)에 대해 공부해보려고 한다.
Terraform 함수는 인프라 코드 내에서 값을 변형하거나 조작하는 데 사용되며, 코드의 유연성과 효율성을 높여준다.
Terraform에서 제공하는 함수 유형에는 다음과 같은 것들이 있다.
- Numeric Functions (숫자 관련 함수)
- String Functions (문자열 관련 함수)
- Collection Functions (컬렉션 관련 함수)
- Encoding Functions (인코딩 관련 함수)
- Filesystem Functions (파일 시스템 관련 함수)
- Date and Time Functions (날짜 및 시간 관련 함수)
- Hash and Crypto Functions (해시 및 암호화 관련 함수)
- IP Network Functions (IP 네트워크 관련 함수)
- Type Conversion Functions (타입 변환 함수)
이 중에서 coalesce, merge, substr, length, format, lookup, element, index 등의 함수를 살펴보고, 실전 예제와 함께 각 함수의 동작 방식과 활용법을 정리해 보았다.

📅 관련 글
2023.03.30 - [IaC/Infrastructure Provisioning] - 1. Terraform 기초 : 설치 및 .tf 파일 설명
2023.03.31 - [IaC/Infrastructure Provisioning] - 2. Terraform 변수 사용법(use-variable) - AWS
2023.04.03 - [IaC/Infrastructure Provisioning] - 4. Terraform의 다양한 Function(함수)
2023.04.04 - [IaC/Infrastructure Provisioning] - 5. Terraform의 다양한 Expression
1. coalesce(Collection Function)
coalesce임의 개수의 인수를 취하고 null 또는 빈 문자열이 아닌 첫 번째 인수를 반환한다.
Syntax
> coalesce("a", "b")
a
> coalesce("", "b")
b
> coalesce(1,2)
1
`Practice .tf` code
`variable.tf`
variable "environment" {
description = "The environment for the infrastructure (e.g., development, staging, production)"
type = string
default = "dev" # Optional: Provide a default value if desired
}
variable "project" {
description = "This resource means that it was created with terraform (e.g., development, staging, production)"
type = string
default = "project1" # Optional: Provide a default value if desired
}
`main.tf`
locals {
company = "somaz"
database_subnet_group_name = coalesce(
var.database_subnet_group_name,
format(
"%s-%s-%s-rds-subnets-group",
var. company,
var. environment,
var. project,
)
)
}
...
database_subnet_group_name = local.database_subnet_group_name
결과
# 1. terraform.tvars에 파일이 정의되있는 경우
## terraform.tvars
database_subnet_group_name = somaz-rds-subnets-group
## result = somaz-rds-subnets-group
# 2. terraform.tvars에 파일이 정의되있지 않는 경우
## result = somaz-dev-project1-rds-subnets-group
- `var.database_subnet_group_name` 이 `terraform.tfvars` 파일에 이미 정의된 경우 `coalesce` 함수는 `var.database_subnet_group_name` 의 값을 반환한다.
- `terraform.tvars` 파일에 `var.database_subnet_group_name` 가 정의되어 있지않다면 `somaz-dev-project1-rds-subnets-group` 을 반환한다.
- 지난시간에 배웠지만 저렇게 local변수로 지정하면, 아래의 `local.database_subnet_group_name` 으로 사용할 수 있다.
2. merge(Collection Functions)
임의의 수의 map 또는 object를 취하고 모든 인수에서 병합된 요소 집합을 포함하는 single map 또는 object를 반환한다.
둘 이상의 지정된 맵 또는 객체가 동일한 키 또는 속성을 정의하는 경우 인수 시퀀스에서 나중에 있는 것이 우선한다.
Syntax
> merge({a="b", c="d"}, {e="f", c="z"})
{
"a" = "b"
"c" = "z"
"e" = "f"
}
> merge({a="b"}, {a=[1,2], c="z"}, {d=3})
{
"a" = [
1,
2,
]
"c" = "z"
"d" = 3
}
`Practice .tf` code
`variable.tf`
variable "environment" {
description = "The environment for the infrastructure (e.g., development, staging, production)"
type = string
default = "dev" # Optional: Provide a default value if desired
}
variable "project" {
description = "This resource means that it was created with terraform (e.g., development, staging, production)"
type = string
default = "project1" # Optional: Provide a default value if desired
}
variable "terraform" {
description = "This resource means that it was created with terraform (e.g., development, staging, production)"
type = string
default = "true" # Optional: Provide a default value if desired
}
`main.tf`
locals {
company = "somaz"
default_tags = {
Terraform = var.terraform
Environment = var.environment
}
database_subnet_group_tags = merge(
local.default_tags,
{
Name = format(
"%s-%s-%s-rds-subnets-group",
var.company,
var.environment,
var.project,
)
}
)
}
결과
따라서 결과 `database_subnet_group_tags map` 은 아래와 같다.
{
"Terraform": true,
"Environment": "dev",
"Name": "somaz-dev-project1-rds-subnets-group"
}
- map(or object): 와 같이 명명된 레이블로 식별되는 값 그룹이다 {name = "Mabel", age = 52}.
- map은 key = value 값으로 되어있고 Terraform의 type 중 하나이다.
3. substr(String Functions) / length(Collection Functions)
substr
주어진 문자열에서 오프셋 및 (최대) 길이로 하위 문자열을 추출한다.
substr(string, offset, length)
length
주어진 목록, 맵 또는 문자열의 길이를 결정한다.
list나 map이 제공되면 결과는 해당 컬렉션의 요소 수이다. 문자열이 주어지면 결과는 문자열의 문자 수이다.
Syntax
substr
> substr("hello world", 1, 4)
ello
> substr("🤔🤷", 0, 1)
🤔
> substr("hello world", -5, -1)
world
length
> length([])
0
> length(["a", "b"])
2
> length({"a" = "b"})
1
> length("hello")
5
> length("👾🕹️")
2
`Practice .tf` code
아래의 코드는 지난번 변수파트에서 설명했던 코드이다.
2023.04.10 - [Hashicorp] - 3. Terraform 다양한 변수(variable, local, data...output, input)
`variable.tf`
variable "image_id" {
type = string
description = "The id of the machine image (AMI) to use for the server."
validation {
condition = length(var.image_id) > 4 && substr(var.image_id, 0, 4) == "ami-"
error_message = "The image_id value must be a valid AMI id, starting with \"ami-\"."
}
}
- image id를 확인하여 문자열 길이가 4개이상이고 substr function으로 4번째 문자열까지 검사하여 결과가 "ami-" 가 맞는지 확인하는 validation이다.
결과
먼저 validation은 해당 블록 내에 블록을 추가하여 특정 변수에 대한 사용자 지정 유효성 검사 규칙을 만들 수 있다.
condition은 유효성 검사를 통과하려면 true로 평가되어야 하는 bool 표현식이다.
length function을 보면 `var.image_id` 의 길이는 4보다 커야한다. 그리고 substr function으로 0번째 문자열부터 4개를 검사하여 image id 가 "ami-"로 확인한 후 true면 유효성 검사가 통과되는 것이고 false면 error_message가 나오는 조건문이다.
4. format(String Functions)
format 함수는 사양 문자열에 따라 여러 다른 값의 형식을 지정하여 문자열을 생성한다. printfC의 함수 및 다른 프로그래밍 언어의 다른 유사한 함수와 유사하다.
format(spec, values...)
Syntax
> format("Hello, %s!", "Ander")
Hello, Ander!
> format("There are %d lights", 4)
There are 4 lights
`Practice .tf` code
`variable.tf`
variable "environment" {
description = "The environment for the infrastructure (e.g., development, staging, production)"
type = string
default = "dev" # Optional: Provide a default value if desired
}
variable "project" {
description = "This resource means that it was created with terraform (e.g., development, staging, production)"
type = string
default = "project1" # Optional: Provide a default value if desired
}
variable "company" {
description = "This resource means that it was created with terraform (e.g., development, staging, production)"
type = string
default = "somaz" # Optional: Provide a default value if desired
}
`main.tf`
resource "aws_eip" "this" {
instance = aws_instance.this.id
vpc = true
tags = {
Name = format(
"%s-%s-%s-eip-bastion",
var.company,
var.environment,
var.project,
)
}
}
결과
반복되는 문자열을 `variable.tf` 파일에 지정하여 변수로 지정한 후 반복사용이 용이하게 만들어준다.
{
"Name": "somaz-dev-project1-eip-bastion"
}
5. lookup(Collection Functions) / element(Collection Functions)
lookup
lookup주어진 키로 맵에서 단일 요소의 값을 검색합니다. 주어진 키가 존재하지 않으면 주어진 기본값이 대신 반환된다.
lookup(map, key, default)
element
element목록에서 단일 요소를 검색한다. 인덱스는 0부터 시작한다. 이 함수는 빈 목록과 함께 사용하면 오류를 생성한다. 인덱스는 음수가 아닌 정수여야 한다.
element(list, index)
Syntax
lookup
> lookup({a="ay", b="bee"}, "a", "what?")
ay
> lookup({a="ay", b="bee"}, "c", "what?")
what?
element
> element(["a", "b", "c"], 1)
b
> element(["a", "b", "c"], 3)
a
> element(["a", "b", "c"], length(["a", "b", "c"])-1)
c
`Practice .tf` code
`variable.tf`
variable "origin" {
description = "One or more origins for this distribution (multiples allowed)."
type = any
default = null
}
variable "origin_group" {
description = "One or more origin_group for this distribution (multiples allowed)."
type = any
default = {}
}
- Terraform에서 'any' 유형은 변수가 string, number, bool, list, map, object, 등과 같은 모든 유형의 값을 허용할 수 있는 특수 유형 제약 조건이다.
`main.tf`
dynamic "origin" {
for_each = var.origin
content {
domain_name = origin.value.domain_name
origin_id = lookup(origin.value, "origin_id", origin.key)
origin_path = lookup(origin.value, "origin_path", "")
origin_path = element(var.origin_paths, index(var.origin, origin.key)) # Use the element function here
connection_attempts = lookup(origin.value, "connection_attempts", null)
connection_timeout = lookup(origin.value, "connection_timeout", null)
dynamic "s3_origin_config" {
for_each = length(keys(lookup(origin.value, "s3_origin_config", {}))) == 0 ? [] : [lookup(origin.value, "s3_origin_config", {})]
content {
origin_access_identity = lookup(s3_origin_config.value, "cloudfront_access_identity_path", lookup(lookup(aws_cloudfront_origin_access_identity.this, lookup(s3_origin_config.value, "origin_access_identity", ""), {}), "cloudfront_access_identity_path", null))
}
}
}
}
- `for_each = var.origin :` 변수 자체가 list 또는 map 이 될 것으로 예상한다.
- `for_each = [var.origin] :` 변수 자체가 list가 아니더라도 for_each 루프의 입력이 항상 list가 된다.
- `index(list, value)` 는 list 내에서 주어진 value가 처음 나타나는 인덱스(0 기반)를 반환하는 Terraform 함수이다.
결과
동적 origin 블록
- `for_each = var.origin` 을 사용하여 origin 리스트 또는 맵의 모든 항목을 반복하면서 설정을 생성한다.
- 각 `origin` 항목에서 다음과 같은 속성을 설정한다.
- `domain_name`: `origin.value` 객체에서 직접 가져온다.
- `origin_id`: `lookup` 을 사용하여 "origin_id" 속성을 찾고, 없으면 기본값(origin.key)을 사용한다.
- `origin_path`: `lookup` 을 사용하여 "origin_path"를 검색하고, 없으면 빈 문자열("")을 기본값으로 한다.
- `connection_attempts, connection_timeout`: `lookup` 을 사용하여 값이 없으면 `null` 을 기본값으로 설정한다.
중첩된 s3_origin_config 블록
- `s3_origin_config` 속성이 정의되어 있을 경우에만 생성되도록 동적으로 구성한다.
- `lookup(origin.value, "s3_origin_config", {})` 을 사용하여 `s3_origin_config` 객체를 가져온다.
- `length(keys(...)) == 0` 을 통해 속성이 없는 경우 빈 리스트([])를 반환하여 블록 생성을 방지한다.
- 생성될 경우 다음 속성을 포함한다.
- `origin_access_identity: lookup` 을 사용하여 찾으며, 없을 경우 `aws_cloudfront_origin_access_identity.this` 객체에서 조회한다.
- 최종적으로 값이 없으면 `null` 로 설정된다.
element 함수 활용 (origin_path)
- `origin_path` 를 `element` 함수를 이용해 `var.origin_paths` 리스트에서 가져온다.
- `index(var.origin, origin.key)` 를 사용해 현재 오리진의 인덱스를 찾고, `element(var.origin_paths, index(...))` 로 해당 인덱스의 값을 가져온다.
최종 요약
- 동적 `origin` 블록이 `for_each` 를 이용해 리스트 또는 맵의 각 항목을 처리한다.
- `lookup` 을 사용하여 속성이 존재하는지 확인하고, 없을 경우 기본값을 설정한다.
- 중첩된 `s3_origin_config` 블록은 `s3_origin_config` 속성이 있을 때만 생성되도록 한다.
- `element` 함수를 활용하여 특정 리스트에서 값을 동적으로 가져온다.
- 이를 통해 유연하고 동적인 Terraform 인프라 구성이 가능해진다.
Dynamic 블록 / Content 블록
간단히 Dynamic 블록에 대해 설명하자면, dynamic 블록은 dynamic 키워드, 블록 유형 이름 및 `Content` 라는 중첩된 구성 블록을 사용하여 정의된다.
dynamic 블록 내에서 일반적으로 `for_each` 루프를 사용하여 list 또는 map을 반복하여 각 요소에 대한 중첩 블록을 생성한다.
Content 블록은 Dynamic 블록과 함께 사용되어 list 또는 map 변수를 기반으로 여러 중첩 블록을 생성한다.
6. index(Collection Functions)
index목록에서 주어진 값에 대한 요소 인덱스를 찾는다.
index(list, value)
- 반환된 인덱스는 0부터 시작한다. 이 함수는 주어진 값이 목록에 없으면 오류를 생성한다.
Syntax
> index(["a", "b", "c"], "b")
1
마무리
Terraform 함수(Function)를 활용하면 코드의 가독성과 유지보수성을 높일 수 있으며, 조건부 로직을 간결하게 만들 수 있다.
특히, `coalesce, merge, lookup, element` 등의 컬렉션 함수와 `format, substr` 같은 문자열 함수는 실제 인프라 코드에서 자주 사용되므로 익숙해지는 것이 중요하다.
다음 시간에는 Terraform에서 사용되는 Expression(표현식) 에 대해 공부해보도록 하겠다.
Reference
terraform function 공식사이트
terraform value and type
'IaC > Infrastructure Provisioning' 카테고리의 다른 글
| Terraformer란? (0) | 2023.05.04 |
|---|---|
| 5. Terraform의 다양한 Expression (0) | 2023.04.13 |
| 3. Terraform 다양한 변수(variable, local, data...output, input) - AWS (0) | 2023.04.09 |
| 2. Terraform 변수 사용법(use-variable) - AWS (0) | 2023.04.05 |
| 1. Terraform 기초 : 설치 및 .tf 파일 설명 (0) | 2023.04.04 |