Back to Blog
★★Intermediate Cloud & Hybrid
AzureVNetNSGAzure FirewallPrivate LinkCloud NetworkingHub-Spoke

Azure Cloud Networking Best Practices: VNet Design, NSGs, Azure Firewall, and Private Link

March 13, 2026·16 min read

Overview

Azure networking is deceptively straightforward to start and surprisingly complex to do correctly at scale. A VNet is just a private network in Azure — but how you design subnets, enforce routing, segment workloads, and control access to PaaS services determines whether you end up with a secure, manageable platform or a patchwork of firewall exceptions and overlapping address spaces.

This post focuses on the patterns that matter: hub-spoke topology, NSG design, Azure Firewall placement, User Defined Routes, Private Endpoints, and the Network Watcher tools that let you see exactly what traffic is doing. It complements the hybrid connectivity post — once your on-prem is connected, this is what runs on top.


VNet Address Space Planning

Get this wrong and you'll be re-IPing workloads years later. Key rules:

  • Never overlap with on-premises. IPAM your cloud ranges from the start. Reserve RFC 1918 blocks intentionally — e.g., 10.0.0.0/8 for on-prem, 172.16.0.0/12 for Azure.
  • Hub VNet: /22 minimum. The hub needs subnets for AzureFirewallSubnet (/26 minimum), GatewaySubnet (/27 minimum), AzureBastionSubnet (/26 minimum), RouteServerSubnet (/27), and a management subnet. These names are not optional — Azure requires exact subnet names for managed services.
  • Spoke VNets: /16 per spoke. Gives you room for future subnets without re-peering.
  • Leave space between spokes. If Spoke A is 10.1.0.0/16, make Spoke B 10.2.0.0/16 — not 10.1.1.0/24. Contiguous allocations simplify route summarization.
  • Never use 10.0.0.0/8 for a single VNet. Too broad to summarize or summarize against.
SubnetRecommended SizeNotes
AzureFirewallSubnet/26 minimumExact name required. Azure Firewall deploys instances here.
GatewaySubnet/27 minimumExact name required. VPN/ER Gateway. Do not place other resources here.
AzureBastionSubnet/26 minimumExact name required. /27 works but /26 is recommended for scale.
RouteServerSubnet/27 minimumExact name required for Azure Route Server.
Spoke app subnets/24 or /25One NSG per subnet. Separate tiers: web, app, db.
Private Endpoint subnet/27–/24Dedicate a subnet for private endpoints — simplifies NSG management.

Hub-Spoke VNet Architecture

The hub-spoke model is the Azure landing zone standard. One central Hub VNet hosts shared services — firewall, gateway, bastion — and spoke VNets connect to it via VNet peering. All spoke-to-spoke and spoke-to-internet traffic transits the hub firewall.

// AZURE HUB-SPOKE VNET TOPOLOGY Internet Public Inbound / Outbound Azure Firewall Premium SKU 10.0.1.0/26 VPN / ER Gateway Active-Active · BGP GatewaySubnet /27 Azure Bastion Standard SKU · RDP/SSH AzureBastionSubnet /26 filtered traffic HTTPS mgmt — HUB VNET 10.0.0.0/22 — Spoke A — Prod Web / App / DB subnets 10.1.0.0/16 Spoke B — Dev Workloads · Test VMs 10.2.0.0/16 Spoke C — Mgmt Jump hosts · Monitoring 10.3.0.0/16 VNet Peering UDR → AzFW Prod VMs NSG + UDR Dev VMs NSG + UDR Jump Hosts via Bastion only On-Premises ExpressRoute / S2S VPN VNet Peering Hybrid (ER/VPN) Internet inbound HTTPS/Bastion

Key hub-spoke rules:

  • VNet peering is non-transitive. Spoke A cannot reach Spoke B directly through the hub — traffic must route via Azure Firewall using UDRs. This is a feature, not a bug: the firewall inspects all spoke-to-spoke traffic.
  • Use-remote-gateway. In peering settings, enable Use remote gateways on spoke peerings so on-premises routes (learned by the hub gateway) propagate to spokes automatically.
  • GatewaySubnet must be empty. Never place NSGs directly on GatewaySubnet — it breaks VPN/ER gateway functionality. NSGs on this subnet are not supported.

Network Security Groups (NSGs)

NSGs are stateful L4 packet filters attached to subnets or individual NICs. Every subnet should have an NSG — attaching to NICs instead creates management sprawl fast.

Rule Evaluation Order

Rules are processed in priority order (100 = first, 65000 = last). Lower number = higher priority. First match wins.

// NSG RULE EVALUATION — INBOUND TRAFFIC Inbound Packet Arrives Source IP · Dest Port · Protocol Priority 100 — Rule Check Match? → Apply Action (Allow / Deny) No match Priority 200 — Rule Check Match? → Apply Action No match ... continue through all custom rules ... Priority 65000 — AllowVnetInBound Allow all VNet-to-VNet (default rule) Priority 65500 — DenyAllInBound Implicit deny — no match = dropped ALLOW traffic flows DENY dropped DENY implicit Allow action Deny (implicit) No match, continue

Application Security Groups (ASGs)

ASGs replace IP addresses in NSG rules with logical groups — WebServers, AppServers, DBServers. When a VM's NIC is assigned to an ASG, it automatically inherits all rules targeting that group.

bash
# Without ASGs: fragile, IP-based rulesAllow TCP 10.1.1.4, 10.1.1.5, 10.1.1.6 → 10.1.2.4, 10.1.2.5 port 8080# With ASGs: readable, maintainableAllow TCP [WebServers-ASG] → [AppServers-ASG] port 8080# Adding a new web server: assign its NIC to WebServers-ASG — no rule changes

Service Tags

Use Azure service tags instead of IP ranges for Microsoft-managed services — they are automatically updated when Azure changes IPs.

TagRepresentsCommon Use
AzureLoadBalancerAzure health probe source IPsAllow inbound health checks on LB backend subnet
AzureMonitorAzure Monitor / Log Analytics IPsAllow outbound from VMs to Log Analytics workspace
Storage.EastAsiaAzure Storage in East Asia regionRegional PaaS access without Private Endpoint
Sql.SoutheastAsiaAzure SQL in Southeast AsiaDB access without Private Endpoint
AppServiceApp Service outbound IPsRestrict traffic from App Service integration subnet
VirtualNetworkAll VNet address spaces + peered + on-premDefault AllowVnetInBound uses this
InternetEverything outside VirtualNetworkAllow/deny internet-facing traffic

Azure Firewall vs NSG: When to Use Each

These are complementary, not competing. You need both.

CapabilityNSGAzure Firewall
LayerL4 (IP + Port)L4–L7 (FQDN, URL, TLS inspection)
FQDN filteringNoYes — allow *.microsoft.com, block others
Threat intelligenceNoYes — Microsoft threat feed, deny known bad IPs
TLS inspectionNoYes (Premium SKU)
IDPSNoYes (Premium SKU)
Centralized loggingFlow logs onlyStructured rule logs, SNAT logs
CostFree~$1.25/hr + $0.016/GB processed
PlacementEvery subnetHub VNet — shared by all spokes

Rule of thumb: NSGs are your inner subnet boundary — cheap, fast, IP/port based. Azure Firewall is your perimeter — expensive, powerful, centrally managed. Use NSGs to enforce micro-segmentation within a spoke, and Azure Firewall to control spoke-to-spoke and spoke-to-internet.


User Defined Routes (UDRs)

Azure's default system routes allow all subnets in a VNet to communicate. UDRs override this to force traffic through a firewall or NVA.

The critical hub-spoke UDR: In every spoke subnet, create a route for 0.0.0.0/0 (default route) pointing to the Azure Firewall private IP. This forces all outbound internet and spoke-to-spoke traffic through the firewall.

# Azure CLI — Create UDR forcing all traffic through Azure Firewall
az network route-table create \
  --name RT-Spoke-A \
  --resource-group rg-networking \
  --location southeastasia

az network route-table route create \
  --route-table-name RT-Spoke-A \
  --resource-group rg-networking \
  --name Default-Via-AzFW \
  --address-prefix 0.0.0.0/0 \
  --next-hop-type VirtualAppliance \
  --next-hop-ip-address 10.0.1.4
# 10.0.1.4 = Azure Firewall private IP (always .4 in AzureFirewallSubnet)

az network vnet subnet update \
  --vnet-name vnet-spoke-a \
  --resource-group rg-networking \
  --name snet-workloads \
  --route-table RT-Spoke-A
# Associate the route table to the spoke subnet

Blackhole routes for PaaS bypass: If you have Private Endpoints for Storage/SQL, add specific routes for those service IP ranges pointing to VirtualNetworkGateway to prevent hairpinning to the firewall for internal traffic.


Private Endpoints and Private Link

Without Private Endpoints, your VMs access Azure Storage, SQL, Key Vault, and other PaaS services over the internet (even from within Azure — traffic exits the VNet). Private Endpoints bring PaaS services into your VNet with a private IP, making them reachable over your private network only.

// PRIVATE ENDPOINT — PAAS SERVICE ACCESS App VM 10.1.0.4 snet-workloads Private Endpoint NIC in snet-pe 10.1.4.5 (private IP) Azure Storage PaaS Service Private Link backend private IP Private Link Private DNS Zone privatelink.blob.core.windows.net DNS A record → 10.1.4.5 Without PE DNS → public IP traffic exits VNet With PE DNS → private IP traffic stays in VNet Private (recommended) DNS resolution Public (avoid in prod)

Private DNS Zone is mandatory. Without it, DNS still resolves the PaaS FQDN to a public IP — the private endpoint exists but gets bypassed. Every Private Endpoint needs a corresponding Private DNS Zone linked to the VNet.

Required Private DNS Zones by service:

bash
Azure Storage (Blob):  privatelink.blob.core.windows.netAzure SQL:             privatelink.database.windows.netAzure Key Vault:       privatelink.vaultcore.azure.netAzure Container Reg.:  privatelink.azurecr.ioAzure Service Bus:     privatelink.servicebus.windows.net

Azure DDoS Protection

Basic (free): Always-on for all Azure public IPs. Protects against common volumetric attacks at the Azure network level.

Standard / Network Protection: Per-VNet, ~$2,944/month base + $0.008/GB over threshold. Worth it for public-facing apps with SLA requirements. Provides:

  • Adaptive tuning per public IP — learns your normal traffic baseline
  • Attack telemetry and real-time metrics
  • DDoS Rapid Response (Microsoft support during active attack)
  • Cost guarantee: Azure credits for scale-up costs caused by DDoS

For most enterprise workloads, place public-facing services behind Azure Front Door or Application Gateway — both have built-in WAF and absorb many attack vectors before they reach your VNet.


Network Watcher: Troubleshooting Toolkit

Network Watcher is the single most useful Azure networking diagnostic tool. Enable it in every region you deploy to.

IP Flow Verify

Answers: "Will this packet be allowed or denied, and by which NSG rule?"

# Test if VM can reach SQL on port 1433
az network watcher test-ip-flow \
  --direction Outbound \
  --local 10.1.0.4:60000 \
  --remote 10.1.2.10:1433 \
  --protocol TCP \
  --vm /subscriptions/.../resourceGroups/rg-prod/providers/Microsoft.Compute/virtualMachines/vm-app-01 \
  --nic /subscriptions/.../networkInterfaces/vm-app-01-nic

# Output: Access: Allow / Deny  RuleName: Allow-App-To-DB
# If Deny: RuleName tells you exactly which NSG rule blocked it

Connection Monitor

Continuously tests connectivity between sources and destinations (VM → VM, VM → external URL). Generates availability and latency metrics over time — far more useful than one-off ping tests.

NSG Flow Logs + Traffic Analytics

NSG Flow Logs record every accepted/denied flow through each NSG. Traffic Analytics processes these logs in Log Analytics and provides:

  • Top talkers, protocols, ports
  • Traffic by country/region
  • Anomalous flows (high-volume unexpected sources)
  • Malicious IP detection
# Query: Top denied flows in last 24 hours (Log Analytics KQL)
AzureNetworkAnalytics_CL
| where TimeGenerated > ago(24h)
| where FlowStatus_s == "D"
| summarize DeniedFlows = count() by SrcIP_s, DestIP_s, DestPort_d, L4Protocol_s
| top 20 by DeniedFlows desc
// Shows top 20 denied flows by volume — helps identify misconfigured NSG rules

# Find all flows to a specific destination port
AzureNetworkAnalytics_CL
| where TimeGenerated > ago(1h)
| where DestPort_d == 3389
| where FlowStatus_s == "A"
| project SrcIP_s, DestIP_s, SrcVM_s, DestVM_s, FlowStatus_s
// RDP (3389) accepted flows — should only be from Azure Bastion subnet

Packet Capture

Captures packets on a VM NIC without needing to log in — useful for deep protocol analysis.

# Start packet capture on a VM — saves to Storage Account
az network watcher packet-capture create \
  --resource-group rg-prod \
  --vm vm-app-01 \
  --name cap-debug-session \
  --storage-account st-captures-prod \
  --time-limit 300 \
  --filters '[{"protocol":"TCP","remoteIPAddress":"10.1.2.10","remotePort":"1433"}]'
# Filters to only capture TCP to DB server — avoids noisy captures
# Download the .cap file from Storage and open in Wireshark

Troubleshooting Reference

SymptomFirst CheckCommon Cause
VM can't reach internetIP Flow Verify outbound to 8.8.8.8:443UDR default route pointing to AzFW, and AzFW missing outbound FQDN rule
Spoke A can't reach Spoke BCheck UDR on both spokes — default route to AzFWAzFW network rule missing for spoke-to-spoke CIDR pair
App can't reach Private Endpointnslookup storage.blob.core.windows.net — should return 10.x.x.xPrivate DNS Zone not linked to VNet, or A record missing
LB health probe failingCheck NSG — AzureLoadBalancer service tag must be permitted inboundNSG missing allow rule for AzureLoadBalancer tag on backend subnet
VPN Gateway unreachableCheck GatewaySubnet — NSG applied? BGP peer IP correct?NSG on GatewaySubnet (unsupported), or BGP ASN mismatch
Bastion can't RDP to VMNSG on VM subnet — allow TCP 3389 from AzureBastionSubnet CIDRVM subnet NSG blocking 3389 from Bastion subnet 10.0.2.0/26
Azure Firewall blocking known-good trafficAzFW logs in Log Analytics — AzureDiagnostics | where Category == "AzureFirewallNetworkRule"Default deny — missing network rule or application rule for FQDN

Azure Networking Hardening Checklist

  • No public IPs on VMs unless absolutely required — use Azure Bastion for management access
  • NSG on every subnet — never rely solely on Azure Firewall for intra-VNet segmentation
  • GatewaySubnet has no NSG attached — Azure will warn you, listen to it
  • DenyAllInbound as a named rule at priority 4000 before the implicit 65500 — makes denial explicit and auditable
  • Private Endpoints for all PaaS services in scope — Storage, SQL, Key Vault minimum
  • Private DNS Zones linked to all VNets that need PE resolution
  • NSG Flow Logs enabled on all NSGs — send to Log Analytics with Traffic Analytics
  • Azure DDoS Network Protection on Hub VNet for production workloads
  • Azure Firewall Threat Intelligence mode set to Alert and Deny in production
  • Diagnostic settings on all resources — Azure Firewall, VPN Gateway, Application Gateway — to Log Analytics
  • Tag all networking resources with Environment, Owner, CostCenter for governance