본문 바로가기

Cloud/AZURE

Terraform on Azure [모듈화] - (6) Firewall & UDR 모듈

배포 계획

구조: Hub-Spoke 네트워크 모델
- Hub: 공통 인프라 구성 (VPN Gateway, Firewall 등)
- Spoke: 업무별 구분된 네트워크 (Web, WAS, DB 등 리소스 배치)

보안 구성
- NSG (Network Security Group): 각 Spoke VNet 및 서브넷에 할당
- VPN Gateway: Hub VNet에 배치
- Firewall: Hub VNet에 배치 및 UDR과 연동

리소스 그룹 구조
- RG-NETWORK – 가상 네트워크 및 서브넷 등 네트워크 리소스
- RG-SECURITY – NSG, Firewall, VPN 등 보안 리소스
- RG-COMPUTE – 가상 머신 등 컴퓨트 리소스
- RG-STORAGE – Storage Account 등 저장소 리소스

Tag 구성
- Key : Value 예시
- ResourceName : vm-web-prd-01 등
- ServiceType : WEB, WAS, DB 등
- Environment : PRD, QA, DEV
- DeploymentDate : 2025-05-07 등

 


1. Firewall 모듈

- modules/firewall/main.tf

variable "firewalls" {
  description = "Firewall 구성 정보 map"
  type        = map(any)
  default     = {}
}

variable "location" {
  description = "리소스 위치"
  type        = string
}

variable "resource_groups" {
  description = "리소스 그룹 이름 map (예: { security = \"rg-security\" })"
  type        = map(string)
}

variable "subnet_ids" {
  description = "Subnet ID map (예: { firewall = \"subnet-id\" })"
  type        = map(string)
}

resource "azurerm_public_ip" "firewall_pip" {
  for_each            = var.firewalls

  name                = each.value.pip_name
  location            = var.location
  resource_group_name = var.resource_groups.security
  allocation_method   = "Static"
  sku                 = "Standard"

  tags = merge({
    DeploymentDate = "2025-05-07"
  }, each.value.tags)
}

resource "azurerm_firewall" "hub_fw" {
  for_each            = var.firewalls

  name                = each.value.fw_name
  location            = var.location
  resource_group_name = var.resource_groups.security

  ip_configuration {
    name                 = each.value.ip_config_name
    subnet_id            = var.subnet_ids[each.key]
    public_ip_address_id = azurerm_public_ip.firewall_pip[each.key].id
  }

  sku_name = "AZFW_VNet"
  sku_tier = "Standard"

  tags = merge({
    DeploymentDate = "2025-05-07"
  }, each.value.tags)
}

- main.tf

module "firewall" {
  source   = "./modules/firewall"
  location = var.location

  resource_groups = {
    security = module.resource_groups.resource_group_names["security"]
  }
  subnet_ids = {
    fw01 = module.subnet.subnet_ids["firewall"]
  }
  firewalls = {
  fw01 = {
    pip_name       = "hub-fw-pip"
    fw_name        = "hub-fw"
    ip_config_name = "hub-fw-ipcfg"
    tags           = {
      DeploymentDate = "2025-05-07"
    }
  }
}
}

 

terraform validate를 통해 설정 유효성 검사

terraform plan을 통해 리소스 생성 계획 확인

terraform apply -auto-approve (yes 자동 입력 )

 

2. UDR 모듈

- UDR : User Defined Route, 사용자 지정 경로

- Firewall을 통하는 inbound traffic은 DNAT으로 처리되나, VM outbound는 Firewall을 통해서 외부로 나갈 수 있도록 URD 설정을 해주어야함 

- (Spoke) PRD web VM이 Hub의 Azure Firewall을 통해 인터넷이나 외부 네트워크로 나가도록 유도하는 UDR(User Defined Route) 설정

** UDR과 VM이 동일한 RG에 존재하지 않아도 됨. UDR은 Subnet에 연결되므로, VM의 NIC가 해당 Subnet에 속해 있다면 라우팅 정책이 잘 적용됨.

 

- modules/udr/main.tf

variable "route_tables" {
  description = "Map of route tables"
  type = map(object({
    name                = string
    location            = string
    resource_group_name = string
    tags                = map(string)
  }))
  default = {}
}

variable "routes" {
  description = "Map of routes, keyed by route name"
  type = map(object({
    route_table_name       = string
    resource_group_name    = string
    address_prefix         = string
    next_hop_type          = string
    next_hop_in_ip_address = string
  }))
  default = {}
}

variable "associations" {
  description = "Map of subnet associations"
  type = map(object({
    subnet_id      = string
    route_table_id = string
  }))
  default = {}
}

# Route Table 생성
resource "azurerm_route_table" "this" {
  for_each = var.route_tables

  name                = each.value.name
  location            = each.value.location
  resource_group_name = each.value.resource_group_name
  tags                = each.value.tags
}

# Route 생성
resource "azurerm_route" "this" {
  for_each = var.routes

  name                   = each.key
  resource_group_name    = each.value.resource_group_name
  route_table_name       = each.value.route_table_name
  address_prefix         = each.value.address_prefix
  next_hop_type          = each.value.next_hop_type
  next_hop_in_ip_address = each.value.next_hop_in_ip_address
}

# Subnet-Route Table 연결
resource "azurerm_subnet_route_table_association" "this" {
  for_each = var.associations

  subnet_id      = each.value.subnet_id
  route_table_id = each.value.route_table_id
}

# parent 모듈 associations 참조용
output "route_table_ids" {
  value = { for k, v in azurerm_route_table.this : k => v.id }
}

- main.tf

module "udr" {
  source = "./modules/udr"

  route_tables = {
    prd-web = {
      name                = "prd-web-subnet-rt"
      location            = var.location
      resource_group_name = module.resource_groups.resource_group_names["network"]
      tags = {
        ResourceName   = "prd-web-subnet-rt"
        Environment    = "PRD"
        DeploymentDate = "2025-05-07"
      }
    }
  }

  routes = {
    default-to-fw = {
      route_table_name       = "prd-web-subnet-rt"
      resource_group_name    = module.resource_groups.resource_group_names["network"]
      address_prefix         = "0.0.0.0/0"
      next_hop_type          = "VirtualAppliance"
      next_hop_in_ip_address = module.firewall.private_ip_address["fw01"] # firewall output에서 참조
    }
  }

  associations = {
    prd-web = {
      subnet_id = module.subnet.subnet_ids["prd_web"]
      route_table_id = module.udr.route_table_ids["prd-web"]
    }
  }
}

- - modules/firewall/main.tf

** routes (경로) 생성시 기입할 next hop 참조값으로서, 서로다른 모듈에 있기 때문에 firewall에 output 값 추가

# udr (vm->fw)에서 next hop 참조용
output "private_ip_address" {
  value = {
    for key, fw in azurerm_firewall.hub_fw :
    key => fw.ip_configuration[0].private_ip_address
  }
}

 

terraform validate를 통해 설정 유효성 검사

terraform plan을 통해 리소스 생성 계획 확인

terraform apply -auto-approve (yes 자동 입력 )