인프라 코드로 관리하는 Teraform을 사용할때, 상황에따라 서로 다른 사용자 초기화 스크립트를 적용해야하는 경우가 있을 수 있습니다. 일반적으로는 하나의 스크립트만 사용하면 되지만, PROD, STAGE, DEV 요건에 따라 다양한 조건이 필요할 수 있습니다.
그렇기때문에 조건문을 분기 할 수 있는 방법이 궁금해졌습니다.
if else라는 조건문은 선언형 코드인 테라폼에서 실행할 수 없습니다. 과연, 테라폼에서는 어떻게 조건 분기를 할 수 있을까요?
답은 다음과 같이 count를 이용해 삼항 연산자를 사용해서 구성할 수 있습니다.
var.enable_new_user_data가 variables.tf 파일에서 false로 선언 되어있을 경우 , count = 1-var.enable_new_user_data 값은 1이되어 "user_data"가 실행되게 됩니다. 그리고 "user_data_new"의 경우, 0이므로 실행이 되지 않겠죠!
data "template_file" "user_data" {
count = 1 - var.enable_new_user_data
template = file("${path.module}/user-data.sh")
vars = {
server_port = var.server_port
db_address = data.terraform_remote_state.db.address
db_port = data.terraform_remote_state.db.port
}
}
data "template_file" "user_data_new" {
count = var.enable_new_user_data
template = file("${path.module}/user-data-new.sh")
vars = {
server_port = var.server_port
}
}
간단하게 이런식으로 조건문을 분기처리 할 수 있습니다.
실습에서는 조건 분기 처리를 통해서 각자 다른 사용자 초기화 스크립트가 동작해서 사용자가 웹 브라우저로 vm에 접근했을때 다른 스크립트가 동작했는지 확인하는 실습을 진행해보도록 하겠습니다.
사전 준비
사전적으로 테스트하기 위한 환경에 terraform과 azure cli가 설치되어있어야합니다.
본 실습에서 vm은 azure 환경에 배포하여 실습을 진행하도록 하겠습니다.
1. 테라폼 설치 : https://developer.hashicorp.com/terraform/install
2. az cli 설치 : https://learn.microsoft.com/en-us/cli/azure/install-azure-cli-windows?view=azure-cli-latest&pivots=winget
조건 분기 처리 실습
실습을 진행하기에 앞서, 먼저 프로젝트의 디렉터리 구조는 다음과 같습니다.
간단한 실습진행이기에, 리소스별 분리만 진행하여 테라폼 파일을 생성하였습니다.
cheol@MZC01-CLOUDLSC0406:/mnt/c/code/terraform_code/conditionals_02$ tree
.
├── main.tf
├── network.tf
├── nsg.tf
├── outputs.tf
├── terraform.tfstate
├── terraform.tfstate.backup
├── user-data-new.sh
├── user-data.sh
├── user_data.tf
├── variables.tf
└── vm.tf
1 directory, 11 files
1. main.tf
azure에 리소스를 배포하기 위해서 main.tf에는 프로바이더를 azure로 설정하는 과정을 진행합니다.
그리고 공통적으로 사용되는 리소스 그룹을 생성하도록 합니다.
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.0"
}
}
}
provider "azurerm" {
features {}
}
resource "azurerm_resource_group" "rg" {
name = "terzmy-userdata-rg"
location = "koreacentral"
}
2 network.tf
가상 네트워크 및 서브넷을 생성하는 과정입니다.
추가적으로 vm에 연결하기 위한 네트워크 인터페이스 카드(NIC)를 생성합니다.
resource "azurerm_virtual_network" "vnet" {
name = "terzmy-vnet"
address_space = ["10.0.0.0/16"]
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
}
resource "azurerm_subnet" "subnet" {
name = "vm-snet"
resource_group_name = azurerm_resource_group.rg.name
virtual_network_name = azurerm_virtual_network.vnet.name
address_prefixes = ["10.0.1.0/24"]
}
resource "azurerm_network_interface" "nic" {
name = "userdata-nic"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
ip_configuration {
name = "internal"
subnet_id = azurerm_subnet.subnet.id
private_ip_address_allocation = "Dynamic"
# ✅ Public IP 연결
public_ip_address_id = azurerm_public_ip.pip.id
}
}
resource "azurerm_network_interface_security_group_association" "nic_nsg_assoc" {
network_interface_id = azurerm_network_interface.nic.id
network_security_group_id = azurerm_network_security_group.nsg.id
}
resource "azurerm_public_ip" "pip" {
name = "userdata-pip"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
allocation_method = "Static"
sku = "Basic"
}
3. nsg.tf
네트워크 보안 그룹에 대한 파일입니다.
가상머신에 접근하거나 웹 브라우저를 통해 접근하기 위해서 필요합니다.
# NSG 생성
resource "azurerm_network_security_group" "nsg" {
name = "userdata-nsg"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
security_rule {
name = "Allow-HTTP"
priority = 1001
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "80"
source_address_prefix = "*"
destination_address_prefix = "*"
}
security_rule {
name = "Allow-SSH"
priority = 1002
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "22"
source_address_prefix = "*"
destination_address_prefix = "*"
}
}
3. outputs.tf
vm의 공인 ip를 출력값으로 출력하기 위한 outputs 파일입니다.
output "vm_public_ip" {
value = azurerm_public_ip.pip.ip_address
description = "Public IP address of the VM"
}
4. user-data-new.sh
만약 조건 분기 처리에서 user-data-new의 사용자 초기화 스크립트가 동작하면, "Hello from new scripts v2"가 웹 페이지에 뜨게됩니다.
#!/bin/bash
# busybox 설치
apt-get update
apt-get install -y busybox-static
# 웹 루트 디렉토리 생성
mkdir -p /var/www/html
# index.html 작성
echo "Hello from new script v2" > /var/www/html/index.html
# busybox 웹 서버 실행 (포그라운드)
nohup busybox httpd -f -p 80 -h /var/www/html &
5. user-data.sh
만약 조건 분기 처리에서 user-data의 사용자 초기화 스크립트가 동작하면, "Hello from classic scripts"가 웹 페이지에 뜨게됩니다.
#!/bin/bash
# busybox 설치
apt-get update
apt-get install -y busybox-static
# 웹 루트 디렉토리 생성
mkdir -p /var/www/html
# index.html 작성
echo "Hello from classic script" > /var/www/html/index.html
# busybox 웹 서버 실행 (포그라운드)
nohup busybox httpd -f -p 80 -h /var/www/html &
6. user_data.tf
만약 `enable_new_user_data`가 변수 false로 정의되어 있다면, 기존 스크립트(`user_data.sh`)만 실행되고,
true이면 새 스크립트(`user_data-new.sh`)만 실행됩니다.
즉, 항상 둘 중 하나만 선택되어 VM에 적용되는 구조입니다!
data "template_file" "user_data" {
count = var.enable_new_user_data ? 0 : 1
template = file("${path.module}/user-data.sh")
}
data "template_file" "user_data_new" {
count = var.enable_new_user_data ? 1 : 0
template = file("${path.module}/user-data-new.sh")
}
삼항 연상자의 간단한 정리는 다음과 같아요. 참고하면 이해가 잘 될 거에요!
<조건식> ? <참일 때 값> : <거짓일 때 값>
예) var.is_prod ? 1 : 0
7. variables.tf
변수 설정을 위한 파일입니다. 일단 enable_new_user_data를 false로 설정하고 기존 스크립트(`user_data.sh`)가 실행되도록 진행해보도록 하겠습니다.
variable "enable_new_user_data" {
type = bool
description = "true면 user-data-new.sh를 사용"
default = false
}
variable "vm_size" {
type = string
default = "Standard_B1s"
}
8. vm.tf
vm을 생성하기 위한 테라폼 코드입니다.
resource "azurerm_linux_virtual_machine" "vm" {
name = "userdata-vm"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
network_interface_ids = [azurerm_network_interface.nic.id]
size = var.vm_size
admin_username = "cheol"
admin_password = "pa55w.rd1234"
disable_password_authentication = false
os_disk {
name = "osdisk"
caching = "ReadWrite"
storage_account_type = "Standard_LRS"
}
source_image_reference {
publisher = "Canonical"
offer = "UbuntuServer"
sku = "18.04-LTS"
version = "latest"
}
custom_data = base64encode(
var.enable_new_user_data
? data.template_file.user_data_new[0].rendered
: data.template_file.user_data[0].rendered
)
}
custom_data = base64encode(
var.enable_new_user_data
? data.template_file.user_data_new[0].rendered
: data.template_file.user_data[0].rendered
)
- custom_data : Azure VM에서 초기화 시 실행할 사용자 스크립트를 넣는 속성입니다. 이 값은 반드시 base64로 인코딩 되어야합니다.
- base64encode : Terraform 내장 함수로서, 스트립트 파일 내용을 base64로 인코딩해서 Azure가 이해할 수 있도록 변환 합니다.
- var.enable_new_user_data ? ... : ... 의 경우 Terraform의 if else 문법입니다.
- data.template_file.user_data_new[0].rendered 의 경우 template_file 데이터 소스를 통해 .sh 파일 내용을 불러온 후 .rendered로 텍스트 변환을 진행합니다. [0]은 count=1일 때 생성된 인덱스를 명시적으로 참조하는 것입니다.
전체적인 코드 설명을 진행하였으니, terraform을 이용해서 Azure에 배포를 진행할때 어떤 프로세스를 거쳐서 진행이 되는지 요약하겠습니다.
전체적인 프로세스 요약은 다음과 같습니다.
Terraform 실행 시,
└── 변수 `enable_new_user_data` 값 체크
├── true → user-data-new.sh 읽어서 base64 인코딩
└── false → user-data.sh 읽어서 base64 인코딩
↓
Azure Linux VM 배포 시 custom_data에 삽입
↓
VM 부팅 시 cloud-init이 해당 shell script 실행
그럼 이제 실제로 terraform 코드를 이용해 Azure 환경 구성을 진행해보도록 할게요!
먼저, Azure에 배포를 진행하기 위해서 구독 설정이 정상적으로 되어있는지 확인합니다.
az account show -o table
프로젝트 디렉터리를 초기화 및 계획을 진행해서 어떻게 배포될 것인지 확인을 진행합니다.
terraform init
terraform plan

terraform plan까지 정상적으로 완료되었으면, 배포를 진행할게요!
terraform apply --auto-approve
성공적으로 배포가 진행되었다면, Azure portal로 접속해서 제대로 배포되었는지 확인합니다.
정상적으로 가상 네트워크 및 가상 머신이 배포된 것을 확인할 수 있습니다.

그럼, user_data 사용자 초기화 스크립트가 정상적으로 실행되었는지 가상머신 접속을 통해 확인해 볼게요!


그럼 variables.tf 파일의 enable_new_user_data를 ture로 변경해보겠습니다.
enable_new_user_data를 ture로 변경하고 terraform apply --auto-approve로 배포를 진행하였습니다.
아래와 같이 user-data-new.sh에 해당하는 사용자 초기화 스크립트가 실행된 것을 볼 수 있습니다.

참고!
조건 분기 처리를 진행하면서 재배포하게 되면 public ip가 변경되기도 하는데요..
이건 아래와 같이 public ip 생성시 allocation_method를 Dynamic에서 Static으로 변경해주시면 됩니다.
이상, 테라폼 코드를 통한 조건 분기 처리 실습을 진행해보았습니다.
다음에 또 다른 문법 열심히 배워봐야겠어요!
'Terraform' 카테고리의 다른 글
| [Terraform] Azure Cyclecloud & Managed Lustre Infra 구축 (2) | 2025.09.01 |
|---|---|
| [Terraform] 반복문 및 조건문 (2) | 2025.08.05 |
| [Terraform] 모듈 버전 관리 with Github (2) | 2025.08.04 |
| [Terraform] 테라폼 모듈 (2) | 2025.08.01 |
| [Terraform] 상태 파일(tfstate) 관리 (0) | 2025.07.31 |