Skip to main content

Azure Private Link, Service Endpoints, and Hub-Spoke Topology

· 9 min read
Goel Academy
DevOps & Cloud Learning Hub

You have VNets, subnets, and NSGs from the basics. But your security team wants to know why your storage account is accessible from the public internet, your database traffic traverses Microsoft's backbone instead of your private network, and your 12 spoke VNets have no central firewall. Time to graduate to enterprise networking — where every PaaS service gets a private IP and every packet flows through your inspection points.

Service Endpoints vs Private Endpoints

Both solve the same problem — keeping traffic to Azure PaaS services (Storage, SQL, Key Vault) off the public internet. But they work very differently.

FeatureService EndpointsPrivate Endpoints (Private Link)
How it worksAdds an optimized route from subnet to PaaS serviceCreates a private NIC with a VNet IP for the PaaS service
Traffic pathAzure backbone (still uses public IP)Entirely within your VNet (private IP)
DNSNo DNS change neededRequires Private DNS Zone
Cross-regionSame region onlyWorks across regions and tenants
On-premises accessNot reachable from on-premReachable from on-prem via VPN/ExpressRoute
CostFree~$7.30/month per endpoint + data processing
GranularitySubnet level (all resources of that type)Per-resource (specific storage account)

The recommendation: Use Private Endpoints for production workloads. They give you a real private IP address on your VNet, which means on-premises clients can reach the service through VPN/ExpressRoute, and you get full DNS control.

# Service Endpoint approach — add to a subnet
az network vnet subnet update \
--resource-group rg-network \
--vnet-name vnet-spoke-01 \
--name snet-app \
--service-endpoints Microsoft.Storage Microsoft.Sql Microsoft.KeyVault

Private Link creates a private endpoint — a network interface with a private IP in your VNet that maps to a specific PaaS resource.

# Create a Private Endpoint for a Storage Account
az network private-endpoint create \
--name pe-storage-prod \
--resource-group rg-network \
--vnet-name vnet-spoke-01 \
--subnet snet-private-endpoints \
--private-connection-resource-id "/subscriptions/<sub-id>/resourceGroups/rg-data/providers/Microsoft.Storage/storageAccounts/stproddata2025" \
--group-id blob \
--connection-name "storage-prod-connection"

# Create the Private DNS Zone for blob storage
az network private-dns zone create \
--resource-group rg-network \
--name "privatelink.blob.core.windows.net"

# Link the DNS zone to your VNet
az network private-dns zone-link create \
--resource-group rg-network \
--zone-name "privatelink.blob.core.windows.net" \
--name "link-vnet-spoke-01" \
--virtual-network vnet-spoke-01 \
--registration-enabled false

# Create the DNS record for the private endpoint
az network private-endpoint dns-zone-group create \
--resource-group rg-network \
--endpoint-name pe-storage-prod \
--name "default" \
--private-dns-zone "privatelink.blob.core.windows.net" \
--zone-name "blob"

After this, any VM or service in vnet-spoke-01 resolving stproddata2025.blob.core.windows.net will get the private IP (e.g., 10.1.5.4) instead of the public IP. Traffic never leaves your network.

Hub-Spoke Network Architecture

Hub-Spoke is the standard enterprise network topology in Azure. A central hub VNet contains shared services (firewall, VPN gateway, DNS). Spoke VNets contain workloads and peer to the hub.

                    On-Premises
|
VPN Gateway / ExpressRoute
|
┌─────────────┐
│ Hub VNet │
│ 10.0.0.0/16 │
│ │
│ Azure Firewall│
│ Bastion Host │
│ DNS Resolver │
└──┬───┬───┬──┘
│ │ │
┌────────┘ │ └────────┐
│ │ │
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Spoke 01 │ │ Spoke 02 │ │ Spoke 03 │
│ Web Tier │ │ App Tier │ │ Data Tier│
│10.1.0/16 │ │10.2.0/16 │ │10.3.0/16 │
└──────────┘ └──────────┘ └──────────┘
# Create the hub VNet
az network vnet create \
--resource-group rg-hub \
--name vnet-hub \
--address-prefix 10.0.0.0/16 \
--subnet-name AzureFirewallSubnet \
--subnet-prefix 10.0.1.0/24

# Create a spoke VNet
az network vnet create \
--resource-group rg-spoke-01 \
--name vnet-spoke-01 \
--address-prefix 10.1.0.0/16 \
--subnet-name snet-app \
--subnet-prefix 10.1.1.0/24

# Peer spoke to hub (both directions)
az network vnet peering create \
--resource-group rg-spoke-01 \
--name spoke01-to-hub \
--vnet-name vnet-spoke-01 \
--remote-vnet "/subscriptions/<sub-id>/resourceGroups/rg-hub/providers/Microsoft.Network/virtualNetworks/vnet-hub" \
--allow-forwarded-traffic true \
--allow-gateway-transit false \
--use-remote-gateways true

az network vnet peering create \
--resource-group rg-hub \
--name hub-to-spoke01 \
--vnet-name vnet-hub \
--remote-vnet "/subscriptions/<sub-id>/resourceGroups/rg-spoke-01/providers/Microsoft.Network/virtualNetworks/vnet-spoke-01" \
--allow-forwarded-traffic true \
--allow-gateway-transit true

Key flags: --allow-gateway-transit true on the hub side lets spokes use the hub's VPN/ExpressRoute gateway. --use-remote-gateways true on the spoke side enables this.

Azure Firewall in the Hub

Azure Firewall sits in the hub VNet and inspects all traffic flowing between spokes, to the internet, and to on-premises. It supports FQDN filtering, threat intelligence, TLS inspection, and network/application rules.

# Create a public IP for the firewall
az network public-ip create \
--resource-group rg-hub \
--name pip-azfw \
--sku Standard \
--allocation-method Static

# Create the Azure Firewall
az network firewall create \
--resource-group rg-hub \
--name azfw-hub \
--location eastus \
--sku AZFW_VNet \
--tier Standard

# Configure the firewall IP
az network firewall ip-config create \
--resource-group rg-hub \
--firewall-name azfw-hub \
--name fw-config \
--public-ip-address pip-azfw \
--vnet-name vnet-hub

# Add a network rule to allow spoke-to-spoke traffic
az network firewall network-rule create \
--resource-group rg-hub \
--firewall-name azfw-hub \
--collection-name "allow-spoke-traffic" \
--name "spoke01-to-spoke02" \
--protocols TCP UDP \
--source-addresses 10.1.0.0/16 \
--destination-addresses 10.2.0.0/16 \
--destination-ports '*' \
--action Allow \
--priority 100

User Defined Routes (UDR)

By default, spoke-to-spoke traffic goes directly via VNet peering. To force traffic through the firewall for inspection, you create UDRs that override the default routes.

# Create a route table
az network route-table create \
--resource-group rg-spoke-01 \
--name rt-spoke-01 \
--disable-bgp-route-propagation true

# Add a default route pointing to the firewall
az network route-table route create \
--resource-group rg-spoke-01 \
--route-table-name rt-spoke-01 \
--name "to-firewall" \
--address-prefix 0.0.0.0/0 \
--next-hop-type VirtualAppliance \
--next-hop-ip-address 10.0.1.4 # Azure Firewall private IP

# Associate the route table with the spoke subnet
az network vnet subnet update \
--resource-group rg-spoke-01 \
--vnet-name vnet-spoke-01 \
--name snet-app \
--route-table rt-spoke-01

Now all outbound traffic from snet-app in Spoke 01 flows through Azure Firewall first. The firewall applies your rules, logs the traffic, and forwards allowed packets. This gives you a single enforcement point for all east-west and north-south traffic.

Azure Bastion for Secure VM Access

Stop exposing RDP (3389) and SSH (22) to the internet. Azure Bastion provides browser-based access to your VMs over TLS without a public IP on the VM.

# Create the AzureBastionSubnet (name and minimum /26 are required)
az network vnet subnet create \
--resource-group rg-hub \
--vnet-name vnet-hub \
--name AzureBastionSubnet \
--address-prefix 10.0.2.0/26

# Create a public IP for Bastion
az network public-ip create \
--resource-group rg-hub \
--name pip-bastion \
--sku Standard \
--allocation-method Static

# Deploy Azure Bastion
az network bastion create \
--resource-group rg-hub \
--name bastion-hub \
--vnet-name vnet-hub \
--public-ip-address pip-bastion \
--sku Standard \
--enable-tunneling true

With the Standard SKU and tunneling enabled, you can also use native SSH/RDP clients through the az network bastion tunnel command — useful for file transfers and tools that need a native connection.

Network Watcher Tools

Network Watcher is Azure's network diagnostic toolkit. It is enabled per-region and provides:

ToolWhat It Does
IP Flow VerifyChecks if a packet is allowed or denied by NSG rules
Next HopShows the next hop for a packet from a VM
Connection TroubleshootTests TCP connectivity between two endpoints
Packet CaptureCaptures packets on a VM's NIC for Wireshark analysis
NSG Flow LogsLogs all traffic allowed/denied by NSGs
TopologyVisualizes your network resources and connections
# Verify if a VM can reach a storage account on port 443
az network watcher test-ip-flow \
--resource-group rg-spoke-01 \
--vm vm-app-01 \
--direction Outbound \
--protocol TCP \
--local 10.1.1.4:* \
--remote 10.0.5.10:443

# Check the next hop for traffic from a VM
az network watcher show-next-hop \
--resource-group rg-spoke-01 \
--vm vm-app-01 \
--source-ip 10.1.1.4 \
--dest-ip 10.2.1.4

VNet Integration for App Service

App Service runs on shared infrastructure by default, outside your VNet. VNet Integration lets your web app's outbound traffic flow through your VNet, reaching private endpoints and on-premises resources.

# Create a dedicated subnet for App Service integration
az network vnet subnet create \
--resource-group rg-spoke-01 \
--vnet-name vnet-spoke-01 \
--name snet-appservice \
--address-prefix 10.1.2.0/24 \
--delegations Microsoft.Web/serverFarms

# Integrate the App Service with the VNet
az webapp vnet-integration add \
--resource-group rg-app \
--name webapp-prod \
--vnet vnet-spoke-01 \
--subnet snet-appservice

After integration, your App Service can reach Private Endpoints (databases, storage, Key Vault) using their private IPs. Combine this with access restrictions to block public inbound traffic, and your App Service becomes fully private.

ExpressRoute Overview

ExpressRoute is a dedicated private connection between your on-premises network and Azure — it does not traverse the public internet. Connections go through a connectivity partner (AT&T, Equinix, Megaport) at a peering location.

FeatureVPN GatewayExpressRoute
ConnectionOver public internet (encrypted)Private dedicated link
BandwidthUp to 10 GbpsUp to 100 Gbps
LatencyVariablePredictable, low
EncryptionIPSec built-inOptional (MACsec)
Cost~$140-800/month~$200-10,000+/month
Best ForSmall offices, dev/testProduction hybrid, data-heavy

ExpressRoute circuits connect to your hub VNet's ExpressRoute Gateway. Because spokes peer to the hub with use-remote-gateways, they automatically get on-premises connectivity without additional configuration.

Wrapping Up

Enterprise Azure networking comes down to three principles: keep traffic private (Private Link for PaaS, Bastion for management), centralize inspection (Hub-Spoke with Azure Firewall and UDRs), and integrate fully (VNet Integration for PaaS, ExpressRoute for on-premises). Start with a hub VNet containing your firewall and bastion, peer your workload spokes, and add private endpoints for every PaaS service. Use Network Watcher when things break. The result is a network where nothing is exposed that should not be, and every packet has a clear, auditable path.


Next up: We will explore Microsoft Defender for Cloud — how to assess your security posture, fix vulnerabilities, and protect workloads across compute, storage, databases, and containers.