Azure Virtual Network Not Working, Connectivity, Rules, and Routing Fixes

Microsoft Fix Intermediate 18 min read Official Docs Grounded Updated April 20, 2026

Why This Is Happening

I've worked through Azure virtual network connectivity failures more times than I can count, and there's one thing almost every case has in common: the person affected had no idea which layer of the stack was blocking traffic. Was it the subnet NSG? The NIC-level NSG? A user-defined route gone wrong? Azure's error messages, if you even get one, rarely point you to the actual culprit. You're left staring at a connection timeout with zero useful context.

Here's the situation I see constantly. Someone spins up a new VM, assigns it to a subnet, and then tries to RDP in on port 3389. Dead silence. Or a dev team deploys an application server and HTTP traffic just refuses to flow, even though they swear they added an allow rule. The problem almost always traces back to how Azure evaluates Network Security Group rules, and the order in which those rules get processed is not obvious from the portal.

Azure virtual network connectivity problems typically fall into three buckets:

NSG rule conflicts: You have an allow rule, but a higher-priority deny rule is silently overriding it. Since Azure processes rules by priority number, lowest number wins, a deny rule sitting at priority 100 will always beat your allow rule at priority 200, no matter how carefully you wrote the allow rule.

Dual-NSG mismatches: Azure lets you attach an NSG to both a subnet and to an individual network interface card. For inbound traffic, the subnet NSG gets evaluated first, then the NIC NSG. Both have to say "allow" for traffic to get through. I've seen setups where the subnet NSG was perfect, but an older NIC-level NSG from a cloned VM template was blocking everything silently.

Routing table misconfiguration: User-defined routes (UDRs) can redirect traffic to an NVA (network virtual appliance) or a VPN gateway that isn't configured to forward it onward. Traffic enters your virtual network, hits the UDR, gets sent somewhere it shouldn't go, and never arrives at its destination.

There are also subtler causes: peering configurations where gateway transit wasn't enabled, missing service endpoints for PaaS services, DNS resolution failures being misread as connectivity failures, and NSG flow log data that's hours stale because diagnostic settings weren't configured correctly. Each of these has its own fix path, and I'll walk you through all of them.

What makes Azure virtual network troubleshooting especially frustrating is that the default deny rules built into every NSG, specifically DenyAllInBound at priority 65500 and DenyAllOutBound at priority 65500, are invisible until you go looking for them. Microsoft's portal doesn't scream at you that these rules exist. They're just quietly blocking everything you haven't explicitly allowed.

Browse all Microsoft fix guides →

The Quick Fix, Try This First

Before you spend an hour digging through route tables and peering configurations, run Network Watcher's IP Flow Verify. This is the fastest way to determine whether an NSG rule is blocking your traffic, which direction it's being blocked, and exactly which rule is responsible. I've seen this tool cut Azure virtual network troubleshooting sessions from three hours down to ten minutes.

Here's how to run it right now:

  1. In the Azure portal, search for Network Watcher in the top search bar and open it.
  2. In the left sidebar under Network diagnostic tools, click IP flow verify.
  3. Select your Subscription, Resource group, and the specific Virtual machine you're trying to reach.
  4. Select the correct Network interface for that VM.
  5. Set the Protocol (TCP or UDP), Direction (Inbound or Outbound), Local IP (the VM's private IP), Local port (e.g., 3389 for RDP), Remote IP (the source IP you're connecting from), and Remote port.
  6. Click Check.

If traffic is being denied, the tool tells you exactly which NSG and which rule is responsible, down to the rule name and priority number. If it says Access: Allow but you still can't connect, the problem isn't your NSG. It's routing, DNS, or the application itself, and you'll need the steps further down this guide.

If IP Flow Verify confirms a block, go straight to that NSG in the portal, find the offending rule, and either remove it or add a higher-priority allow rule above it. Remember: lower priority number = higher priority in Azure's evaluation order. Add your allow rule at a lower number than the blocking rule.

Pro Tip
Network Watcher must be enabled in the same region as your virtual network. If you get an error saying Network Watcher isn't available, go to Network Watcher → Overview and click Enable for the relevant region. It takes about 30 seconds and it's free, there's no reason not to have it enabled everywhere you have VNets deployed.
1
Audit Your NSG Rule Priority Order

The single most common cause of Azure virtual network connectivity failures is a priority conflict, specifically, a deny rule with a lower priority number overriding your allow rule. Let me show you how to check this properly.

Navigate to Virtual networks → [Your VNet] → Subnets. Click the subnet in question and note whether there's an NSG listed under Network security group. Then go to that NSG. In the left panel, click Inbound security rules (or Outbound, depending on your traffic direction).

What you're looking for: any Deny rule with a priority number lower than your allow rule. Rules are sorted by priority in the portal by default, lower numbers appear first because they have higher precedence. If you see a Deny rule at priority 200 and your Allow rule is at priority 300, the deny wins every time, full stop.

Azure evaluates rules in this order for inbound traffic:

  1. Subnet-level NSG rules (evaluated first)
  2. NIC-level NSG rules (evaluated second)

Both NSGs must permit the traffic. Check both. To find the NIC-level NSG, go to Virtual machines → [Your VM] → Networking → Network Interface, then click the NIC and look for its attached NSG under Settings → Network security group.

The fix: add a new allow rule with a priority number lower than the offending deny rule. For example, if a deny rule sits at priority 200, add your allow rule at priority 150. Don't delete the default deny rules, they serve a purpose, just make sure your explicit allows are evaluated before them.

You know it worked when: IP Flow Verify shows Access: Allow and names your new rule as the matching rule.

2
Check Both NSG Levels, Subnet and NIC

I can't tell you how many tickets I've seen where someone spent two hours fixing the subnet NSG, only to discover there was a separate NIC-level NSG quietly blocking the same traffic. Azure applies NSGs at two different attachment points, and both have to agree.

For inbound connections, here's the exact evaluation sequence Azure follows: traffic hits the subnet boundary first, where the subnet-level NSG checks it against its rules. If that NSG allows the traffic, the packet moves on to the virtual machine's network interface card, where the NIC-level NSG runs its own independent rule check. If either one of them issues a deny, the connection dies right there.

For outbound traffic from a VM, the order flips: the NIC-level NSG evaluates first, then the subnet-level NSG.

To audit both levels systematically, use the Effective security rules view. Go to Virtual machines → [Your VM] → Networking, then click Network interface at the top. In the NIC blade, click Effective security rules under Support + troubleshooting. This single view shows you a merged, flattened list of all rules from both NSGs, subnet and NIC, in the order Azure actually evaluates them. This is dramatically faster than checking each NSG manually.

Look for any rule showing Deny in the Action column that covers the port and protocol you're trying to use. If you see a deny before your allow, that's your culprit.

Also check that both NSGs are actually associated correctly. A common mistake during VM migrations or cloning operations: the NIC ends up with an NSG from the source environment that's attached but never updated for the new network context.

You know it worked when: The Effective security rules view shows a clear Allow path for your target port and protocol with no earlier Deny rule intercepting it.

3
Verify Default Rules Aren't Silently Blocking You

Every NSG in Azure, every single one, without exception, comes with a set of built-in default rules that you cannot delete. They're there for a reason, but they're also the invisible wall that most Azure virtual network troubleshooting guides skip over.

Here are the ones that matter most for inbound traffic:

  • AllowVnetInBound (priority 65000): Allows all traffic originating from within the same virtual network address space. This is why VM-to-VM traffic inside the same VNet typically works without any custom rules.
  • AllowAzureLoadBalancerInBound (priority 65001): Permits health probes and traffic from the Azure Load Balancer service tag. If this is missing or blocked, your load-balanced VMs will appear unhealthy.
  • DenyAllInBound (priority 65500): Blocks all inbound traffic that no earlier rule has explicitly allowed. This is the wall. Anything not covered by a rule at priority 65499 or lower gets hit by this.

And for outbound:

  • AllowVnetOutBound (priority 65000): Allows outbound traffic to addresses within the VNet.
  • AllowInternetOutBound (priority 65001): Permits outbound traffic to the internet. Note: if you add a deny rule targeting the Internet service tag at a priority below 65001, you'll cut off all internet access from your VMs.
  • DenyAllOutBound (priority 65500): Blocks all outbound traffic not explicitly allowed.

The fix for most "everything is blocked" scenarios: add explicit allow rules for each traffic type you need, with priority numbers well below 65000. A common convention is to start custom rules at priority 100 and increment by 10 for each subsequent rule, this leaves room to insert rules between existing ones without renumbering everything.

For RDP access, add an inbound rule: protocol TCP, destination port 3389, source set to your specific IP range (not "Any", that's a security exposure), action Allow, priority something like 110.

You know it worked when: Your new explicit allow rule appears in the Effective security rules list above the DenyAllInBound rule for the relevant port.

4
Diagnose Routing Problems with Next Hop

If IP Flow Verify tells you traffic should be allowed but you still can't reach your VM, the problem has moved from NSG rules to routing. User-defined routes (UDRs), BGP-propagated routes from VPN gateways, and Azure's system routes can all interact in unexpected ways that silently drop or misdirect traffic.

Use Network Watcher's Next hop tool to see exactly where Azure would send a packet originating from your VM. Go to Network Watcher → Next hop, select your subscription and resource group, pick the VM and network interface, then enter the source IP (your VM's private IP) and the destination IP you're trying to reach. Click Next hop.

The output tells you three things: the next hop type (Internet, VirtualAppliance, VirtualNetworkGateway, None, etc.), the next hop IP address if applicable, and the route table entry that determined this outcome. If the result shows None as the next hop type, it means Azure has no valid route to that destination and is dropping the traffic.

Common routing problems I see:

  • UDR pointing to a dead NVA: A route table entry sends traffic to a firewall appliance IP that's been deallocated or changed. Traffic enters, hits the UDR, goes to a non-responsive IP, and dies.
  • Missing peering routes: VNet peering exists but Allow gateway transit or Use remote gateways settings are misconfigured, so routes aren't propagating between peered networks.
  • Overlapping address spaces: Two subnets or peered VNets have overlapping CIDR ranges. Azure's routing gets confused about which path to use.

To check route tables: go to Virtual networks → [Your VNet] → Subnets → [Your subnet] and click the associated route table if one exists. Review all custom routes and verify their next hop addresses are still valid and reachable.

You know it worked when: Next hop returns a valid next hop type (not "None") pointing to the correct destination for your traffic flow.

5
Enable and Read NSG Flow Logs

When you need a full audit trail of what's actually flowing through your NSGs, not what should be happening in theory, but what's actually happening in practice, NSG flow logs are your answer. This is the tool that tells you definitively which traffic Azure accepted, which it denied, and from which IP addresses.

To enable NSG flow logs, go to Network Watcher → Flow logs in the left sidebar. Click + Create. Select your subscription, then choose the NSG you want to monitor. You'll need a storage account to write logs to, create one in the same region as your NSG if you don't have one already. Set the retention period (30 days is usually enough for troubleshooting). Enable Traffic Analytics if you want aggregated insights in a Log Analytics workspace.

Once enabled, flow log data is written to your storage account in JSON format at paths structured like this:

insights-logs-networksecuritygroupflowevent/resourceId=/SUBSCRIPTIONS/[ID]/RESOURCEGROUPS/[RG-NAME]/PROVIDERS/MICROSOFT.NETWORK/NETWORKSECURITYGROUPS/[NSG-NAME]/y=[YEAR]/m=[MONTH]/d=[DAY]/h=[HOUR]/m=[MINUTE]

The logs include entries for every evaluated flow: source IP, destination IP, source port, destination port, protocol, flow direction, and whether it was allowed (A) or denied (D). Look for denied flows matching your troublesome traffic pattern.

If you've enabled Traffic Analytics, you can query this data much more elegantly through Log Analytics. Navigate to Log Analytics workspace → Logs and run queries against the AzureNetworkAnalytics_CL table. A quick query to find denied flows looks like this:

AzureNetworkAnalytics_CL
| where FlowType_s == "MaliciousFlow" or FlowStatus_s == "D"
| where TimeGenerated > ago(1h)
| project SrcIP_s, DestIP_s, DestPort_d, L4Protocol_s, FlowStatus_s
| order by TimeGenerated desc

You know it worked when: You can see flow log entries in your storage account or Traffic Analytics workspace and have identified the specific denied flows matching your connectivity problem.

Advanced Troubleshooting

If the standard NSG rule checks and Network Watcher tools haven't cracked it, here's where things get more technical. These are the scenarios I see in enterprise environments, domain-joined VMs, complex hub-and-spoke topologies, forced tunneling, and cross-subscription VNet peering.

Forced Tunneling and Default Route Overrides

In many enterprise environments, all outbound internet traffic is routed through an on-premises network or a centralized NVA firewall. This is done by injecting a 0.0.0.0/0 default route via a UDR or through BGP from an ExpressRoute or VPN gateway. When this is in place, traffic that you expect to flow directly to the internet, including Azure management plane calls, gets redirected. If the NVA or on-premises firewall doesn't have the right rules, it drops the traffic silently.

To check for this: use the Next Hop tool from Step 4 above, and test the route for destination 8.8.8.8 or any internet IP. If the result is VirtualAppliance or VirtualNetworkGateway instead of Internet, you're running forced tunneling. Work with your network team to verify the NVA or gateway is configured to forward the specific traffic you need.

VNet Peering, Allow/Use Gateway Settings

VNet peering is a popular way to connect multiple virtual networks, but it has a notable gotcha: peering does not automatically propagate routes from a VPN gateway in one VNet to a peered VNet. You have to explicitly configure it. In the peering settings, the hub VNet must have Allow gateway transit enabled, and the spoke VNet must have Use remote gateways enabled. If either setting is missing, spoke VMs won't have routes to on-premises networks, even though the peering itself shows as Connected.

Check this at: Virtual networks → [Hub VNet] → Peerings → [Peering name] → edit and verify both checkboxes.

Service Endpoints and Private Endpoints

If you're trying to reach an Azure PaaS service (like Azure Storage or SQL Database) from a VM and getting connection timeouts, check whether a service endpoint or private endpoint is involved. If a private endpoint exists for the service, DNS resolution might be returning the private IP, but your NSG or route table might not be allowing traffic to that private IP range. Use nslookup [your-storage-account].blob.core.windows.net from inside the VM to see what IP it resolves to. A private endpoint resolves to a 10.x.x.x or similar RFC1918 address rather than a public IP.

Connection Monitor for Ongoing Visibility

For persistent monitoring of Azure virtual network connectivity between specific endpoints, set up Connection Monitor in Network Watcher. It actively probes your connections on a schedule and alerts you when connectivity degrades. Go to Network Watcher → Connection monitor → + Create and configure source and destination endpoints with your chosen protocol and port. This is far better than manual spot-checks for catching intermittent issues.

When to Call Microsoft Support
If you've exhausted NSG audits, routing diagnostics, flow log analysis, and peering configuration checks, and traffic is still being dropped with no clear rule accounting for it, it's time to escalate. Platform-level issues, such as routing anomalies in Azure's underlying fabric, bugs in NSG rule processing for specific SKUs, or ExpressRoute BGP advertisement problems, are not diagnosable from the portal. Open a support ticket through Azure Portal → Help + support → New support request with severity A or B, attach your Network Watcher diagnostics output, and reference your subscription ID and affected resource IDs. Alternatively, go directly to Microsoft Support for additional channels.

Prevention & Best Practices

The best Azure virtual network troubleshooting session is the one you never have to have. Most of the NSG misconfigurations and routing problems I've walked through above are entirely preventable with a few consistent habits.

Document your rule intent, not just the rule. An NSG rule that says "priority 200, allow TCP 443 from Any" tells you what it does. It doesn't tell you why it exists, which application depends on it, or what happens if you remove it. Add descriptions to every custom NSG rule the moment you create it. The description field in the portal supports plain text, use it. Six months from now, the person troubleshooting that rule (possibly you) will thank you.

Always use Application Security Groups for multi-VM environments. Instead of writing rules against specific IP addresses that change when VMs get deallocated and re-allocated, assign VMs to Application Security Groups (ASGs) and write NSG rules targeting those ASGs. This makes rules portable, readable, and much less likely to break silently when infrastructure changes.

Test connectivity immediately after every NSG change. Don't deploy a new rule and walk away. Open IP Flow Verify, run a quick check for each traffic type the rule was meant to affect, and confirm the result. This takes two minutes and catches priority conflicts before they cause an incident.

Enable NSG flow logs with Traffic Analytics before you need them. You don't want to be enabling flow logs for the first time in the middle of an outage, waiting for data to populate while your service is down. Enable them proactively on all production NSGs and build the habit of reviewing Traffic Analytics weekly. Anomalies in denied flow counts are an early warning sign of misconfigurations or security events.

Use infrastructure as code for NSG management. Whether it's Bicep, Terraform, or ARM templates, defining your NSG rules in code means you have version history, peer review, and the ability to detect drift between what's in code and what's deployed. Portal-only NSG management is how you end up with undocumented rules nobody can explain.

Quick Wins
  • Enable Network Watcher in every Azure region where you have virtual networks, it's free and you'll need it.
  • Set NSG flow log retention to at least 30 days on all production subnets.
  • Assign descriptive names to every custom NSG rule (not just "Rule1", "Rule2").
  • Use the Effective security rules view as your first stop when investigating any new connectivity issue, it saves 20 minutes versus checking each NSG manually.

Frequently Asked Questions

Why is my Azure VM not reachable even though I added an allow rule in the NSG?

The most likely cause is a higher-priority deny rule overriding your allow. Remember that in Azure, lower priority numbers win, a deny at priority 100 beats an allow at priority 200. Also check that you've updated both the subnet-level NSG and the NIC-level NSG, because both must allow the traffic for inbound connections to succeed. Use the Effective security rules view on the VM's network interface to see the merged, evaluated rule list in one place.

What's the difference between a subnet NSG and a NIC NSG in Azure?

Both types of NSG contain the same kinds of rules, but they're attached at different points in the traffic path. A subnet NSG applies to all traffic entering or leaving an entire subnet, every VM in that subnet is affected. A NIC NSG applies only to the specific virtual machine whose network interface it's attached to. For inbound traffic, Azure evaluates the subnet NSG first, then the NIC NSG. For outbound traffic, it's the reverse. Both must allow traffic for it to flow, which means mismatches between the two are a common source of unexpected blocks.

Can I delete the default NSG rules like DenyAllInBound?

No, Azure's default NSG rules are immutable. You cannot delete or modify DenyAllInBound (priority 65500), AllowVnetInBound (priority 65000), AllowAzureLoadBalancerInBound (priority 65001), or the equivalent outbound defaults. The correct approach is to add your own custom allow rules at priority numbers below 65500. Since lower priority numbers take precedence, any rule you add at priority 100–65499 will be evaluated before the catch-all deny rule, allowing your specified traffic through.

RDP port 3389 is open in my NSG but I still can't connect, what am I missing?

A few things to check beyond the NSG. First, make sure the VM itself is running, a deallocated VM won't respond regardless of NSG rules. Second, verify the VM has a public IP assigned or that you're connecting through a valid path like VPN or Azure Bastion. Third, check that Windows Firewall inside the VM isn't blocking port 3389, NSGs operate at the network level, but the OS-level firewall is a separate layer. Finally, run IP Flow Verify in Network Watcher with your specific source IP and port 3389 to confirm the NSG is actually allowing the flow; it might be that your source IP isn't matching the rule's source filter.

How do I fix Azure virtual network connectivity between two peered VNets?

Start by confirming the peering status shows Connected on both sides, it must be Connected in both the source and destination VNet's peering blade. Then use Network Watcher's Next Hop tool from a VM in one VNet, targeting an IP in the other, to verify a valid route exists. If you're using a hub-and-spoke topology and traffic needs to route through a gateway, make sure Allow gateway transit is enabled on the hub side and Use remote gateways is enabled on the spoke side. Also check that NSGs on both subnets have rules permitting traffic from the other VNet's address space, peering establishes a route, but NSG rules still apply independently.

What does it mean when Network Watcher IP Flow Verify says "Allow" but I still can't reach my VM?

IP Flow Verify only checks NSG rules, it doesn't evaluate routing, DNS, or application-layer behavior. If it returns Allow but connectivity fails, the issue has moved to a different layer. Check routing with the Next Hop tool to make sure traffic isn't being misdirected to a dead-end NVA or VPN gateway. Check DNS resolution from inside the VM using nslookup or Resolve-DnsName in PowerShell to rule out name resolution failures being misread as connectivity failures. Also verify the application or service on the destination VM is actually running and listening on the expected port using netstat -ano | findstr :3389 for RDP or the equivalent port.

Related Microsoft Fix Guides

H
Sai Kiran Pandrala
Our team includes certified Microsoft engineers, Azure architects, and system administrators with 10+ years of enterprise IT experience. Every guide is written from hands-on troubleshooting, not guesswork. We test every fix before publishing.