Managing Azure ExpressRoute with Terraform

Learn how to set up and manage Azure ExpressRoute using Terraform, including circuit and peering configurations

Managing Azure ExpressRoute with Terraform

Azure ExpressRoute provides a private, dedicated, and high-bandwidth connection between your on-premises network and Azure. This guide demonstrates how to set up and manage ExpressRoute using Terraform.

Video Tutorial

Learn more about managing Azure ExpressRoute with Terraform in this comprehensive video tutorial:

Prerequisites

  • Azure CLI configured with appropriate permissions
  • Terraform installed (version 1.0.0 or later)
  • Resource group created
  • Understanding of networking concepts
  • ExpressRoute service provider

Project Structure

terraform-azure-expressroute/
├── main.tf
├── variables.tf
├── outputs.tf
├── modules/
│   └── expressroute/
│       ├── main.tf
│       ├── variables.tf
│       └── outputs.tf
└── configs/
    └── peering.json

ExpressRoute Configuration

Create modules/expressroute/main.tf:

# ExpressRoute Circuit
resource "azurerm_express_route_circuit" "main" {
  name                  = "${var.project_name}-circuit"
  resource_group_name   = var.resource_group_name
  location              = var.location
  service_provider_name = "Equinix"
  peering_location      = "Silicon Valley"
  bandwidth_in_mbps     = 1000

  sku {
    tier   = "Premium"
    family = "MeteredData"
  }

  allow_classic_operations = false

  tags = var.tags
}

# ExpressRoute Circuit Authorization
resource "azurerm_express_route_circuit_authorization" "main" {
  name                       = "${var.project_name}-auth"
  express_route_circuit_name = azurerm_express_route_circuit.main.name
  resource_group_name        = var.resource_group_name
}

# Private Peering
resource "azurerm_express_route_circuit_peering" "private" {
  peering_type                  = "AzurePrivatePeering"
  express_route_circuit_name    = azurerm_express_route_circuit.main.name
  resource_group_name           = var.resource_group_name
  primary_peer_address_prefix   = "192.168.1.0/30"
  secondary_peer_address_prefix = "192.168.2.0/30"
  vlan_id                       = 100
  shared_key                    = var.peering_shared_key
  peer_asn                      = 65001

  microsoft_peering_config {
    advertised_public_prefixes = ["123.0.0.0/24"]
    customer_asn              = 65001
    routing_registry_name     = "ARIN"
  }

  route_filter_id = azurerm_express_route_route_filter.main.id
}

# Microsoft Peering
resource "azurerm_express_route_circuit_peering" "microsoft" {
  peering_type                  = "MicrosoftPeering"
  express_route_circuit_name    = azurerm_express_route_circuit.main.name
  resource_group_name           = var.resource_group_name
  primary_peer_address_prefix   = "192.168.3.0/30"
  secondary_peer_address_prefix = "192.168.4.0/30"
  vlan_id                       = 200
  shared_key                    = var.peering_shared_key
  peer_asn                      = 65001

  microsoft_peering_config {
    advertised_public_prefixes = ["123.0.0.0/24"]
    customer_asn              = 65001
    routing_registry_name     = "ARIN"
  }

  route_filter_id = azurerm_express_route_route_filter.main.id
}

# Route Filter
resource "azurerm_express_route_route_filter" "main" {
  name                = "${var.project_name}-filter"
  resource_group_name = var.resource_group_name
  location            = var.location

  rule {
    name        = "allow-o365"
    access      = "Allow"
    rule_type   = "Community"
    communities = ["12076:5010", "12076:5020"]
  }

  tags = var.tags
}

# ExpressRoute Gateway
resource "azurerm_virtual_network_gateway" "expressroute" {
  name                = "${var.project_name}-ergw"
  location            = var.location
  resource_group_name = var.resource_group_name
  type                = "ExpressRoute"
  sku                 = "ErGw2AZ"
  
  ip_configuration {
    name                          = "default"
    public_ip_address_id          = azurerm_public_ip.expressroute.id
    private_ip_address_allocation = "Dynamic"
    subnet_id                     = azurerm_subnet.gateway.id
  }

  tags = var.tags
}

# ExpressRoute Connection
resource "azurerm_virtual_network_gateway_connection" "expressroute" {
  name                = "${var.project_name}-connection"
  location            = var.location
  resource_group_name = var.resource_group_name

  type                           = "ExpressRoute"
  virtual_network_gateway_id     = azurerm_virtual_network_gateway.expressroute.id
  express_route_circuit_id       = azurerm_express_route_circuit.main.id
  routing_weight                = 10
  enable_bgp                    = true

  tags = var.tags
}

# FastPath
resource "azurerm_express_route_circuit_connection" "fastpath" {
  name                = "${var.project_name}-fastpath"
  peering_id          = azurerm_express_route_circuit_peering.private.id
  peer_peering_id     = azurerm_express_route_circuit_peering.private.id
  address_prefix      = "192.168.100.0/29"
}

# Global Reach
resource "azurerm_express_route_circuit_connection" "global" {
  name                = "${var.project_name}-global"
  peering_id          = azurerm_express_route_circuit_peering.private.id
  peer_peering_id     = var.peer_circuit_peering_id
  address_prefix      = "192.168.101.0/29"
}

Network Configuration

  1. Virtual Network Setup
resource "azurerm_virtual_network" "main" {
  name                = "${var.project_name}-vnet"
  location            = var.location
  resource_group_name = var.resource_group_name
  address_space       = ["10.0.0.0/16"]

  subnet {
    name           = "GatewaySubnet"
    address_prefix = "10.0.255.0/24"
  }

  tags = var.tags
}

resource "azurerm_public_ip" "expressroute" {
  name                = "${var.project_name}-ergw-pip"
  location            = var.location
  resource_group_name = var.resource_group_name
  allocation_method   = "Static"
  sku                 = "Standard"
  zones               = ["1", "2", "3"]

  tags = var.tags
}

Route Configuration

  1. Route Table
resource "azurerm_route_table" "main" {
  name                = "${var.project_name}-rt"
  location            = var.location
  resource_group_name = var.resource_group_name

  route {
    name                   = "ToOnPremise"
    address_prefix         = "192.168.0.0/16"
    next_hop_type         = "VirtualNetworkGateway"
  }

  tags = var.tags
}

resource "azurerm_subnet_route_table_association" "main" {
  subnet_id      = azurerm_subnet.main.id
  route_table_id = azurerm_route_table.main.id
}

Monitoring Configuration

  1. Diagnostic Settings
resource "azurerm_monitor_diagnostic_setting" "expressroute" {
  name                       = "${var.project_name}-diag"
  target_resource_id        = azurerm_express_route_circuit.main.id
  log_analytics_workspace_id = var.log_analytics_workspace_id

  log {
    category = "PeeringRouteLog"
    enabled  = true

    retention_policy {
      enabled = true
      days    = 30
    }
  }

  metric {
    category = "AllMetrics"
    enabled  = true

    retention_policy {
      enabled = true
      days    = 30
    }
  }
}

resource "azurerm_monitor_metric_alert" "expressroute" {
  name                = "${var.project_name}-bandwidth-alert"
  resource_group_name = var.resource_group_name
  scopes              = [azurerm_express_route_circuit.main.id]
  description         = "Alert when bandwidth utilization is high"

  criteria {
    metric_namespace = "Microsoft.Network/expressRouteCircuits"
    metric_name      = "BitsInPerSecond"
    aggregation      = "Average"
    operator         = "GreaterThan"
    threshold        = 800000000
  }

  action {
    action_group_id = var.action_group_id
  }
}

Best Practices

  1. Performance

    • Use appropriate SKU
    • Enable FastPath
    • Configure BGP
    • Optimize routing
  2. High Availability

    • Use zone-redundant gateways
    • Configure redundant circuits
    • Implement failover
    • Monitor connections
  3. Security

    • Use private peering
    • Implement encryption
    • Configure authentication
    • Monitor traffic
  4. Cost Optimization

    • Choose appropriate bandwidth
    • Monitor usage
    • Optimize connections
    • Use appropriate tier

Conclusion

You’ve learned how to set up and manage Azure ExpressRoute using Terraform. This setup provides:

  • Private connectivity
  • High bandwidth
  • Global reach
  • Reliable performance

Remember to:

  • Monitor circuit status
  • Review peering settings
  • Update route filters
  • Maintain connections