Terraform 구성 언어
Terraform 구성 언어 (Configuration Language)
Terraform이 "무엇을 만들지" 알려면 그 내용을 코드로 적어 줘야 해요. 그 코드를 쓰는 언어가 바로 **Terraform 구성 언어(Configuration Language)**이고, 줄여서 HCL이라고 불러요. 이 언어의 가장 중심 목적은 **리소스(resource)**를 선언하는 일이고, 나머지 문법 요소들은 모두 그 리소스 정의를 더 유연하고 편리하게 만들기 위해 존재해요.
구성 언어는 Terraform CLI뿐 아니라 HCP Terraform, Terraform Enterprise에서도 공통으로 쓰는 기본 인터페이스예요. 이 언어로 적은 설정 파일은 Terraform에게 어떤 플러그인을 설치하고, 어떤 인프라를 만들고, 어떤 데이터를 가져올지 알려줘요. 또한 리소스 사이의 의존 관계를 정의하거나, 하나의 구성 블록으로 비슷한 리소스를 여러 개 만들 수도 있게 해 줍니다.
구성의 최소 요소
구성 언어의 문법은 사실 몇 가지 요소로 이루어져 있어요. 블록과 인자, 표현식이 그 전부인데요, resource 블록 하나를 보면 바로 감이 와요.
resource "aws_vpc" "main" {
cidr_block = var.base_cidr_block
}
이 블록을 일반화하면 아래 모양이에요. 블록 타입 뒤에 라벨이 붙고, 블록 본문에는 인자와 중첩 블록이 들어가요.
<BLOCK TYPE> "<BLOCK LABEL>" "<BLOCK LABEL>" {
# Block body
<IDENTIFIER> = <EXPRESSION> # Argument
}
- 블록(Block) — 다른 내용을 담는 컨테이너예요. 주로 리소스 같은 객체의 구성을 나타내요. 블록 타입이 있고, 라벨이 0개 이상 붙을 수 있으며, 본문에는 인자와 중첩 블록이 몇 개든 들어가요. 대부분의 기능이 설정 파일의 최상위 블록으로 제어돼요.
- 인자(Argument) — 이름에 값을 할당해요. 항상 블록 안에 나타나요.
- 표현식(Expression) — 값을 나타내요. 리터럴이거나 다른 값을 참조·결합한 형태이며, 인자의 값이나 다른 표현식 안에 들어가요.
선언적이라는 것의 의미
Terraform 언어는 **선언적(declarative)**이에요. 목표 상태를 그려 주는 것이지, 그 상태에 도달하기 위한 절차를 나열하지 않아요. 그래서 블록의 순서나 파일이 정리된 방식은 대체로 중요하지 않아요. Terraform은 리소스 사이의 암묵적·명시적 관계만 보고 작업 순서를 정해요.
AWS에 간단한 네트워크 토폴로지를 만드는 예시로 전체 구조를 보여 드릴게요. 프로바이더를 지정하고, 변수를 선언하고, VPC와 서브넷 리소스를 만드는 흐름이 자연스럽게 이어져요.
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 1.0.4"
}
}
}
variable "aws_region" {}
variable "base_cidr_block" {
description = "A /16 CIDR range definition, such as 10.1.0.0/16, that the VPC will use"
default = "10.1.0.0/16"
}
variable "availability_zones" {
description = "A list of availability zones in which to create subnets"
type = list(string)
}
provider "aws" {
region = var.aws_region
}
resource "aws_vpc" "main" {
# Referencing the base_cidr_block variable allows the network address
# to be changed without modifying the configuration.
cidr_block = var.base_cidr_block
}
resource "aws_subnet" "az" {
# Create one subnet for each given availability zone.
count = length(var.availability_zones)
# For each subnet, use one of the specified availability zones.
availability_zone = var.availability_zones[count.index]
# By referencing the aws_vpc.main object, Terraform knows that the subnet
# must be created only after the VPC is created.
vpc_id = aws_vpc.main.id
# Built-in functions and operators can be used for simple transformations of
# values, such as computing a subnet address. Here we create a /20 prefix for
# each subnet, using consecutive addresses for each availability zone,
# such as 10.1.16.0/20 .
cidr_block = cidrsubnet(aws_vpc.main.cidr_block, 4, count.index+1)
}
여기서 놓치기 쉬운 포인트가 두 가지예요. aws_subnet.az가 aws_vpc.main.id를 참조하는 것만으로 Terraform이 "VPC가 먼저 만들어져야 한다"는 의존 관계를 스스로 알아챈다는 점, 그리고 cidrsubnet() 같은 내장 함수로 값 변환을 표현식 안에서 할 수 있다는 점이에요. 코드가 스스로 어떤 흐름으로 만들어지는지를 설명하고 있죠.
더 알아보기
- 리소스 다루기 — resource 블록의 전체 구조와 메타 인자
- 공식 문서: Terraform Language, Write Terraform Configuration 튜토리얼