Secure hybrid network

This sample deploys a hub and spoke network, a mock on-premises network, and connects both with a site-to-site VPN connection.

Where applicable, each resource is configured to send diagnostics to an Azure Log Analytics instance.

Hub and spoke architectural diagram

For detailed information, see the Implement a secure hybrid network:

Deploy sample

Clone this repo

git clone https://github.com/mspnp/samples.git
cd samples/solutions/secure-hybrid-network

Run the following commands to initiate the deployment. When prompted, enter values for an admin username, password, and VPN shared key. These values are used to log into the included virtual machines and establish the site-to-site VPN connection.

# Deploy the base infrastructure: hub, spoke, firewall, VPN, and VMSS workload
az deployment sub create -n secure-hybrid-network --location eastus2 --template-file azuredeploy.bicep -p mockOnPremResourceGroup=rg-site-to-site-mock-prem-eastus2 azureNetworkResourceGroup=rg-site-to-site-azure-network-eastus2

Now that the on-premises site has joined the network, update the hub firewall with a DNAT rule so it can reach the spoke workloads:

# Get the firewall and load balancer private IPs
FW_IP=$(az network firewall show -g rg-site-to-site-azure-network-eastus2 -n AzureFirewall --query "ipConfigurations[0].privateIPAddress" -o tsv)
LB_IP=$(az network lb frontend-ip list -g rg-site-to-site-azure-network-eastus2 --lb-name lb-internal --query "[0].privateIPAddress" -o tsv)

# Add DNAT rules for on-premises to spoke traffic
az deployment group create -n firewallDnat -g rg-site-to-site-azure-network-eastus2 --template-file nestedtemplates/azure-network-azuredeploy-v2.bicep -p firewallName=AzureFirewall firewallPrivateIp=$FW_IP internalLoadBalancerPrivateIp=$LB_IP

Solution deployment parameters

azuredeploy.bicep

Parameter Type Description Default and properties
mockOnPremResourceGroup string The name of the mock on-prem resource group. null
azureNetworkResourceGroup string The name of the Azure network resource group. null
adminUserName string The admin user name for the virtual machines. null
adminPassword securestring The admin password for the virtual machines. null
sharedKey securestring The shared key used for VPN site-to-site connections. null

nestedtemplates/azure-network-azuredeploy.bicep

Parameter Type Description Default and properties
adminUserName string The admin user name for the virtual machines. azureadmin
adminPassword securestring The admin password for the virtual machines. null
vmSize string Size of the load-balanced virtual machines. Standard_D2s_v3
configureSitetosite bool Condition for configuring a site-to-site VPN connection. true
hubNetwork object Object representing the configuration of the hub network. name, addressPrefix
spokeNetwork object Object representing the configuration of the spoke network. name, addressPrefix, subnetName, subnetPrefix, subnetNsgName
vpnGateway object Object representing the configuration of the VPN gateway. name, subnetName, subnetPrefix, publicIPAddressName
bastionHost object Object representing the configuration of the Bastion host. name, subnetName, subnetPrefix, publicIPAddressName, nsgName
azureFirewall object Object representing the configuration of the Azure Firewall. name, subnetName, subnetPrefix, publicIPAddressName
spokeRoutes object Object representing user-defined routes for the spoke subnet. tableName, routeNameFirewall
gatewayRoutes object Object representing user-defined routes for the gateway network. tableName, routeNameFirewall
internalLoadBalancer object Object representing the configuration of the application load balancer. name, backendName, frontendName, probeName
location string Location to be used for all resources. rg location

nestedtemplates/azure-network-local-gateway.bicep

Parameter Type Description Default and properties
connectionName string Name of the Azure connection resource. hub-to-mock-prem
gatewayIpAddress string Public IP address of the mock on-prem virtual network gateway. null
azureCloudVnetPrefix string Address prefix of the hub network. null
azureNetworkGatewayName string Name of the Azure virtual network gateway. null
localNetworkGatewayName string Name of the Azure local network gateway. local-gateway-azure-network
sharedKey securestring The shared key for the VPN connection. null

nestedtemplates/mock-onprem-azuredeploy.bicep

Parameter Type Description Default
adminUserName string The admin user name for the virtual machine. null
adminPassword securestring The admin password for the virtual machine. null
mocOnpremNetwork object Object representing the configuration of the mock on-prem network. name, addressPrefix, mgmt, subnetPrefix
mocOnpremGateway object Object representing the configuration of the VPN gateway. name, subnetName, subnetPrefix, publicIPAddressName
bastionHost object Object representing the configuration of the Bastion host. name, subnetName, subnetPrefix, publicIPAddressName, nsgName
vmSize string Size of the virtual machine. Standard_D2s_v3
configureSitetosite bool Condition for configuring a site-to-site VPN connection. true
location string Location to be used for all resources. rg location

nestedtemplates/mock-onprem-local-gateway.bicep

Parameter Type Description Default
connectionName string Name of the mock on-prem connection resource. mock-prem-to-hub
azureCloudVnetPrefix string Address prefix of the hub network. null
spokeNetworkAddressPrefix string Address prefix of the spoke network. null
gatewayIpAddress string Public IP address of the Azure virtual network gateway. null
mocOnpremGatewayName string Name of the mock on-prem virtual network gateway. null
localNetworkGateway string Name of the mock on-prem local network gateway. local-gateway-moc-prem
sharedKey securestring The shared key for the VPN connection. null
location string Location to be used for all resources. rg location

nestedtemplates/azure-network-azuredeploy-v2.bicep

Parameter Type Description Default
firewallName string Name of the Azure Firewall. null
firewallPrivateIp string Private IP address of the firewall. null
internalLoadBalancerPrivateIp string Private IP address of the internal load balancer. null
location string Location for the resource. rg location

Validate deployment

After the deployment completes, verify end-to-end connectivity by accessing the IIS web server from the mock on-premises VM through the VPN tunnel. Traffic flows through the Azure Firewall via a DNAT rule that translates requests to the internal load balancer.

Option 1: Azure Bastion

Connect to the mock on-premises virtual machine using the included Azure Bastion host, open a web browser, and navigate to the Azure Firewall's private IP address (http://<firewall-private-ip>). The firewall translates the request to the application's internal load balancer.

Option 2: CLI

# Get the Azure Firewall private IP (DNAT entry point)
FW_IP=$(az network firewall show \
  -g rg-site-to-site-azure-network-eastus2 \
  -n AzureFirewall \
  --query "ipConfigurations[0].privateIPAddress" -o tsv)

# From the mock on-prem VM, reach IIS through the VPN tunnel via firewall DNAT
az vm run-command invoke \
  -g rg-site-to-site-mock-prem-eastus2 \
  -n vm-windows \
  --command-id RunPowerShellScript \
  --scripts "Invoke-WebRequest -Uri http://$FW_IP -UseBasicParsing | Select-Object -Property StatusCode"

A successful response returns StatusCode: 200, confirming the full path: on-prem VM → VPN → hub → firewall (DNAT) → spoke → load balancer → VMSS (IIS).

Clean Up

az group delete --name rg-site-to-site-mock-prem-eastus2 --yes
az group delete --name rg-site-to-site-azure-network-eastus2 --yes

Microsoft Open Source Code of Conduct

This project has adopted the Microsoft Open Source Code of Conduct.

Resources: