Поделиться через


Быстрый старт: Создание канала ExpressRoute и шлюза виртуальной сети с помощью Terraform

В этом кратком руководстве вы используете Terraform для создания канала Azure ExpressRoute и связанной с ней инфраструктуры. Шаблон Terraform создает полную настройку ExpressRoute, включая виртуальную сеть, шлюз ExpressRoute, конфигурацию канала и частный пиринг. Все ресурсы развертываются с настраиваемыми параметрами, позволяющими настраивать развертывание для конкретных требований.

Схема среды развертывания канала Azure ExpressRoute с помощью Terraform.

Terraform поддерживает определение, предварительный просмотр и развертывание облачной инфраструктуры. С помощью Terraform можно создавать файлы конфигурации с применением синтаксиса HCL. Синтаксис HCL позволяет указать поставщика облачных служб, например Azure, и элементы, составляющие облачную инфраструктуру. После создания файлов конфигурации создается план выполнения, который позволяет предварительно просматривать изменения инфраструктуры до их развертывания. После проверки изменений примените план выполнения для развертывания инфраструктуры.

Вы узнаете, как выполнять следующие задачи:

  • Создание группы ресурсов Azure с уникальным именем
  • Создание виртуальной сети с подсетью для шлюза
  • Создайте шлюз ExpressRoute с настраиваемым SKU
  • Создание канала ExpressRoute с настраиваемыми параметрами поставщика услуг
  • Настройка частного соединения для канала ExpressRoute
  • Вывести идентификаторы ключевых ресурсов и сведения о конфигурации

Необходимые компоненты

Реализация кода Terraform

  1. Создайте каталог, в котором необходимо протестировать и запустить пример кода Terraform и сделать его текущим каталогом.

  2. Создайте файл с именем main.tfи вставьте следующий код:

    # Create Resource Group
    resource "random_pet" "rg_name" {
      prefix = var.resource_group_name_prefix
    }
    
    resource "azurerm_resource_group" "rg" {
      location = var.resource_group_location
      name     = random_pet.rg_name.id
      tags     = var.tags
    }
    
    # Random String for unique naming
    resource "random_string" "name" {
      length  = 8
      special = false
      upper   = false
      lower   = true
      numeric = false
    }
    
    # Create Virtual Network
    resource "azurerm_virtual_network" "vnet" {
      name                = "vnet-${random_string.name.result}"
      address_space       = var.virtual_network_address_space
      location            = azurerm_resource_group.rg.location
      resource_group_name = azurerm_resource_group.rg.name
      tags                = var.tags
    }
    
    # Create ExpressRoute Gateway using Azure Verified Module with HOBO
    module "expressroute_gateway" {
      source  = "Azure/avm-ptn-vnetgateway/azurerm"
      version = "~> 0.10.0"
    
      # Basic Configuration
      location  = azurerm_resource_group.rg.location
      name      = "vgw-${random_string.name.result}"
      parent_id = azurerm_resource_group.rg.id
    
      # ExpressRoute Gateway Configuration
      type                                  = "ExpressRoute"
      sku                                   = var.gateway_sku
      hosted_on_behalf_of_public_ip_enabled = var.enable_hosted_on_behalf_of_public_ip # Enable Azure-managed public IP (HOBO)
    
      # Virtual Network Configuration
      virtual_network_id    = azurerm_virtual_network.vnet.id
      subnet_address_prefix = var.gateway_subnet_address_prefix # GatewaySubnet CIDR
    
      # Optional: Enable telemetry for Azure Verified Module
      enable_telemetry = true
    
      # Express Route Circuit Connection (if circuit is provided)
      express_route_circuits = var.express_route_circuit_id != null ? {
        "primary" = {
          id = var.express_route_circuit_id
          connection = {
            authorization_key = var.express_route_authorization_key
          }
        }
      } : {}
    
      tags = merge(var.tags, {
        environment  = "production"
        project      = "expressroute-hobo"
        gateway_type = "ExpressRoute"
        deployment   = "azure-verified-module"
      })
    
      depends_on = [azurerm_virtual_network.vnet]
    }
    
    # Create ExpressRoute Circuit (if enabled)
    resource "azurerm_express_route_circuit" "circuit" {
      count = var.create_express_route_circuit ? 1 : 0
    
      name                  = "erc-${random_string.name.result}"
      resource_group_name   = azurerm_resource_group.rg.name
      location              = azurerm_resource_group.rg.location
      service_provider_name = var.service_provider_name
      peering_location      = var.peering_location
      bandwidth_in_mbps     = var.bandwidth_in_mbps
    
      sku {
        tier   = var.circuit_sku_tier
        family = var.circuit_sku_family
      }
    
      tags = merge(var.tags, {
        environment = "production"
        project     = "expressroute-hobo"
      })
    }
    
    # Create ExpressRoute Circuit Peering (if circuit is created)
    resource "azurerm_express_route_circuit_peering" "private" {
      count = var.create_express_route_circuit && var.create_private_peering ? 1 : 0
    
      peering_type                  = "AzurePrivatePeering"
      express_route_circuit_name    = azurerm_express_route_circuit.circuit[0].name
      resource_group_name           = azurerm_resource_group.rg.name
      primary_peer_address_prefix   = var.primary_peer_address_prefix
      secondary_peer_address_prefix = var.secondary_peer_address_prefix
      vlan_id                       = var.vlan_id
      peer_asn                      = var.peer_asn
    }
    
  3. Создайте файл с именем outputs.tfи вставьте следующий код:

    output "resource_group_name" {
      description = "Name of the resource group"
      value       = azurerm_resource_group.rg.name
    }
    
    output "express_route_circuit_id" {
      description = "ID of the ExpressRoute circuit (if created)"
      value       = var.create_express_route_circuit ? azurerm_express_route_circuit.circuit[0].id : null
    }
    
    output "express_route_circuit_service_key" {
      description = "Service key for the ExpressRoute circuit (if created)"
      value       = var.create_express_route_circuit ? azurerm_express_route_circuit.circuit[0].service_key : null
      sensitive   = true
    }
    
    output "gateway_id" {
      description = "ID of the ExpressRoute Virtual Network Gateway"
      value       = module.expressroute_gateway.virtual_network_gateway.id
    }
    
    output "gateway_name" {
      description = "Name of the ExpressRoute Virtual Network Gateway"
      value       = module.expressroute_gateway.virtual_network_gateway.name
    }
    
    output "gateway_subnet_id" {
      description = "ID of the GatewaySubnet created by the module"
      value       = module.expressroute_gateway.subnet.id
    }
    
    output "hosted_on_behalf_of_public_ip_note" {
      description = "Information about the Azure-managed public IP for ExpressRoute gateway"
      value       = "This ExpressRoute Virtual Network Gateway uses an Azure-managed public IP address. The public IP is automatically provisioned and managed by Azure, and is not visible in your subscription's public IP resources. This feature is only available for ExpressRoute gateways, not VPN gateways."
    }
    
    output "public_ip_addresses" {
      description = "Public IP addresses created by the module (empty when using HOBO for ExpressRoute)"
      value       = module.expressroute_gateway.public_ip_addresses
    }
    
    output "virtual_network_id" {
      description = "ID of the virtual network"
      value       = azurerm_virtual_network.vnet.id
    }
    
    output "virtual_network_gateway_connections" {
      description = "Virtual Network Gateway Connections created by the module"
      value       = module.expressroute_gateway.virtual_network_gateway_connections
    }
    
  4. Создайте файл с именем providers.tfи вставьте следующий код:

    terraform {
      required_version = ">= 1.3"
    
      required_providers {
        azapi = {
          source  = "Azure/azapi"
          version = "~> 2.4"
        }
        azurerm = {
          source  = "hashicorp/azurerm"
          version = "~> 4.0"
        }
        random = {
          source  = "hashicorp/random"
          version = "~> 3.5"
        }
      }
    }
    
    provider "azurerm" {
      features {}
    }
    
  5. Создайте файл с именем variables.tfи вставьте следующий код:

    variable "resource_group_location" {
      type        = string
      default     = "eastus"
      description = "Location of the resource group."
    }
    
    variable "resource_group_name_prefix" {
      type        = string
      default     = "rg"
      description = "Prefix of the resource group name that's combined with a random ID so name is unique in your Azure subscription."
    }
    
    variable "tags" {
      type        = map(string)
      default     = {}
      description = "A map of tags to assign to all resources."
    }
    
    # Virtual Network Configuration
    variable "virtual_network_address_space" {
      type        = list(string)
      default     = ["10.0.0.0/16"]
      description = "The address space that is used by the virtual network."
    
      validation {
        condition     = length(var.virtual_network_address_space) > 0
        error_message = "At least one address space must be provided."
      }
    }
    
    variable "gateway_subnet_address_prefix" {
      type        = string
      default     = "10.0.0.0/24"
      description = "The address prefix for the GatewaySubnet. Must be at least /29."
    
      validation {
        condition     = can(cidrhost(var.gateway_subnet_address_prefix, 0)) && tonumber(split("/", var.gateway_subnet_address_prefix)[1]) <= 29
        error_message = "gateway_subnet_address_prefix must be a valid CIDR block with at least /29 prefix."
      }
    }
    
    # ExpressRoute Gateway Configuration
    variable "gateway_sku" {
      type        = string
      default     = "ErGw1AZ"
      description = "The SKU of the ExpressRoute Virtual Network Gateway. Valid values: ErGw1AZ, ErGw2AZ, ErGw3AZ, ErGwScale, HighPerformance, Standard, UltraPerformance."
    
      validation {
        condition     = contains(["ErGw1AZ", "ErGw2AZ", "ErGw3AZ", "ErGwScale", "HighPerformance", "Standard", "UltraPerformance"], var.gateway_sku)
        error_message = "gateway_sku must be one of: ErGw1AZ, ErGw2AZ, ErGw3AZ, ErGwScale, HighPerformance, Standard, UltraPerformance."
      }
    }
    
    variable "enable_hosted_on_behalf_of_public_ip" {
      type        = bool
      default     = true
      description = "Enable Azure-managed public IP for the ExpressRoute gateway (HOBO feature). When enabled, Azure manages the public IP internally and it won't appear in your subscription."
    }
    
    # ExpressRoute Circuit Configuration
    variable "create_express_route_circuit" {
      type        = bool
      default     = true
      description = "Whether to create an ExpressRoute circuit. Set to false if you want to connect to an existing circuit."
    }
    
    variable "express_route_circuit_id" {
      type        = string
      default     = null
      description = "ID of an existing ExpressRoute circuit to connect to. Only used if create_express_route_circuit is false."
    }
    
    variable "express_route_authorization_key" {
      type        = string
      default     = null
      description = "Authorization key for connecting to an existing ExpressRoute circuit."
      sensitive   = true
    }
    
    variable "service_provider_name" {
      type        = string
      default     = "Equinix"
      description = "The name of the ExpressRoute circuit service provider."
    }
    
    variable "peering_location" {
      type        = string
      default     = "Washington DC"
      description = "The name of the peering location and not the Azure resource location."
    }
    
    variable "bandwidth_in_mbps" {
      type        = number
      default     = 50
      description = "The bandwidth in Mbps of the ExpressRoute circuit."
    
      validation {
        condition     = contains([50, 100, 200, 500, 1000, 2000, 5000, 10000], var.bandwidth_in_mbps)
        error_message = "bandwidth_in_mbps must be one of: 50, 100, 200, 500, 1000, 2000, 5000, 10000."
      }
    }
    
    variable "circuit_sku_tier" {
      type        = string
      default     = "Standard"
      description = "The service tier of the ExpressRoute circuit SKU."
    
      validation {
        condition     = contains(["Basic", "Local", "Standard", "Premium"], var.circuit_sku_tier)
        error_message = "circuit_sku_tier must be one of: Basic, Local, Standard, Premium."
      }
    }
    
    variable "circuit_sku_family" {
      type        = string
      default     = "MeteredData"
      description = "The billing mode for the ExpressRoute circuit SKU."
    
      validation {
        condition     = contains(["MeteredData", "UnlimitedData"], var.circuit_sku_family)
        error_message = "circuit_sku_family must be either MeteredData or UnlimitedData."
      }
    }
    
    # ExpressRoute Private Peering Configuration
    variable "create_private_peering" {
      type        = bool
      default     = true
      description = "Whether to create Azure Private Peering for the ExpressRoute circuit."
    }
    
    variable "primary_peer_address_prefix" {
      type        = string
      default     = "192.168.10.16/30"
      description = "A /30 subnet for the primary link."
    
      validation {
        condition     = can(cidrhost(var.primary_peer_address_prefix, 0))
        error_message = "primary_peer_address_prefix must be a valid CIDR block."
      }
    }
    
    variable "secondary_peer_address_prefix" {
      type        = string
      default     = "192.168.10.20/30"
      description = "A /30 subnet for the secondary link."
    
      validation {
        condition     = can(cidrhost(var.secondary_peer_address_prefix, 0))
        error_message = "secondary_peer_address_prefix must be a valid CIDR block."
      }
    }
    
    variable "vlan_id" {
      type        = number
      default     = 200
      description = "A valid VLAN ID to establish this peering on."
    
      validation {
        condition     = var.vlan_id >= 1 && var.vlan_id <= 4094
        error_message = "vlan_id must be between 1 and 4094."
      }
    }
    
    variable "peer_asn" {
      type        = number
      default     = 65001
      description = "A valid private ASN for the customer side BGP session."
    
      validation {
        condition     = (var.peer_asn >= 64512 && var.peer_asn <= 65534) || (var.peer_asn >= 4200000000 && var.peer_asn <= 4294967294)
        error_message = "peer_asn must be a valid private ASN (64512-65534 or 4200000000-4294967294)."
      }
    }
    

Инициализация Terraform

Запустите terraform init, чтобы инициализировать развертывание Terraform. Эта команда скачивает поставщик Azure, необходимый для управления ресурсами Azure.

terraform init -upgrade

Основные моменты:

  • Параметр -upgrade обновляет необходимые подключаемые модули поставщика до последней версии, которая соответствует ограничениям версии конфигурации.

Создание плана выполнения Terraform

Чтобы создать план выполнения, выполните terraform plan.

terraform plan -out main.tfplan

Основные моменты:

  • Команда terraform plan создает план выполнения, но не выполняет его. Вместо этого она определяет, какие действия необходимы для создания конфигурации, заданной в файлах конфигурации. Этот шаблон позволяет проверить, соответствует ли план выполнения вашим ожиданиям, прежде чем вы начнете вносить изменения в фактические ресурсы.
  • Необязательный параметр -out позволяет указать выходной файл для плана. Использование параметра -out гарантирует, что проверяемый план полностью соответствует применяемому.

Применение плана выполнения Terraform

Выполните terraform apply, чтобы применить план выполнения к вашей облачной инфраструктуре.

terraform apply main.tfplan

Основные моменты:

  • В примере terraform apply команды предполагается, что вы ранее выполнили.terraform plan -out main.tfplan
  • Если для параметра -out указано другое имя файла, используйте то же имя в вызове к terraform apply.
  • Если вы не использовали параметр -out, вызовите terraform apply без параметров.

Проверка результатов

  1. Получите имя группы ресурсов Azure.

    resource_group_name=$(terraform output -raw resource_group_name)
    
  2. Получите имя канала связи ExpressRoute.

    circuit_name=$(terraform output -raw expressroute_circuit_name)
    
  3. Получите имя шлюза.

    gateway_name=$(terraform output -raw gateway_name)
    
  4. Выполните команду az network express-route show , чтобы просмотреть канал ExpressRoute.

    az network express-route show --name $circuit_name --resource-group $resource_group_name
    
  5. Выполните команду az network vnet-gateway show , чтобы просмотреть шлюз виртуальной сети Azure.

    az network vnet-gateway show --name $gateway_name --resource-group $resource_group_name
    

Очистка ресурсов

Если вам больше не нужны ресурсы, созданные через Terraform, выполните следующие действия:

  1. Выполните команду terraform plan и укажите флаг destroy.

    terraform plan -destroy -out main.destroy.tfplan
    

    Основные моменты:

    • Команда terraform plan создает план выполнения, но не выполняет его. Вместо этого она определяет, какие действия необходимы для создания конфигурации, заданной в файлах конфигурации. Этот шаблон позволяет проверить, соответствует ли план выполнения вашим ожиданиям, прежде чем вы начнете вносить изменения в фактические ресурсы.
    • Необязательный параметр -out позволяет указать выходной файл для плана. Использование параметра -out гарантирует, что проверяемый план полностью соответствует применяемому.
  2. Выполните команду terraform apply, чтобы применить план выполнения.

    terraform apply main.destroy.tfplan
    

Устранение неполадок с Terraform в Azure

Устранение распространенных проблем при использовании Terraform в Azure.

Следующие шаги

Дополнительные статьи о шлюзе виртуальной сети Azure см. в дополнительных статьях.

Чтобы узнать, как связать виртуальную сеть с каналом, см. руководства по ExpressRoute.