Debugging VNet Connectivity Problems in Azure
VNet connectivity failures in Azure are caused by one of three things: an NSG rule blocking traffic, a missing or incorrect route in a route table, or a DNS resolution failure. This page provides a systematic diagnostic flow using Network Watcher and Azure CLI to isolate and fix each type of failure.
The diagnostic decision tree
Start with the most specific question and work outward:
- Does the TCP connection reach the destination at all? If the connection times out, suspect NSG or routing. If you get a TLS error or HTTP error, the network path works.
- Is an NSG blocking the traffic? Use IP Flow Verify to answer this definitively.
- Is the traffic being routed to the right place? Use effective routes on the source NIC.
- Is DNS returning the expected IP? Run nslookup from inside the VM.
- Does the destination accept the connection? Use Connection Troubleshoot for end-to-end TCP testing.
This order matters. Many engineers jump to checking NSG rules manually without using IP Flow Verify first, wasting time reading through rules that may not even apply to the traffic path.
Before running any Network Watcher commands, ensure the Network Watcher instance exists in the relevant region:
az network watcher list --output table
# If no watcher exists in eastus:
az network watcher configure \
--resource-group NetworkWatcherRG \
--locations eastus \
--enabled trueStep 1: IP Flow Verify — is an NSG blocking traffic?
IP Flow Verify tests whether a specific 5-tuple (protocol, source IP/port, destination IP/port) would be allowed or denied by NSGs applied to a VM’s NIC.
# Get the VM resource ID
VM_ID=$(az vm show \
--resource-group myResourceGroup \
--name myVM \
--query id -o tsv)
# Test outbound HTTP from VM to a specific IP
az network watcher test-ip-flow \
--vm "$VM_ID" \
--direction Outbound \
--protocol TCP \
--local-ip 10.1.0.4 \
--local-port 12345 \
--remote-ip 10.2.0.5 \
--remote-port 443 \
--output jsonA successful response looks like:
{
"access": "Allow",
"ruleName": "AllowVnetInBound",
"location": "eastus"
}A blocked flow looks like:
{
"access": "Deny",
"ruleName": "DenyInternetOutBound",
"location": "eastus"
}The ruleName field tells you exactly which NSG rule is responsible. Look up that rule to understand the priority and modify it:
# Find and list the specific NSG rule
az network nsg rule show \
--resource-group myResourceGroup \
--nsg-name myNSG \
--name DenyInternetOutBound \
--output jsonIP Flow Verify evaluates NSG rules on the VM’s NIC. It does not evaluate subnet-level NSGs in isolation — it evaluates the effective combination of NIC-level and subnet-level NSG rules together, which is what actually applies to traffic.
Step 2: Effective routes — is traffic routing correctly?
If IP Flow Verify shows traffic is allowed but connectivity still fails, the problem may be routing. Check the effective routes on the VM’s NIC to see where traffic is actually being sent:
# Get NIC name for the VM
NIC_NAME=$(az vm show \
--resource-group myResourceGroup \
--name myVM \
--query "networkProfile.networkInterfaces[0].id" -o tsv \
| xargs -I{} az network nic show --ids {} --query name -o tsv)
# View effective routes on the NIC
az network nic show-effective-route-table \
--resource-group myResourceGroup \
--name "$NIC_NAME" \
--output tableSample output:
Source State Address Prefix Next Hop Type Next Hop IP
-------- ------- ---------------- --------------------- -----------
Default Active 10.1.0.0/16 VnetLocal
Default Active 0.0.0.0/0 Internet
User Active 10.2.0.0/16 VirtualAppliance 10.1.1.4
User Active 0.0.0.0/0 VirtualAppliance 10.1.1.4In this example, a user-defined route sends 0.0.0.0/0 to a network virtual appliance at 10.1.1.4. If that appliance is not running or not configured for internet egress, all outbound traffic fails.
Key things to look for:
- “Active” vs “Invalid”: An “Invalid” state means the route has a problem, such as the next hop address being unreachable.
- Multiple routes for the same prefix: The most specific prefix wins. If two routes match, the one with a longer prefix (e.g., /24 beats /16) wins.
- User routes shadowing system routes: A user route for
0.0.0.0/0overrides the system internet route, which is a common cause of lost internet connectivity.
Step 3: Connection Troubleshoot — end-to-end TCP test
Connection Troubleshoot performs an actual TCP connectivity test from a VM to a destination and shows every hop, including NSG evaluation and routing decisions:
az network watcher test-connectivity \
--source-resource "$VM_ID" \
--dest-address 10.2.0.5 \
--dest-port 443 \
--output jsonThe response includes:
{
"avgLatencyInMs": 3,
"connectionStatus": "Reachable",
"hops": [...],
"maxLatencyInMs": 5,
"minLatencyInMs": 2,
"probesFailed": 0,
"probesSent": 100
}If connectionStatus is “Unreachable”, the hops array shows where the connection failed and why.
Connection Troubleshoot requires the Network Watcher Agent VM extension to be installed on the source VM. If it is not installed, the command will attempt to install it automatically, which requires internet access from the VM. On locked-down VMs, pre-install the extension manually.
VPN connectivity: BGP and local network gateway issues
When traffic should flow through a VPN Gateway but does not, check the BGP peer status and learned routes:
# Check VPN gateway BGP peer status
az network vnet-gateway list-bgp-peer-status \
--resource-group myResourceGroup \
--name myVPNGateway \
--output table
# Check routes learned via BGP
az network vnet-gateway list-learned-routes \
--resource-group myResourceGroup \
--name myVPNGateway \
--output table
# Check routes advertised to the BGP peer
az network vnet-gateway list-advertised-routes \
--resource-group myResourceGroup \
--name myVPNGateway \
--peer 192.168.1.1 \
--output tableIf the BGP peer shows “Unknown” or the tunnel is not connected, check the local network gateway’s address prefix configuration:
az network local-gateway show \
--resource-group myResourceGroup \
--name myLocalGateway \
--query "{localNetworkAddressSpace: localNetworkAddressSpace, gatewayIpAddress: gatewayIpAddress}" \
--output jsonCommon VPN failures:
- Local network gateway address prefix does not match the on-premises network range
- Pre-shared key mismatch between Azure and on-premises device
- BGP AS numbers conflict (Azure uses 65515 by default)
- IKE policy mismatch (algorithms, DH group, lifetime)
Private endpoint connectivity: DNS is the first suspect
When a service with a private endpoint is unreachable from a VM in the VNet, test DNS resolution first. The VM must resolve the service hostname to the private IP, not the public IP.
SSH into the VM and test:
# Should return private IP (10.x.x.x), not public IP
nslookup myaccount.blob.core.windows.net
# Verbose DNS trace to see which server is answering
nslookup -debug myaccount.blob.core.windows.net
# Test TCP connectivity to the resolved IP
curl -v https://myaccount.blob.core.windows.net/mycontainer/myblobIf nslookup returns the public IP, the private DNS zone is either missing, not linked to this VNet, or the VM’s DNS server is not forwarding to Azure DNS (168.63.129.16).
Check private DNS zone links from the CLI:
az network private-dns link vnet list \
--resource-group myResourceGroup \
--zone-name privatelink.blob.core.windows.net \
--output tableCommon mistakes
- Manually reviewing NSG rules instead of using IP Flow Verify. NSGs can have dozens of rules, and both NIC-level and subnet-level NSGs apply. Manually tracing the effective rule is error-prone. IP Flow Verify evaluates them all in the correct order and returns the winning rule name in seconds.
- Adding a custom route table without accounting for health probe traffic. Load balancer health probes come from the special IP 168.63.129.16. If a route table redirects all traffic through a firewall that blocks this IP, health probes fail even though application traffic is fine. Always add an explicit route or NSG rule to allow 168.63.129.16.
- Enabling forced tunneling without configuring the next hop for internet egress. Forced tunneling routes 0.0.0.0/0 to a VPN or firewall. If that appliance does not SNAT and route internet traffic, VMs lose internet access. Verify the next hop appliance is configured for outbound NAT before enabling forced tunneling.
- Testing connectivity from outside the VNet instead of from a VM inside it. Private endpoints and VNet-restricted services only work from inside the VNet. Testing from your laptop will always fail for these resources. Always test from a VM inside the VNet to get an accurate result.
Summary
- Start every VNet connectivity investigation with Network Watcher IP Flow Verify — it identifies the exact NSG rule blocking or allowing traffic without manual rule inspection.
- Use
az network nic show-effective-route-tableto see which route actually applies for a destination — user-defined routes can silently override system routes. - Private endpoint connectivity failures are almost always DNS — the hostname must resolve to the private IP from inside the VNet.
- Connection Troubleshoot provides an end-to-end TCP test including hop-by-hop analysis when IP Flow Verify and routing look correct but connectivity still fails.
Frequently asked questions
What is the first tool to use when a VM cannot reach another resource?
Start with Network Watcher IP Flow Verify to check whether an NSG is blocking the traffic. It gives a yes/no answer immediately and names the specific NSG rule allowing or denying the flow. If IP Flow Verify shows allowed but connectivity still fails, move to effective routes and then DNS resolution.
How do I check if an NSG is blocking traffic without logging into the VM?
Use az network watcher test-ip-flow with the VM resource ID, direction, protocol, local port, remote address, and remote port. This queries the NSG evaluation engine directly without generating actual traffic, so it works even if the VM is unreachable.
Why does a VM lose internet connectivity after I add a custom route table?
A custom route table with a default route (0.0.0.0/0) pointing to a network virtual appliance or custom next hop overrides the system default route to the internet. If that next hop is not configured for internet egress (NAT, firewall with SNAT), outbound traffic has nowhere to go. Check effective routes on the NIC to see which route is winning for 0.0.0.0/0.