Building a tiny Azure hub with VPN, firewall, and peered VNets

Greetings!

I’ve been tightening up my Azure homelab so that West US and West US 3 can both ride a single VPN gateway and still force traffic through an Azure Firewall. This is all in preps and hopes to get my AZ-700 exam completed next month.

This post walks through the exact Terraform I’m using (and a couple of defaults that keep me honest).

Lets dive in.

What we’re building

  • Two VNets: West US (10.51.0.0/16) with the VPN gateway, and West US 3 (10.50.0.0/16) with the firewall.
  • VNet peering with gateway transit enabled so West US 3 can use the West US VPN gateway to reach on‑prem.
  • Azure Firewall Basic sitting in West US 3 with a simple “allow everything” outbound rule set.
  • One Ubuntu VM in each VNet for quick tests.

Remote state (because losing state is pain)
I back state with an Azure Storage account. Drop these values in backend.hcl and keep the access key out of git:

resource_group_name  = "rg-terraform-state"
storage_account_name = "tfhomelabstate01"
container_name       = "tfstate"
key                  = "homelab/terraform.tfstate"
subscription_id      = "00000000-1111-2222-3333-444444444444"
access_key           = "azsa_access_key_goes_here"

Terraform init (from azure/terraform/):

terraform init -backend-config=backend.hcl

Helper script to create state storage
create-state-storage.ps1 stands up the storage account and container. Defaults are placeholders—override as needed.

.\create-state-storage.ps1 `
  -SubscriptionId "00000000-1111-2222-3333-444444444444" `
  -ResourceGroupName "rg-terraform-state" `
  -Location "westus3" `
  -StorageAccountName "tfstatesa12345" `
  -ContainerName "tfstate" `
  -EnableSoftDelete

The script ensures you’re logged in, creates the RG/storage/container, and prints an access key you can drop into backend.hcl.

Variable file with consistent examples
Here’s the trimmed terraform.tfvars I’m using for the lab. Secrets are placeholders—swap your own.

subscription_id             = "00000000-1111-2222-3333-444444444444"
network_resource_group_name = "rg-tf-networking-west"
resource_group_location     = "westus3"

uswest3_vnet_name                    = "tf-uswest3-vnet1"
uswest3_vnet_address_space           = "10.50.0.0/16"
uswest3_location                     = "westus3"
uswest3_default_subnet_prefix        = "10.50.0.0/26"
uswest3_vm_subnet_prefix             = "10.50.0.64/29"
uswest3_firewall_subnet_prefix       = "10.50.0.128/26"
uswest3_firewall_mgmt_subnet_prefix  = "10.50.0.192/26"
uswest3_firewall_name                = "tf-uswest3-azfw1"
uswest3_firewall_policy_name         = "tf-uswest3-azfw1-policy"
uswest3_firewall_public_ip_name      = "tf-uswest3-azfw1-pip"
uswest3_firewall_mgmt_public_ip_name = "tf-uswest3-azfw1-mgmt-pip"
uswest3_route_table_name             = "tf-uswest3-rt-azfw"

uswest1_vnet_name             = "tf-uswest1-vnet1"
uswest1_vnet_address_space    = "10.51.0.0/16"
uswest1_location              = "westus"
uswest1_gateway_subnet_prefix = "10.51.0.224/27"
uswest1_vm_subnet_prefix      = "10.51.0.0/29"

uswest1_vm_name           = "tf-uswest1-vm1"
uswest1_vm_nic_name       = "tf-uswest1-vm1-nic1"
uswest1_vm_size           = "Standard_D2as_v5"
uswest1_vm_admin_username = "zbc-admin"
uswest1_vm_admin_password = "SuperSecretPassw0rd!"

vpn_gateway_name           = "tf-uswest1-vpngw1"
vpn_gateway_public_ip_name = "tf-uswest1-vpngw1-pip"
vpn_gateway_sku            = "VpnGw1"

local_network_gateway_name           = "homelab-onprem"
local_network_gateway_public_ip      = "203.0.113.24"
local_network_gateway_address_spaces = ["10.0.0.0/24"]
vpn_connection_name                  = "tf-uswest1-s2s-conn1"
vpn_connection_shared_key            = "AnotherS3cretKey!"

uswest3_vm_name           = "tf-uswest3-vm1"
uswest3_vm_nic_name       = "tf-uswest3-vm1-nic1"
uswest3_vm_size           = "Standard_D2as_v5"
uswest3_vm_admin_username = "zbc-admin"
uswest3_vm_admin_password = "SuperSecretPassw0rd!"

tags = {
  Owner       = "Example Owner"
  Environment = "Dv"
  Product     = "AZ700"
  ManagedBy   = "Terraform"
}

Core Terraform highlights

VNet peering with gateway transit

# West US advertises its VPN gateway
resource "azurerm_virtual_network_peering" "uswest1_to_uswest3" {
  name                         = "uswest1-to-uswest3"
  resource_group_name          = azurerm_resource_group.network.name
  virtual_network_name         = azurerm_virtual_network.uswest1.name
  remote_virtual_network_id    = azurerm_virtual_network.uswest3.id
  allow_gateway_transit        = true
  allow_virtual_network_access = true
  allow_forwarded_traffic      = true
}

# West US 3 consumes that gateway
resource "azurerm_virtual_network_peering" "uswest3_to_uswest1" {
  name                         = "uswest3-to-uswest1"
  resource_group_name          = azurerm_resource_group.network.name
  virtual_network_name         = azurerm_virtual_network.uswest3.name
  remote_virtual_network_id    = azurerm_virtual_network.uswest1.id
  use_remote_gateways          = true
  allow_virtual_network_access = true
  allow_forwarded_traffic      = true
}

VPN gateway without BGP (static S2S)

resource "azurerm_virtual_network_gateway" "vpn" {
  name                = var.vpn_gateway_name
  location            = var.uswest1_location
  resource_group_name = azurerm_resource_group.network.name
  type                = "Vpn"
  vpn_type            = "RouteBased"
  sku                 = var.vpn_gateway_sku
  active_active       = false
  # BGP is off; we rely on static prefixes from the local network gateway

  ip_configuration {
    name                          = "vnetGatewayConfig"
    public_ip_address_id          = azurerm_public_ip.vpn.id
    private_ip_address_allocation = "Dynamic"
    subnet_id                     = azurerm_subnet.uswest1_gateway.id
  }
}

Routing on the firewall side

# Default 0/0 through Azure Firewall
resource "azurerm_route" "uswest3_default_to_firewall" {
  name                   = "default-to-firewall"
  route_table_name       = azurerm_route_table.uswest3_default.name
  address_prefix         = "0.0.0.0/0"
  next_hop_type          = "VirtualAppliance"
  next_hop_in_ip_address = azurerm_firewall.uswest3_firewall.ip_configuration[0].private_ip_address
}

# On-prem prefix via the remote VPN gateway (enabled by peering)
resource "azurerm_route" "uswest3_onprem_via_vpngw" {
  name             = "onprem-via-vpngw"
  route_table_name = azurerm_route_table.uswest3_default.name
  address_prefix   = var.local_network_gateway_address_spaces[0]
  next_hop_type    = "VirtualNetworkGateway"
}

Firewall policy: wide open for now

resource "azurerm_firewall_policy_rule_collection_group" "uswest3_allow_outbound" {
  name               = "allow-outbound"
  priority           = 100
  firewall_policy_id = azurerm_firewall_policy.uswest3.id

  network_rule_collection {
    name     = "allow-all-vnet-out"
    priority = 100
    action   = "Allow"

    rule {
      name                  = "All-out-uswest3"
      source_addresses      = [azurerm_virtual_network.uswest3.address_space[0]]
      destination_addresses = ["*"]
      destination_ports     = ["*"]
      protocols             = ["Any"]
    }

    rule {
      name                  = "All-out-uswest1"
      source_addresses      = [azurerm_virtual_network.uswest1.address_space[0]]
      destination_addresses = ["*"]
      destination_ports     = ["*"]
      protocols             = ["Any"]
    }
  }
}

Deploying

cd azure/terraform
terraform plan -var-file=terraform.tfvars
terraform apply -var-file=terraform.tfvars

What to tighten next

  • Replace the “allow all” firewall rules with scoped collections and logging to a workspace.
  • If you want dynamic routing, turn BGP back on and add bgp_settings to both the VPN gateway and local network gateway.
  • Loop over local_network_gateway_address_spaces to install all on-prem prefixes instead of just the first entry.

That’s it for now. The peering + remote gateway setup keeps the topology simple while I test. Next iteration will lock down egress and add better route handling. Thanks for reading!

Grab the code


If you want to clone the exact public version of this lab, it’s on my GitHub:

https://github.com/zveroboy152/zbc-azure-network-lab-example


Comments

4 responses to “Azure Lab – VNet Hub & Firewall Topology”

  1. It’s fascinating how easily accessible gaming has become – registration processes are now so streamlined! Seeing platforms like XoPlay prioritize verification is good; security is key. Check out the xoplay app download apk for a secure experience! It’s interesting how these platforms cater to diverse preferences.

  2. Interesting points on bankroll management! Seeing platforms like egovph legit prioritize secure account verification is a big plus for player trust & responsible gaming. Solid foundation for any strategy!

  3. It’s fascinating how RTP impacts player behavior – seeking statistically favorable games is common! Seeing platforms like play super ace casino emphasize that (94.5%-98.2%!) is a smart move for building trust & engagement. 🤔

  4. Interesting read! Considering variance in roulette, a solid platform with localized payment options like those at philslots app online casino can really enhance the experience. Easy deposits are key! 🤔

Leave a Reply

Your email address will not be published. Required fields are marked *