Skip to content

Day 1: Virtual WAN Hub Provisioning

File: terraform/03-vwan/main.tf

terraform {
  required_version = ">= 1.5.0"
  required_providers {
    azurerm = { source = "hashicorp/azurerm", version = "~> 3.85.0" }
  }
}

provider "azurerm" { features {} }

data "azurerm_resource_group" "networking" { name = "abhavtech-networking" }
data "azurerm_virtual_network" "prod" {
  name                = "abhavtech-azure-prod"
  resource_group_name = data.azurerm_resource_group.networking.name
}

# Virtual WAN Resource

resource "azurerm_virtual_wan" "main" {
  name                            = "abhavtech-vwan"
  resource_group_name             = data.azurerm_resource_group.networking.name
  location                        = data.azurerm_resource_group.networking.location
  disable_vpn_encryption          = false
  allow_branch_to_branch_traffic  = true
  office365_local_breakout_category = "OptimizeAndAllow"
  type                            = "Standard"
}

## Virtual WAN Hub: Central India

resource "azurerm_virtual_hub" "india" {
  name                = "abhavtech-vwan-hub-india"
  resource_group_name = data.azurerm_resource_group.networking.name
  location            = "centralindia"
  virtual_wan_id      = azurerm_virtual_wan.main.id
  address_prefix      = "10.200.0.0/23"
}

## VPN Gateway in Hub (for branch IPsec)

resource "azurerm_vpn_gateway" "india" {
  name                = "abhavtech-vpn-gw-india"
  resource_group_name = data.azurerm_resource_group.networking.name
  location            = "centralindia"
  virtual_hub_id      = azurerm_virtual_hub.india.id
  scale_unit          = 3  # VpnGw3: 1.25 Gbps per tunnel, 10 tunnels max

  bgp_settings {
    asn         = 65515  # Azure auto-assigned ASN
    peer_weight = 0
    instance_0_bgp_peering_address {
      custom_ips = ["10.200.0.254"]
    }
  }
}

## Connect Prod VNet to Virtual WAN Hub

resource "azurerm_virtual_hub_connection" "prod_vnet" {
  name                      = "prod-vnet-connection"
  virtual_hub_id            = azurerm_virtual_hub.india.id
  remote_virtual_network_id = data.azurerm_virtual_network.prod.id
}

## VPN Sites (one per branch)

resource "azurerm_vpn_site" "bangalore" {
  name                = "branch-bangalore"
  resource_group_name = data.azurerm_resource_group.networking.name
  location            = "centralindia"
  virtual_wan_id      = azurerm_virtual_wan.main.id
  address_cidrs       = ["192.168.50.0/24"]

  link {
    name          = "blr-isr4331-primary"
    ip_address    = var.bangalore_wan_ip
    speed_in_mbps = 100
    bgp { asn = 65010; peering_address = "10.200.1.10" }
  }
}

resource "azurerm_vpn_site" "delhi" {
  name                = "branch-delhi"
  resource_group_name = data.azurerm_resource_group.networking.name
  location            = "centralindia"
  virtual_wan_id      = azurerm_virtual_wan.main.id
  address_cidrs       = ["192.168.51.0/24"]

  link {
    name          = "del-isr4331-primary"
    ip_address    = var.delhi_wan_ip
    speed_in_mbps = 100
    bgp { asn = 65011; peering_address = "10.200.1.14" }
  }
}

resource "azurerm_vpn_site" "hyderabad" {
  name                = "branch-hyderabad"
  resource_group_name = data.azurerm_resource_group.networking.name
  location            = "centralindia"
  virtual_wan_id      = azurerm_virtual_wan.main.id
  address_cidrs       = ["192.168.52.0/24"]

  link {
    name          = "hyd-isr1100-primary"
    ip_address    = var.hyderabad_wan_ip
    speed_in_mbps = 50
    bgp { asn = 65012; peering_address = "10.200.1.18" }
  }
}

## VPN Connections (attach each site to gateway)

resource "azurerm_vpn_gateway_connection" "bangalore" {
  name                = "conn-bangalore"
  resource_group_name = data.azurerm_resource_group.networking.name
  vpn_gateway_id      = azurerm_vpn_gateway.india.id
  remote_vpn_site_id  = azurerm_vpn_site.bangalore.id

  vpn_link {
    name             = "bangalore-link"
    vpn_site_link_id = azurerm_vpn_site.bangalore.link[0].id
    bgp_enabled      = true
    shared_key       = var.bangalore_psk
    protocol         = "IKEv2"

    ipsec_policy {
      dh_group                 = "DHGroup14"
      ike_encryption_algorithm = "AES256"
      ike_integrity_algorithm  = "SHA384"
      encryption_algorithm     = "AES256"
      integrity_algorithm      = "SHA256"
      pfs_group                = "PFS14"
      sa_lifetime_seconds      = 27000
      sa_data_size_kilobytes   = 102400000
    }
  }
}

output "vpn_gateway_public_ips" {
  description = "Configure these as tunnel destinations on branch ISR routers"
  value       = azurerm_vpn_gateway.india.bgp_settings[0].instance_0_bgp_peering_address[0].tunnel_ips
}

Deploy Virtual WAN

cd ~/azure-deployment/terraform/03-vwan
terraform init
terraform plan -out=tfplan
terraform apply tfplan

## Get VPN Gateway public IPs (needed for ISR tunnel config)

VWAN_GW_IP1=$(terraform output -json vpn_gateway_public_ips | jq -r '.[0]')
VWAN_GW_IP2=$(terraform output -json vpn_gateway_public_ips | jq -r '.[1]')
echo "VWAN GW IP1: $VWAN_GW_IP1"
echo "VWAN GW IP2: $VWAN_GW_IP2"

Day 2-3: Branch IPsec Configuration

Bangalore ISR 4331 (BGP ASN 65010)

! ============================================================================
! BANGALORE BRANCH - ISR 4331 TO AZURE VIRTUAL WAN
! ============================================================================

hostname BLR-ISR-01

! IKEv2 Proposal (must match Azure Virtual WAN policy)
crypto ikev2 proposal AZURE-VWAN-PROPOSAL
 encryption aes-cbc-256
 integrity sha384
 group 14

crypto ikev2 policy AZURE-VWAN-POLICY
 proposal AZURE-VWAN-PROPOSAL

crypto ikev2 keyring AZURE-VWAN-KEYRING
 peer azure-gw1
  address <AZURE_VWAN_GW_IP_1>
  pre-shared-key <PSK_FROM_AZURE_PORTAL>
 peer azure-gw2
  address <AZURE_VWAN_GW_IP_2>
  pre-shared-key <PSK_FROM_AZURE_PORTAL>

crypto ikev2 profile AZURE-VWAN-PRIMARY
 match identity remote address <AZURE_VWAN_GW_IP_1>
 identity local address <BLR_WAN_PUBLIC_IP>
 authentication remote pre-share
 authentication local pre-share
 keyring local AZURE-VWAN-KEYRING
 lifetime 27000

crypto ikev2 profile AZURE-VWAN-SECONDARY
 match identity remote address <AZURE_VWAN_GW_IP_2>
 identity local address <BLR_WAN_PUBLIC_IP>
 authentication remote pre-share
 authentication local pre-share
 keyring local AZURE-VWAN-KEYRING
 lifetime 27000

crypto ipsec transform-set AZURE-VWAN-TS esp-aes 256 esp-sha-hmac
 mode tunnel

crypto ipsec profile AZURE-VWAN-IPSEC-PRIMARY
 set transform-set AZURE-VWAN-TS
 set ikev2-profile AZURE-VWAN-PRIMARY
 set security-association lifetime seconds 27000
 set pfs group14

crypto ipsec profile AZURE-VWAN-IPSEC-SECONDARY
 set transform-set AZURE-VWAN-TS
 set ikev2-profile AZURE-VWAN-SECONDARY
 set security-association lifetime seconds 27000
 set pfs group14

! Tunnel interfaces
interface Tunnel100
 description IPSEC-TO-AZURE-VWAN-PRIMARY
 ip address 10.200.1.10 255.255.255.252
 ip mtu 1400
 ip tcp adjust-mss 1360
 tunnel source GigabitEthernet0/0/0
 tunnel mode ipsec ipv4
 tunnel destination <AZURE_VWAN_GW_IP_1>
 tunnel protection ipsec profile AZURE-VWAN-IPSEC-PRIMARY
 no shutdown

interface Tunnel101
 description IPSEC-TO-AZURE-VWAN-SECONDARY
 ip address 10.200.1.22 255.255.255.252
 ip mtu 1400
 ip tcp adjust-mss 1360
 tunnel source GigabitEthernet0/0/0
 tunnel mode ipsec ipv4
 tunnel destination <AZURE_VWAN_GW_IP_2>
 tunnel protection ipsec profile AZURE-VWAN-IPSEC-SECONDARY
 no shutdown

! BGP
router bgp 65010
 bgp router-id 192.168.50.1
 neighbor 10.200.0.254 remote-as 65515
 neighbor 10.200.0.254 description AZURE-VWAN-HUB-PRIMARY
 neighbor 10.200.0.254 ebgp-multihop 3
 neighbor 10.200.0.254 update-source Tunnel100
 neighbor 10.200.0.253 remote-as 65515
 neighbor 10.200.0.253 description AZURE-VWAN-HUB-SECONDARY
 neighbor 10.200.0.253 ebgp-multihop 3
 neighbor 10.200.0.253 update-source Tunnel101
 address-family ipv4
  network 192.168.50.0 mask 255.255.255.0
  neighbor 10.200.0.254 activate
  neighbor 10.200.0.254 route-map AZURE-VNETS-IN in
  neighbor 10.200.0.254 route-map BRANCH-LAN-OUT out
  neighbor 10.200.0.254 route-map SET-LOCALPREF-200 in
  neighbor 10.200.0.253 activate
  neighbor 10.200.0.253 route-map AZURE-VNETS-IN in
  neighbor 10.200.0.253 route-map BRANCH-LAN-OUT out
  neighbor 10.200.0.253 route-map SET-LOCALPREF-100 in
 exit-address-family

! Route-maps
ip prefix-list AZURE-VNETS seq 10 permit 10.100.0.0/16 le 32
ip prefix-list AZURE-VNETS seq 20 permit 10.101.0.0/16 le 32
ip prefix-list AZURE-VNETS seq 999 deny 0.0.0.0/0 le 32

ip prefix-list BANGALORE-LAN seq 10 permit 192.168.50.0/24
ip prefix-list BANGALORE-LAN seq 999 deny 0.0.0.0/0 le 32

route-map AZURE-VNETS-IN permit 10
 match ip address prefix-list AZURE-VNETS
route-map AZURE-VNETS-IN deny 999

route-map BRANCH-LAN-OUT permit 10
 match ip address prefix-list BANGALORE-LAN
route-map BRANCH-LAN-OUT deny 999

route-map SET-LOCALPREF-200 permit 10
 set local-preference 200
route-map SET-LOCALPREF-100 permit 10
 set local-preference 100

! Static routes for BGP peer reachability
ip route 10.200.0.254 255.255.255.255 Tunnel100
ip route 10.200.0.253 255.255.255.255 Tunnel101

Apply similar config to Delhi (ASN 65011, subnet 192.168.51.0/24) and all other India branches, adjusting ASN, LAN subnet, and tunnel IPs per branch.