본문 바로가기

Cloud/AZURE

Terraform on Azure 정리 (4) - Azure 배포 테스트 : Network

Terraform으로 Azure 필수 리소스 배포 테스트를 진행

참고 문헌 : https://learn.microsoft.com/ko-kr/azure/developer/terraform/create-resource-group?tabs=azure-cli

 

빠른 시작: Terraform을 사용하여 Azure 리소스 그룹 만들기

이 문서에서는 Terraform을 사용하여 Azure 리소스 그룹을 만드는 방법을 알아봅니다.

learn.microsoft.com

 

 

배포 계획

구조: 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. Azure Cloud Shell > 현재 구독 확인

az account set --subscription "<subscription_id_or_subscription_name>"
# 변경 후 확인
az account show

 

2. Terraform 디렉토리 생성

mkdir terraform-test
cd terraform-test

 

3. Terraform 디렉토리 기본 구성

terraform/
│
├── provider.tf          # Azure Provider 정의
├── variables.tf         # 공통 변수 정의
├── outputs.tf           # 필요한 출력 정의 (옵션)
│
├── network.tf           # Hub-Spoke VNet, Subnet
├── security.tf          # NSG, Firewall, VPN Gateway
├── compute.tf           # VM 정의
├── database.tf          # Azure Database for MySQL
├── tags.tf              # 태그 관련 공통 모듈 (선택)

 

4. versions.tf

Terraform 및 Azure Provider 버전 고정

- vi 편집기로 tf 파일 생성

vi versions.tf
# 아래는 vi 편집기 입력 내용
terraform {
  required_version = ">= 1.6.0"
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 3.0"
    }
  }
}

 

5. provider.tf

Azure 인증 및 초기화 설정

provider "azurerm" {
  features {}
}

 

6. variables.tf

리소스 배포 시 유연성과 재사용성을 위해 다양한 입력값(예: 리전, 태그, 리소스명, VM 구성 정보 등)을 변수로 선언

# 리전
variable "location" {
  description = "Azure region to deploy all resources"
  type        = string
  default     = "koreacentral"
}

# 리소스 그룹 이름 정의
variable "resource_groups" {
  description = "Resource group names by purpose"
  type = object({
    network  = string
    security = string
    compute  = string
    storage  = string
  })
  default = {
    network  = "LJY-RG-NETWORK"
    security = "LJY-RG-SECURITY"
    compute  = "LJY-RG-COMPUTE"
    storage  = "LJY-RG-STORAGE"
  }
}

# VM 정의
# VM을 효율적으로 배포하려면 각 VM의 정보를 구조화된 variable로 관리하는 것이 좋음

# Web VM
variable "vms" {
  description = "List of web virtual machines to deploy"
  type = list(object({
    name            = string
    environment     = string
    resource_name   = string
    deployment_date = string
    tags            = map(string)
    size            = string
    admin_username  = string
    admin_password  = string
  }))
  default = [
    {
      name            = "prd-web-01"
      environment     = "PRD"
      resource_name   = "prd-web-01"
      deployment_date = "2025-05-07"
      size            = "Standard_B2s"
      admin_username  = "azureuser"
      admin_password  = "P@ssw0rdWebPrd"
      tags = {
        ResourceName   = "prd-web-01"
        ServiceType    = "WEB"
        Environment    = "PRD"
        DeploymentDate = "2025-05-07"
      }
    },
    {
      name            = "dev-web-01"
      environment     = "DEV"
      resource_name   = "dev-web-01"
      deployment_date = "2025-05-07"
      size            = "Standard_B2s"
      admin_username  = "azureuser"
      admin_password  = "P@ssw0rdWebDev"
      tags = {
        ResourceName   = "dev-web-01"
        ServiceType    = "WEB"
        Environment    = "DEV"
        DeploymentDate = "2025-05-07"
      }
    }
  ]
}

# MySQL 정의
variable "mysql_servers" {
  description = "Azure Database for MySQL Flexible Server instances"
  type = list(object({
    name            = string
    environment     = string
    resource_name   = string
    deployment_date = string
    sku_name        = string
    version         = string
    admin_username  = string
    admin_password  = string
    storage_mb      = number
    tags            = map(string)
  }))
  default = [
    {
      name            = "prd-db-01"
      environment     = "PRD"
      resource_name   = "prd-db-01"
      deployment_date = "2025-05-07"
      sku_name        = "Standard_D2ds_v4"
      version         = "8.0"
      admin_username  = "mysqladmin"
      admin_password  = "P@ssw0rdDbPrd"
      storage_mb      = 51200
      tags = {
        ResourceName   = "prd-db-01"
        ServiceType    = "DB"
        Environment    = "PRD"
        DeploymentDate = "2025-05-07"
      }
    },
    {
      name            = "dev-db-01"
      environment     = "DEV"
      resource_name   = "dev-db-01"
      deployment_date = "2025-05-07"
      sku_name        = "Standard_D2ds_v4"
      version         = "8.0"
      admin_username  = "mysqladmin"
      admin_password  = "P@ssw0rdDbDev"
      storage_mb      = 51200
      tags = {
        ResourceName   = "dev-db-01"
        ServiceType    = "DB"
        Environment    = "DEV"
        DeploymentDate = "2025-05-07"
      }
    }
  ]
}

 

7. outputs.tf (옵션)

Terraform이 리소스를 배포한 후 사용자에게 필요한 중요한 정보를 출력해주는 역할

  예 ) VM IP, MySQL 접속 정보, 리소스 그룹 이름 등

해당 테스트에서는 생략

 

8. network.tf (Hub-Spoke 구성)

** subnet에는 tags 지원하지 않음

유형 VNet 이름 Subnet 범위
Hub vnet-hub
10.0.0.0/16
(VPN)Gateway Subnet
Firewall Subnet
10.0.1.0/27
10.0.2.0/26
PRD vnet-prd
10.1.0.0/16
snet-prd-web
snet-prd-db
10.1.1.0/24
10.1.2.0/24
DEV vnet-dev
10.2.0.0/16
snet-dev-web
snet-dev-db
10.2.1.0/24
10.2.2.0/24
#########################
# Resource Group for Storage
#########################
# storage account 배포안할거라 미리 RG만 여기서 함께 생성
resource "azurerm_resource_group" "storage_rg" {
  name     = var.resource_groups.storage
  location = var.location
}




#########################
# Resource Group for Security
#########################
resource "azurerm_resource_group" "security_rg" {
  name     = var.resource_groups.security # variables에서 정의한 RG 이름 사용
  location = var.location # variables에서 정의한 리전 사용
}

# Hub VNet 
# Firewall과 subnet의 RG 위치가 같아야함
resource "azurerm_virtual_network" "hub_vnet" {
  name                = "vnet-hub"
  address_space       = ["10.0.0.0/16"]
  location            = azurerm_resource_group.security_rg.location # security_rg로 변경
  resource_group_name = azurerm_resource_group.security_rg.name # security_rg로 변경

  tags = {
    ResourceName    = "hub-vnet"
    DeploymentDate  = "2025-05-07"
  }
}

# Gateway Subnet (Required for VPN Gateway)
resource "azurerm_subnet" "hub_gateway_subnet" {
  name                 = "GatewaySubnet"
  resource_group_name  = azurerm_resource_group.security_rg.name # security_rg로 변경
  virtual_network_name = azurerm_virtual_network.hub_vnet.name
  address_prefixes     = ["10.0.1.0/27"]
}

# Firewall Subnet (Required for Azure Firewall)
resource "azurerm_subnet" "hub_firewall_subnet" {
  name                 = "AzureFirewallSubnet"
  resource_group_name  = azurerm_resource_group.security_rg.name # security_rg로 변경
  virtual_network_name = azurerm_virtual_network.hub_vnet.name
  address_prefixes     = ["10.0.2.0/26"]
}


#########################
# Resource Group for Network
#########################
resource "azurerm_resource_group" "network_rg" {
  name     = var.resource_groups.network # variables에서 정의한 RG 이름 사용
  location = var.location # variables에서 정의한 리전 사용
}

# PRD VNet
resource "azurerm_virtual_network" "prd_vnet" {
  name                = "vnet-prd"
  address_space       = ["10.1.0.0/16"]
  location            = azurerm_resource_group.network_rg.location
  resource_group_name = azurerm_resource_group.network_rg.name

  tags = {
    ResourceName    = "vnet-prd"
    DeploymentDate  = "2025-05-07"
  }
}

resource "azurerm_subnet" "prd_subnet_web" {
  name                 = "prd-web-subnet"
  resource_group_name  = azurerm_resource_group.network_rg.name
  virtual_network_name = azurerm_virtual_network.prd_vnet.name
  address_prefixes     = ["10.1.1.0/24"]
}

resource "azurerm_subnet" "prd_subnet_db" {
  name                 = "prd-db-subnet"
  resource_group_name  = azurerm_resource_group.network_rg.name
  virtual_network_name = azurerm_virtual_network.prd_vnet.name
  address_prefixes     = ["10.1.2.0/24"]
}

# DEV VNet
resource "azurerm_virtual_network" "dev_vnet" {
  name                = "vnet-dev"
  address_space       = ["10.2.0.0/16"]
  location            = azurerm_resource_group.network_rg.location
  resource_group_name = azurerm_resource_group.network_rg.name

  tags = {
    ResourceName    = "vnet-dev"
    DeploymentDate  = "2025-05-07"
  }
}

resource "azurerm_subnet" "dev_subnet_web" {
  name                 = "dev-web-subnet"
  resource_group_name  = azurerm_resource_group.network_rg.name
  virtual_network_name = azurerm_virtual_network.dev_vnet.name
  address_prefixes     = ["10.2.1.0/24"]
}

resource "azurerm_subnet" "dev_subnet_db" {
  name                 = "dev-db-subnet"
  resource_group_name  = azurerm_resource_group.network_rg.name
  virtual_network_name = azurerm_virtual_network.dev_vnet.name
  address_prefixes     = ["10.2.2.0/24"]
}

#########################
# Hub ↔ PRD Peering
#########################

resource "azurerm_virtual_network_peering" "hub_to_prd" {
  name                      = "hub-to-prd"
  resource_group_name       = azurerm_resource_group.security_rg.name
  virtual_network_name      = azurerm_virtual_network.hub_vnet.name
  remote_virtual_network_id = azurerm_virtual_network.prd_vnet.id
  allow_forwarded_traffic   = true
  allow_gateway_transit     = false
  use_remote_gateways       = false
}

resource "azurerm_virtual_network_peering" "prd_to_hub" {
  name                      = "prd-to-hub"
  resource_group_name       = azurerm_resource_group.network_rg.name
  virtual_network_name      = azurerm_virtual_network.prd_vnet.name
  remote_virtual_network_id = azurerm_virtual_network.hub_vnet.id
  allow_forwarded_traffic   = true
  allow_gateway_transit     = false
  use_remote_gateways       = false
}

#########################
# Hub ↔ DEV Peering
#########################

resource "azurerm_virtual_network_peering" "hub_to_dev" {
  name                      = "hub-to-dev"
  resource_group_name       = azurerm_resource_group.security_rg.name 
  # hub Vnet은 VPN, Firewall 용도로 Security RG에 위치!함을 주의
  virtual_network_name      = azurerm_virtual_network.hub_vnet.name
  remote_virtual_network_id = azurerm_virtual_network.dev_vnet.id
  allow_forwarded_traffic   = true
  allow_gateway_transit     = false
  use_remote_gateways       = false
}

resource "azurerm_virtual_network_peering" "dev_to_hub" {
  name                      = "dev-to-hub"
  resource_group_name       = azurerm_resource_group.network_rg.name
  virtual_network_name      = azurerm_virtual_network.dev_vnet.name
  remote_virtual_network_id = azurerm_virtual_network.hub_vnet.id
  allow_forwarded_traffic   = true
  allow_gateway_transit     = false
  use_remote_gateways       = false
}

 

9. Terraform 설정 시작

- terraform init를 실행하여 Terraform 배포를 초기화

이 명령은 Azure 리소스를 관리하는 데 필요한 Azure 공급자(provider)를 다운로드함

terraform init -upgrade

 

- network.tf 작성 후 terraform validate를 통해 설정 유효성 검사

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

중략

- # az account show로 현재 구독 위치 확인

 

- terraform apply로 적용 (yes 입력)

중략

 

10. Azure 콘솔에서 Network 리소스 배포 확인

 

- 아래 명령어로 생성된 subnet까지 확인

az network vnet subnet list --resource-group <리소스_그룹_이름> --vnet-name <VNet_이름> --output table

- Vnet Peering 확인