Terraform에서 arbitrary expressions는 별도의 특수 문법 이름이라기보다, 인수 값에 리터럴만 쓰지 않고 참조·연산·함수·조건식처럼 값을 계산하는 표현식을 사용할 수 있다는 뜻에 가깝다.
기존 메모에서는 이를 “임의 표현식”이라고 번역하고 내장 함수와 연산자를 쓸 수 있다고만 적었다. 핵심은 맞지만, 모든 위치에서 모든 표현식을 쓸 수 있는 것은 아니다. 어떤 블록과 인수에 표현식이 놓였는지에 따라 허용되는 타입과 참조 범위가 달라진다.
인수 오른쪽에는 값이 되는 표현식이 온다
Terraform 설정의 기본 모양은 블록, 인수, 표현식으로 나뉜다.
resource "example_server" "app" {
name = "api-server"
}
resource는 블록 유형이다.example_server,app은 블록 레이블이다.name은 인수 이름이다."api-server"는 문자열 값을 만드는 리터럴 표현식이다.
리터럴 대신 다른 값을 참조하거나 계산할 수도 있다.
variable "environment" {
type = string
}
locals {
service_name = "api-${var.environment}"
}
local.service_name의 오른쪽은 문자열 템플릿이며, var.environment를 참조해 새 값을 만든다. 표현식의 결과는 항상 어떤 값이 되고, 그 값에는 타입이 있다.
Terraform 값의 타입부터 확인한다
Terraform 표현식이 만드는 주요 타입은 다음과 같다.
| 구분 | 예시 | 주의할 점 |
|---|---|---|
string |
"prod" |
문자열 템플릿에서 다른 타입이 변환될 수 있다. |
number |
3, 1.5 |
정수와 실수를 별도 타입으로 나누지 않는다. |
bool |
true |
조건식의 조건은 논리값이어야 한다. |
list·tuple |
["a", "b"] |
순서가 있고 인덱스로 접근한다. |
set |
toset(["a", "b"]) |
순서와 중복이 없다. 인덱스로 접근하지 않는다. |
map·object |
{ name = "api" } |
키 또는 속성 이름으로 접근한다. |
null |
null |
문맥에 따라 값을 생략한 것처럼 처리될 수 있다. |
자동 타입 변환이 가능한 문맥도 있지만, 그 동작에 기대면 읽기 어렵고 조건식에서 예상하지 못한 타입이 나올 수 있다. 의도가 분명해야 할 때는 tostring, tonumber, tolist 같은 변환 함수를 명시한다.
자주 쓰는 표현식 네 가지
참조와 문자열 템플릿
locals {
full_name = "${var.environment}-api"
}
리소스 속성, 입력 변수, 로컬 값, 모듈 출력 등을 참조할 수 있다. 다만 아직 결정되지 않은 리소스 속성은 계획 단계에서 unknown 값이 될 수 있다. 이를 빈 문자열이나 null과 같다고 취급하면 안 된다.
연산자
locals {
enough_replicas = var.replica_count >= 2
use_cache = var.environment == "prod" && var.cache_enabled
}
산술, 비교, 동등성, 논리 연산자를 조합할 수 있다. 복잡한 조건은 괄호와 의미 있는 로컬 이름으로 나누는 편이 읽기 쉽다.
조건식
locals {
instance_class = var.environment == "prod" ? "large" : "small"
}
조건식은 condition ? true_value : false_value 형태다. 두 결과는 Terraform이 하나의 공통 타입으로 판단할 수 있어야 한다. 숫자와 문자열처럼 자동 변환이 가능한 조합도 있지만, 유지보수를 위해 결과 타입을 맞추는 편이 낫다.
for 식
variable "service_names" {
type = list(string)
}
locals {
normalized_names = [
for name in var.service_names : lower(trimspace(name))
]
}
for 식은 컬렉션을 다른 컬렉션으로 변환한다. 블록을 반복 생성하는 for_each와 이름이 비슷하지만 역할은 다르다.
for식: 값을 변환해 새 값 하나를 만든다.for_each: 리소스나 모듈 인스턴스를 여러 개 만든다.dynamic블록: 반복 가능한 중첩 블록을 동적으로 만든다.
모든 문맥이 arbitrary한 것은 아니다
HashiCorp 문서도 일부 문맥에서는 표현식 사용이 제한된다고 명시한다.
예를 들어 variable 블록의 type에는 일반 값 표현식이 아니라 타입 제약 문법이 온다. required_providers의 버전 제약처럼 특정 형식의 문자열을 요구하는 자리도 있다. 리소스 인수는 provider 스키마가 요구하는 타입을 따라야 한다.
따라서 오류를 볼 때는 “표현식 문법이 맞는가”만 보지 말고 다음을 함께 확인한다.
- 이 위치가 일반 값 표현식을 허용하는가?
- 표현식 결과 타입이 인수가 요구하는 타입과 맞는가?
- 참조한 값이 현재 단계에서 알려져 있는가?
null, 빈 컬렉션,unknown을 같은 의미로 오해하지 않았는가?
terraform console로 작은 식부터 확인한다
리터럴과 함수, 조건식의 동작은 terraform console에서 빠르게 확인할 수 있다.
terraform console
> 1 + 2 * 3
7
> [for s in ["API", " Web "] : lower(trimspace(s))]
[
"api",
"web",
]
변수나 리소스 참조가 포함된 식은 현재 설정과 상태에 따라 결과가 달라질 수 있다. 콘솔에서 리터럴 예제가 계산됐다는 사실이 실제 provider 설정과 terraform plan까지 유효하다는 뜻은 아니다.
표현식을 작성한 뒤에는 다음 경계를 나눠 확인한다.
terraform fmt -check
terraform validate
terraform plan
fmt는 형식을 정리한다.validate는 구성의 문법과 내부 일관성을 확인한다.plan은 provider와 현재 상태를 포함해 예상 변경을 계산한다.
이 글의 예제는 Terraform 언어 문법을 설명하기 위한 것이며 실제 인프라에 적용한 결과를 의미하지 않는다.
참고 자료
'배움과 성장 > DevOps·클라우드' 카테고리의 다른 글
| Kubernetes 컨트롤 플레인과 워커 노드: Pod가 실행되는 통신 흐름 (0) | 2024.08.13 |
|---|---|
| GitHub 인증 오류 해결: 비밀번호 대신 CLI·Credential Manager·SSH 사용하기 (4) | 2023.04.09 |
| Docker란 무엇인가: 이미지·컨테이너·VM 차이와 격리 구조 (0) | 2023.03.25 |
| Docker 컨테이너 생명주기: run·start·stop·rm·commit 차이 (0) | 2023.03.25 |
| 신입 DevOps 엔지니어 학습 순서: Linux·네트워크부터 CI/CD·Cloud까지 (0) | 2023.03.15 |
댓글