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.
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.
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 gatewayson 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.
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.
# 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 changesService Tags
Use Azure service tags instead of IP ranges for Microsoft-managed services — they are automatically updated when Azure changes IPs.
Azure Firewall vs NSG: When to Use Each
These are complementary, not competing. You need both.
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 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:
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.netAzure 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
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
DenyAllInboundas 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 Denyin 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