Condividi tramite


Guida introduttiva: Creare un circuito ExpressRoute e un gateway di rete virtuale con Terraform

In questa guida introduttiva si usa Terraform per creare un circuito Azure ExpressRoute e l'infrastruttura associata. Il modello Terraform crea un'installazione di ExpressRoute completa, tra cui una rete virtuale, un gateway ExpressRoute, una configurazione del circuito e un peering privato. Tutte le risorse vengono distribuite con parametri configurabili che consentono di personalizzare la distribuzione per i requisiti specifici.

Diagramma di un ambiente di distribuzione del circuito Azure ExpressRoute con Terraform.

Terraform abilita la definizione, l'anteprima e la distribuzione dell'infrastruttura cloud. Con Terraform si creano file di configurazione usando la sintassi HCL. La sintassi HCL consente di specificare il provider di servizi cloud, ad esempio Azure, e gli elementi che costituiscono l'infrastruttura cloud. Dopo aver creato i file di configurazione, si crea un piano di esecuzione che consente di visualizzare in anteprima le modifiche dell'infrastruttura prima della distribuzione. Dopo aver verificato le modifiche, è possibile applicare il piano di esecuzione per distribuire l'infrastruttura.

In questo articolo vengono illustrate le operazioni seguenti:

  • Creare un gruppo di risorse di Azure con un nome univoco
  • Creare una rete virtuale con una subnet per il gateway
  • Creare un gateway ExpressRoute con SKU configurabile
  • Creare un circuito ExpressRoute con impostazioni configurabili del provider di servizi
  • Configurare il peering privato per il circuito ExpressRoute
  • Identificatori della risorsa chiave di output e dettagli di configurazione

Prerequisiti

Implementare il codice Terraform

  1. Creare una directory in cui testare ed eseguire il codice Terraform di esempio e impostarla come directory corrente.

  2. Creare un file denominato main.tfe immettere il codice seguente:

    # 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. Creare un file denominato outputs.tfe immettere il codice seguente:

    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. Creare un file denominato providers.tfe immettere il codice seguente:

    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. Creare un file denominato variables.tfe immettere il codice seguente:

    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)."
      }
    }
    

Inizializzare Terraform

Eseguire terraform init per inizializzare la distribuzione di Terraform. Questo comando scarica il provider di Azure necessario per gestire le risorse di Azure.

terraform init -upgrade

Punti chiave:

  • Il parametro -upgrade aggiorna i plug-in del provider necessari alla versione più recente conforme ai vincoli di versione della configurazione.

Creare un piano di esecuzione Terraform

Eseguire terraform plan per creare un piano di esecuzione.

terraform plan -out main.tfplan

Punti chiave:

  • Il comando terraform plan consente di creare un piano di esecuzione, ma non di eseguirlo. Determina invece le azioni necessarie per creare la configurazione specificata nei file di configurazione. Questo modello consente di verificare se il piano di esecuzione corrisponde alle aspettative prima di apportare modifiche alle risorse effettive.
  • Il parametro -out facoltativo consente di specificare un file di output per il piano. L'uso del parametro -out garantisce che il piano esaminato sia esattamente quello che viene applicato.

Applicare un piano di esecuzione Terraform

Eseguire terraform apply per applicare il piano di esecuzione all'infrastruttura cloud.

terraform apply main.tfplan

Punti chiave:

  • Il comando terraform apply di esempio presuppone che in precedenza sia stato eseguito terraform plan -out main.tfplan.
  • Se è stato specificato un nome file diverso per il parametro -out, usare lo stesso nome file nella chiamata a terraform apply.
  • Se non è stato usato il parametro -out, chiamare terraform apply senza parametri.

Verificare i risultati

  1. Ottenere il nome del gruppo di risorse di Azure.

    resource_group_name=$(terraform output -raw resource_group_name)
    
  2. Ottenere il nome del circuito ExpressRoute.

    circuit_name=$(terraform output -raw expressroute_circuit_name)
    
  3. Ottenere il nome del gateway.

    gateway_name=$(terraform output -raw gateway_name)
    
  4. Eseguire az network express-route show per visualizzare il circuito ExpressRoute.

    az network express-route show --name $circuit_name --resource-group $resource_group_name
    
  5. Eseguire az network vnet-gateway show per visualizzare il gateway di rete virtuale di Azure.

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

Pulire le risorse

Quando le risorse create tramite Terraform non sono più necessarie, eseguire i passaggi seguenti:

  1. Eseguire terraform plan e specificare il flag destroy.

    terraform plan -destroy -out main.destroy.tfplan
    

    Punti chiave:

    • Il comando terraform plan consente di creare un piano di esecuzione, ma non di eseguirlo. Determina invece le azioni necessarie per creare la configurazione specificata nei file di configurazione. Questo modello consente di verificare se il piano di esecuzione corrisponde alle aspettative prima di apportare modifiche alle risorse effettive.
    • Il parametro -out facoltativo consente di specificare un file di output per il piano. L'uso del parametro -out garantisce che il piano esaminato sia esattamente quello che viene applicato.
  2. Eseguire terraform apply per applicare il piano di esecuzione.

    terraform apply main.destroy.tfplan
    

Risolvere i problemi di Terraform in Azure

Risolvere i problemi comuni relativi all'uso di Terraform in Azure.

Passaggi successivi

Vedere altri articoli sul gateway di rete virtuale di Azure.

Per informazioni su come collegare una rete virtuale a un circuito, continuare con le esercitazioni su ExpressRoute.