Azure Troubleshooting

Why Is My Traffic Blocked? Debugging NSGs with Effective Rules and IP Flow Verify

You deployed a virtual machine, opened the right port, and tried to connect — and nothing happened. The connection just hangs until it times out, or it comes straight back as “connection refused.” Nothing changed. Except, in Azure networking, something almost always changed somewhere you weren’t looking. The most common reason a packet never reaches your VM is a Network Security Group (NSG) — the stateful, priority-ordered allow/deny list Azure evaluates on the way into and out of a network interface or subnet. When an NSG drops a packet it sends nothing back; you just get silence, and silence is the hardest thing to debug.

The good news: you almost never have to guess which rule blocked you. Azure gives you two tools that turn the guesswork into a lookup. Effective Security Rules flattens every NSG that applies to a network interface into the single ordered list Azure actually enforces. IP Flow Verify (a Network Watcher feature) takes a specific 5-tuple — source IP, destination IP, port, protocol, direction — and tells you, in one line, Allow or Deny and the exact name of the rule that decided it. Used together they answer “why is my traffic blocked?” in under two minutes — no packet captures, no trial-and-error rule edits.

By the end you will stop poking at rules hoping one sticks. When a connection fails you will localise it — is it really the NSG, or routing, DNS, a closed listener, or a firewall further along? — and when it is the NSG, you will name the offending rule and understand why it won. Keep the symptom-to-fix playbook open the next time a port refuses to answer.

What problem this solves

An NSG block is uniquely frustrating because it is invisible by default. A firewall appliance logs a drop; a web server returns a 403; a database refuses a login with a message. An NSG, when it denies a packet, sends nothing back — the client waits for a SYN-ACK that never comes and reports a timeout. Beginners read “timeout” as “the server is down,” restart the VM, or open a ticket, when the real cause is one deny rule at priority 200 quietly doing its job.

What breaks without this skill: an engineer adds an allow at a higher priority number than an existing deny (deny still wins); opens the port on the NIC’s NSG while a subnet NSG above it still blocks; or spends an hour on the NSG when the packet never reached it — a User-Defined Route sent it elsewhere, or the app was never listening. Every one is diagnosable in minutes with the two tools — if you know they exist. This bites first-timers on RDP/SSH, teams exposing a web app on 443, anyone wiring a VM to Azure SQL or Storage, and engineers juggling a subnet and a NIC NSG. The fix is rarely “open everything” — it’s “find the rule Azure actually applied, and understand why.”

The shape of every case is the same: a timeout points at a silent NSG drop or routing black-hole (reach for IP Flow Verify), a refusal points at a dead listener (reach for the VM), and “worked yesterday” points at a changed rule (reach for Effective Security Rules and the Activity Log). The full symptom→tool decision table is in Telling an NSG block from everything else below.

Learning objectives

By the end of this article you can:

Prerequisites & where this fits

You should know the basics of an Azure Virtual Network (VNet): it holds subnets, each with a CIDR range, and resources like VMs get a network interface (NIC) with a private IP. Helpful but not required: running az in Cloud Shell, reading JSON, and a rough sense of TCP (SYN/SYN-ACK), ports, and inbound vs outbound. New to subnets and private IPs? Start with Azure Virtual Network, Subnets and NSGs: Networking Fundamentals.

This sits in the Networking → Troubleshooting track. It assumes the rules model from Azure NSG Rules Explained: Priorities, Default Rules and Augmented Security Rules for Beginners — that article teaches how to write NSG rules; this one how to debug them. It pairs with Diagnosing Azure VNet Connectivity: NSGs, UDRs, Effective Routes & Network Watcher, the broader playbook covering routes too; when the cause turns out to be routing, User-Defined Routes and Route Tables: Steering Azure Traffic the Way You Intend is where you go next.

A quick map of who owns what, so you escalate to the right layer instead of staring at the wrong tool:

Layer What lives here Tool that confirms it Failure it causes
NSG (subnet + NIC) Allow/deny rules, priorities, service tags Effective Security Rules, IP Flow Verify Silent drop → timeout
Routing (UDR / system routes) Where the packet is sent next Effective Routes, Next Hop Reply lost, traffic black-holed
The app inside the VM The process listening on the port ss -tlnp / netstat on the VM Connection refused
Downstream / PaaS firewall Azure Firewall, Storage/SQL firewall, DNS The firewall’s own blade; nslookup for DNS Block after the NSG passes it

Core concepts

Five ideas make every later diagnosis obvious.

An NSG is an ordered allow/deny list, lowest priority number first. Each rule has a priority from 100 to 4096; Azure evaluates in ascending order per direction and stops at the first match. Lower number wins, so a deny at 200 beats an allow at 300 — which is the most common beginner mistake: an allow with a bigger number than an existing deny, baffled that nothing changed.

There is always an implicit “deny everything” at the bottom. Every NSG has hidden, non-deletable default rules (65000–65500): they allow traffic within the VNet (AllowVnetInBound) and from the load balancer (AllowAzureLoadBalancerInBound), then DenyAllInBound/DenyAllOutBound at 65500 catch the rest. So “I didn’t write a deny but it’s still blocked” is normal — absence of an allow is a deny.

NSGs are stateful — allow the request, the reply is automatic. You do not add an outbound rule for return traffic: an allowed inbound flow’s reply is permitted automatically (and vice versa). A lost reply is therefore almost never the NSG — it’s usually asymmetric routing (a UDR sending the reply out a different path) masquerading as a security problem.

An NSG can attach to a subnet, a NIC, or both — and both apply. Inbound is filtered subnet-first then NIC, outbound the reverse, and a packet must be allowed by both. So “I opened the port on the NIC” can still fail because the subnet NSG above it denies — and Effective Security Rules is the only view that shows the combined result.

The status you don’t get is the clue. A timeout almost always means a packet was silently dropped — NSG deny, no route, or a black-hole. A connection refused (instant) means the packet arrived but nothing was listening — an application problem, because an NSG never sends a reset. Timeout → suspect the NSG/route; refused → suspect the listener.

How NSG evaluation actually works

Most “wrong rule won” bugs are really “I misunderstood the order.” Azure evaluates per direction, in ascending priority, and stops at the first match (protocol, source, destination, and port all fall inside the rule). That match’s action is final. When a subnet NSG and a NIC NSG both exist the packet must clear both, and the order flips by direction:

Direction NSG order Drops the packet when…
Inbound Subnet NSG → NIC NSG Either NSG’s first match is a Deny, or only the 65500 implicit deny matches
Outbound (incl. replies) NIC NSG → Subnet NSG A too-strict outbound deny blocks new outbound (updates, API calls); allowed inbound’s reply is auto-permitted
After NSGs Routing takes over A bad route/UDR black-holes it — now a route question, not an NSG one

Reading the Effective Security Rules

A single NSG blade shows only its rules — not the combined effect of a subnet NSG plus a NIC NSG plus the hidden defaults. Effective Security Rules flattens all of that into the single ordered list Azure enforces on a network interface: the ground truth, and the first thing to read when traffic is blocked.

Portal path: the NIC → HelpEffective security rules (also from the VM’s Networking blade or Network Watcher). It lists every rule, both directions, in priority order, tagged with which NSG it came from, defaults included. With the CLI, pull them as JSON and filter:

# Effective security rules for a specific NIC (the ground-truth combined list)
az network nic list-effective-nsg \
  --name nic-web-01 --resource-group rg-net-prod -o json \
  --query "value[].{nsg:name, rules:effectiveSecurityRules[].{name:name, dir:direction, prio:priority, access:access, port:destinationPortRange, src:sourceAddressPrefix}}"

The method is mechanical: scanning from the lowest priority number down, find the first rule whose direction, source, destination, and port all match — its Access is the verdict. A Deny (or nothing matching until the 65500 implicit deny) means blocked. Watch the traps: an allow sitting below a broad deny never fires; a rule on the wrong direction does nothing; an allow scoped to one CIDR won’t help a different source; 443 allowed won’t help an app on 8443. The source NSG column tells you where to edit — subnet or NIC.

IP Flow Verify: the one-line answer

Effective Security Rules shows the whole list; IP Flow Verify does the matching for you. Give it a precise packet — local IP, remote IP, port, protocol, direction — and it returns Allow/Deny plus the rule name that decided. It’s a Network Watcher feature (enabled per region, usually automatic). Point it at a target VM/NIC and describe the flow as Azure sees it:

# Is inbound HTTPS from a public client allowed to this VM's private IP?
az network watcher test-ip-flow \
  --vm vm-web-01 --nic nic-web-01 \
  --resource-group rg-net-prod \
  --direction Inbound --protocol TCP \
  --local 10.20.1.4:443 \
  --remote 203.0.113.10:51514 -o json
# → { "access": "Allow" | "Deny", "ruleName": "<NSG>/<ruleName or DefaultRule_DenyAllInBound>" }

The two fields that matter, and how to read them:

Field Value What it means
access Allow The NSG would let the packet through — the block is elsewhere (route, listener, downstream firewall)
access Deny The NSG is blocking it — read ruleName to know which
ruleName nsg-web/Allow-HTTPS (a real rule) Edit this rule or its priority
ruleName DefaultRule_DenyAllInBound No allow matched — the implicit deny caught it; add an allow

Portal path: Network Watcher → IP flow verify → pick the VM/NIC, set direction, protocol, local and remote IP:port → Check. Use this first in almost every incident — in ten seconds it tells you whether you’re even in the right layer. If it says Allow, stop blaming the NSG and look at routing, the listener, or a downstream firewall. That single fact saves the most time of anything here.

Use the IP/port as Azure sees the packet: --local is the VM’s own private IP (not its public IP), --remote is the other end’s IP (not a hostname), --direction is from the VM’s perspective (test Outbound for the VM’s own calls), and --nic disambiguates a multi-NIC VM.

Telling an NSG block from everything else

The biggest beginner time-sink is debugging the NSG when it was never the problem. Before editing a rule, localise the failure — the symptom plus one tool usually settles it:

If you see… It’s probably… Confirm with Not the NSG if…
Timeout, and IP Flow Verify says Deny An NSG rule (or implicit deny) az network watcher test-ip-flowruleName — (it is the NSG)
Timeout, but IP Flow Verify says Allow Routing, downstream firewall, or dead listener Effective Routes / Next Hop; the app’s port NSG already passed it
Connection refused (instant) Nothing listening on the port ss -tlnp / netstat -an on the VM NSG never sends a reset
“Host not found” / wrong IP DNS, not connectivity at all nslookup <name> on the client You haven’t reached the network yet
Reply never returns though inbound allowed Asymmetric routing (a UDR) Effective Routes; check next hop of the return path NSGs are stateful — not the rule
Works from inside the VNet, not from internet Missing Internet-sourced allow, or no public IP/LB IP Flow Verify with the public source IP Internal works because AllowVnetInBound

The whole method is one ordering: verify, then route, then listener, then downstream. Run IP Flow Verify first — Deny puts you in the NSG (read ruleName); Allow clears it, so move to Effective Routes, the listener, and any downstream firewall instead of editing rules that were never the cause.

Architecture at a glance

The diagram traces a single inbound HTTPS request from a public client to the app inside the VM, and pins each common failure to the hop where it bites. Read it left to right: a client sends TCP 443 into the VNet, where the subnet NSG is evaluated first (inbound order) then the NIC NSG — both must allow it. Only then does it reach the VM, where the app must be listening on 443; if it isn’t, you get connection refused, not an NSG drop. The reply is automatic (NSGs are stateful) but leaves via routing, where a stray User-Defined Route can black-hole the return path.

Underneath sits the diagnostic band — IP Flow Verify against the NSG hops, Effective Security Rules for the combined list, Effective Routes for the routing question when IP Flow Verify says Allow — and the numbered badges mark the four places this most often breaks, with the legend narrating each as symptom · confirm · fix.

Left-to-right Azure NSG packet path: a public client sends TCP 443 into a Virtual Network where the subnet NSG is evaluated first then the NIC NSG, both of which must allow the flow before it reaches the VM whose application must be listening on 443, with the stateful reply leaving via routing where a User-Defined Route can black-hole the return path; a diagnostics band shows IP Flow Verify returning Allow or Deny plus the deciding rule name against the NSG hops, Effective Security Rules reading the combined subnet-plus-NIC list, and Effective Routes answering the routing question, with numbered badges on the subnet NSG deny, the NIC NSG deny, the dead listener on the VM, and the black-holing route

Real-world scenario

Saffron Logistics, a mid-size freight company, runs a customer tracking portal on a Linux VM in Central India: a web app on port 443 behind a Standard Load Balancer, NIC in snet-web. One Tuesday a release went out at 14:00 and by 14:20 the portal was unreachable from the public internet — every request timed out — yet from a jump box in the same VNet it loaded fine. The deploy had only updated the app binary; nothing touched the network.

The on-call engineer’s first instinct was to restart the VM (no change), then scale the Load Balancer (no change, needless cost). Twenty minutes in, with customers calling, they ran IP Flow Verify for the exact failing flow — inbound TCP to 10.30.1.5:443, remote the public client range. In seconds: Deny, ruleName: nsg-web-subnet/Deny-Internet-Inbound, a rule at priority 150. Weeks earlier someone had added a temporary “lock it down during maintenance” deny on the subnet NSG and never removed it; the existing Allow-HTTPS rule sat at 300, behind it, and a scheduled automation had just re-applied the deny. Because it lived on the subnet NSG, nobody scanning the VM’s NIC rules had seen it. Effective Security Rules confirmed why: the deny at 150 sat above the allow at 300, so it matched first and stopped — public requests died while VNet-internal traffic (AllowVnetInBound) sailed through.

The fix was a one-liner: delete Deny-Internet-Inbound, re-run IP Flow Verify — now Allow — and the portal recovered within a minute. The follow-ups mattered more: they removed the automation that re-applied the deny and added an Activity Log alert on NSG rule changes. Downtime was ~35 minutes; with IP Flow Verify reached first, it would have been five.

The incident as a timeline, because the order of moves is the lesson:

Time Action taken Effect What it should have been
14:20 Restart the VM No change Run IP Flow Verify first
14:28 Scale the Load Balancer No change, extra cost Don’t scale to mask
14:40 Run IP Flow Verify (Inbound 443) Deny + rule name in seconds The breakthrough
14:46 Remove stale Deny-Internet-Inbound Re-verify → Allow; portal recovers Correct fix
+1 day Activity Log alert on rule changes Won’t silently recur The durable fix

Advantages and disadvantages

The same properties that make NSGs simple make them confusing to debug. Weigh it honestly:

Advantages (why NSGs are good) Disadvantages (why they bite)
Free and built in — no appliance to deploy or pay for A denied packet is silent — you get a timeout, not an error
Stateful — allow the request, the reply is automatic The statefulness fools firewall veterans into adding pointless reverse rules
Priority-ordered and deterministic — the same packet always gets the same verdict The order is unforgiving — one wrong priority number and your allow never fires
Effective Security Rules + IP Flow Verify make root cause a lookup, not a guess Beginners don’t know these tools exist and debug blind
Service tags (Internet, Storage, AzureLoadBalancer) avoid hard-coding Azure IPs Misusing a tag (e.g. forgetting AzureLoadBalancer) silently breaks health probes
Layer subnet + NIC for defence in depth; deny-by-default is safe Two NSGs = two places to check (people forget the subnet one), and “I wrote no deny” + still blocked confuses first-timers

NSGs are the right default for almost every VNet — they only become painful when you debug them without the tools. For L7 inspection, TLS, or centralised egress filtering you add Application Gateway v2 WAF or Azure Firewall alongside NSGs, not instead of them — a packet still clears the NSG first.

Hands-on lab

Reproduce an NSG block, see it in IP Flow Verify and Effective Security Rules, then fix it — free-tier-friendly (one B-series VM; delete at the end). Run in Cloud Shell (Bash).

Step 1 — Variables and resource group.

RG=rg-nsg-lab
LOC=centralindia
VM=vm-web-lab
az group create -n $RG -l $LOC -o table

Step 2 — Create a small Linux VM (this also creates a VNet, subnet, NIC and NSG).

az vm create -n $VM -g $RG --image Ubuntu2204 --size Standard_B1s \
  --admin-username azureuser --generate-ssh-keys --public-ip-sku Standard -o table
# Note the publicIpAddress and (we'll fetch) the private IP next.
PRIV_IP=$(az vm list-ip-addresses -n $VM -g $RG \
  --query "[0].virtualMachine.network.privateIpAddresses[0]" -o tsv)
echo "Private IP: $PRIV_IP"

Step 3 — Install a web server, then confirm the port is open by default-block. Open port 80 logically on the OS, but don’t add an NSG allow rule yet:

az vm run-command invoke -n $VM -g $RG --command-id RunShellScript \
  --scripts "sudo apt-get update -y && sudo apt-get install -y nginx && sudo systemctl enable --now nginx"

Browse to http://<publicIp> — it times out. Nginx is running and listening on 80, but no NSG rule allows inbound HTTP, so the implicit DenyAllInBound drops it. This is the silent block, reproduced.

Step 4 — Prove it’s the NSG with IP Flow Verify.

NIC=$(az vm show -n $VM -g $RG --query "networkProfile.networkInterfaces[0].id" -o tsv | xargs -I{} az network nic show --ids {} --query name -o tsv)
az network watcher test-ip-flow \
  --vm $VM --nic $NIC -g $RG \
  --direction Inbound --protocol TCP \
  --local ${PRIV_IP}:80 \
  --remote 203.0.113.5:40000 -o json
# Expect: "access": "Deny", "ruleName": ".../DefaultRule_DenyAllInBound"

The DefaultRule_DenyAllInBound in ruleName is the proof: no allow rule matched, so the implicit deny caught it.

Step 5 — Read the Effective Security Rules to see the same truth.

az network nic list-effective-nsg --name $NIC -g $RG \
  --query "value[].effectiveSecurityRules[?direction=='Inbound'].{name:name, prio:priority, access:access, port:destinationPortRange}" -o table
# You'll see the default Deny at 65500 and no Allow for port 80.

Step 6 — Fix it by adding an allow rule, then re-verify. Find the NSG (the VM created one), add an inbound allow for 80 at a sensible priority:

NSG=$(az network nic show --name $NIC -g $RG --query "networkSecurityGroup.id" -o tsv | xargs -I{} az network nsg show --ids {} --query name -o tsv)
az network nsg rule create -g $RG --nsg-name $NSG -n Allow-HTTP \
  --priority 300 --direction Inbound --access Allow \
  --protocol Tcp --destination-port-ranges 80 --source-address-prefixes Internet -o table

az network watcher test-ip-flow --vm $VM --nic $NIC -g $RG \
  --direction Inbound --protocol TCP --local ${PRIV_IP}:80 --remote 203.0.113.5:40000 -o json
# Now: "access": "Allow", "ruleName": ".../Allow-HTTP"

Reload http://<publicIp> — the nginx page renders. You watched the verdict flip from Deny to Allow.

Validation. You reproduced a silent NSG block, confirmed it was the implicit deny (DefaultRule_DenyAllInBound, not a rule you wrote) via IP Flow Verify, corroborated it in Effective Security Rules, and fixed it with one allow rule — re-verifying to prove it. No code involved: a silent timeout that was purely the rule set.

Cleanup (avoid lingering charges):

az group delete -n $RG --yes --no-wait

A Standard_B1s is a few rupees/hour; an hour of this lab is well under ₹50, and deleting the resource group stops everything.

Common mistakes & troubleshooting

This is the playbook — the part you bookmark and read mid-incident. It spans the basic traps (wrong priority, forgotten subnet NSG) and the ones that only look like NSG problems: routing (see User-Defined Routes and Route Tables), dead listeners, and downstream firewalls — including a PaaS service’s own, e.g. Fixing Azure Storage 403 Errors. Each row is symptom → root cause → confirm (exact command/portal path) → fix.

# Symptom Root cause Confirm (exact cmd / portal path) Fix
1 Added an allow rule, still blocked Allow has a higher priority number than an existing deny Effective security rules: the deny sits above the allow; or IP Flow Verify ruleName shows the deny Give the allow a lower number than the deny (e.g. 200), or remove the deny
2 Opened the port on the NIC, still times out A subnet NSG above the NIC NSG denies it az network nic list-effective-nsg shows a deny from the subnet NSG Add the allow to the subnet NSG too (both must allow)
3 “I wrote no deny rule” yet inbound is blocked Implicit DenyAllInBound (65500) — no allow matched IP Flow Verify → ruleName: DefaultRule_DenyAllInBound Add an explicit Allow rule for the port/source
4 Connection refused instantly (not a timeout) Nothing listening on the port — not the NSG ss -tlnp / netstat -an on the VM; NSG never sends RST Start/bind the app on the port (0.0.0.0, not 127.0.0.1)
5 IP Flow Verify says Allow but it still fails The block is downstream: routing, firewall, or PaaS firewall Effective Routes / Next hop; the downstream service’s own firewall Fix the route / Azure Firewall rule / Storage-SQL firewall — not the NSG
6 Inbound allowed, but the reply never returns Asymmetric routing (a UDR sends the reply elsewhere) Effective routes: a UDR overrides the system route for the return CIDR Fix/remove the UDR; route the reply symmetrically
7 Some source IPs work, others are blocked Allow rule scoped to specific source CIDRs Effective security rules: read the rule’s Source prefix Widen the source prefix or add the missing CIDR / service tag
8 App works on 443 but a second port (e.g. 8443) doesn’t Allow rule port range doesn’t include the new port Effective security rules: rule covers 443 only Add the port to the rule’s destination range
9 Load Balancer health probe fails; backend marked unhealthy Inbound from AzureLoadBalancer service tag not allowed IP Flow Verify with remote = LB probe source; check for the tag rule Add an inbound allow with source AzureLoadBalancer
10 Outbound to the internet (updates, APIs) suddenly fails An outbound deny (often a custom DenyAllOutBound or to Internet) Effective security rules (Outbound); IP Flow Verify --direction Outbound Add an outbound allow for the needed destination/port
11 “Host not found” or connecting to the wrong IP DNS, not connectivity — you never reached the network nslookup <name> / dig on the client returns wrong/no IP Fix DNS (record, private DNS zone, resolver) — NSG is irrelevant
12 RDP/SSH works from your office, fails from home Allow rule pinned to a single office IP/CIDR Effective security rules: management rule source = office CIDR only Add your current IP, or use Bastion instead of a public mgmt port
13 Private Endpoint traffic blocked despite an allow NSG on the PE subnet, or older PE/NSG interaction nuance IP Flow Verify on the PE NIC; check the PE subnet NSG Allow the consumer source on the PE subnet NSG; verify private DNS
14 Flow worked, broke after an automation/IaC run A pipeline re-applied a deny or changed priorities Activity log filtered to NSG operations; diff the rule set Fix the IaC source; add an Activity Log alert on rule changes

Two rows carry reasoning the table can’t fully spell out. Row 1 (allow still blocked) is pure priority order — the deny’s lower number matches first and stops evaluation; the same trap hides at the subnet layer, and a low-priority DenyAllInBound silently overrides the default AllowAzureLoadBalancerInBound, which is how you accidentally kill health probes (row 9). Row 5 (IP Flow Verify says Allow, still failing) is the key habit: the NSG passed, so stop editing rules — the block is downstream, and an allowed-but-lost reply is asymmetric routing, not a rule. For exposed management ports (row 12), front the VM with Azure Bastion rather than widening the CIDR (Cannot RDP or SSH into Your Azure VM?).

NSG-relevant codes and signals reference

NSGs don’t emit HTTP codes, but the signals you observe map cleanly to causes:

Signal you observe What it strongly implies Confirm with Note
TCP timeout (SYN, no SYN-ACK) Silent drop — NSG deny, no route, or black-hole IP Flow Verify (Deny?) → Effective routes The classic NSG fingerprint
Connection refused (RST) Packet arrived, no listener ss -tlnp on the VM An NSG never sends RST
DefaultRule_DenyAllInBound in ruleName No allow matched the inbound flow IP Flow Verify Add an explicit allow
DefaultRule_DenyAllOutBound in ruleName No allow matched the outbound flow IP Flow Verify --direction Outbound Add an outbound allow
Allow in IP Flow Verify, still failing Block is downstream (route/firewall/listener) Effective routes; downstream firewall blade Leave the NSG alone
Probe-down / unhealthy backend AzureLoadBalancer tag not allowed Effective security rules Restore the LB allow
Intermittent loss under load only Likely not the NSG (NSG is deterministic) Look at the app / SNAT / capacity NSGs allow or deny consistently

Best practices

Security notes

Cost & sizing

NSGs themselves are free — no per-rule, per-NSG, or per-evaluation charge — and the diagnostic tools (IP Flow Verify, Effective Security Rules, Next Hop, Effective Routes) run on demand without a bill. So “sizing” an NSG is about limits and hygiene, not money. The only NSG-adjacent cost is NSG flow logs — storage plus optional Traffic Analytics ingestion — typically a few hundred rupees a month, growing with connection volume, so scope them to the NSGs you actually audit:

Item Cost Limit / sizing note
NSGs, rules, and the diagnostic tools Free ~1000 rules per NSG (soft, raisable); IP Flow Verify / Effective Rules / Next Hop are free, on-demand
NSG flow logs (storage) Per-GB storage Scope to needed NSGs; set retention to control growth
Traffic Analytics Log Analytics ingestion + retention Optional; adds cost — enable only where you need the analysis

Keep the number of rules small and intentional (fewer rules are faster to debug — NSGs evaluate fine either way).

Interview & exam questions

1. A connection to a VM times out. How do you decide in seconds whether an NSG is to blame, and which rule? Run IP Flow Verify (Network Watcher) for the exact 5-tuple — direction, protocol, the VM’s private IP:port as local, the remote IP:port. It returns Allow/Deny plus the deciding rule’s name: Deny means the NSG is blocking (and which rule); Allow means it’s innocent and the problem is downstream.

2. Why doesn’t a single NSG blade tell the whole story, and what does? A single NSG shows only its own rules, not the combined effect of a subnet NSG, a NIC NSG, and the hidden defaults. Effective Security Rules flattens all of those into the single ordered list Azure enforces on a NIC — the ground truth for debugging.

3. You added an allow rule but traffic is still blocked. Most likely cause? The allow has a higher priority number than an existing deny. Azure evaluates in ascending priority and stops at the first match, so the lower-numbered deny wins. Give the allow a lower number, or remove the deny.

4. Difference between a connection that times out and one that’s refused, in NSG terms? A timeout means a packet was silently dropped — a classic NSG deny or a routing black-hole. A refusal (instant RST) means the packet arrived but nothing was listening — an app problem, because an NSG never sends a reset. Timeout → suspect NSG/route; refused → suspect the listener.

5. You wrote no deny rule, yet inbound is blocked. Why? Every NSG has an implicit DenyAllInBound at 65500. If no allow matches, it denies — absence of an allow is effectively a deny. Add an explicit allow.

6. A subnet NSG and a NIC NSG both exist. Which applies, and in what order? Both, and the packet must clear both. Inbound: subnet NSG first, then NIC NSG. Outbound: NIC NSG first, then subnet NSG. Effective Security Rules shows the combined result.

7. A Load Balancer marks your backend unhealthy after you tightened the NSG. What broke? You blocked the health probe from the AzureLoadBalancer service tag, so the instance was pulled from rotation. Add an inbound allow with source AzureLoadBalancer for the probe port; never blanket-deny inbound without preserving it.

8. IP Flow Verify returns Allow but the connection still fails. Where next? Not the NSG — it passed. Check routing (Next Hop / Effective Routes for a black-holing UDR or a route to a firewall), the listener on the VM, and any downstream firewall (Azure Firewall or a PaaS service’s own firewall).

9. What is a service tag and why prefer it over IP ranges? A service tag (Internet, Storage, Sql, AzureLoadBalancer) is a Microsoft-managed group of Azure IP ranges. Referencing the tag avoids hard-coding IPs that go stale and makes the rule’s intent readable.

10. How would you get an auditable record of what an NSG actually dropped over time? Enable NSG flow logs (written to a storage account), recording allowed and denied flows, optionally analysed with Traffic Analytics. IP Flow Verify is point-in-time; flow logs give you history.

These map to AZ-104 (Administrator) and AZ-700 (Network Engineer); the “is it the route or the rule?” reasoning is squarely AZ-700. A compact cert-mapping for revision:

Question theme Primary cert Objective area
NSG rules, priorities, defaults AZ-104 Configure & manage virtual networking
IP Flow Verify / Effective Rules / Next Hop AZ-104 / AZ-700 Network Watcher; troubleshoot connectivity
Service tags, NSG vs firewall AZ-700 Design & implement network security
Subnet vs NIC NSG, statefulness AZ-700 Implement NSGs and ASGs
Flow logs / Traffic Analytics AZ-700 Monitor & troubleshoot networks

Quick check

  1. A connection to your VM times out. What is the first tool you run, and what two pieces of information does it return?
  2. You added an allow rule for port 443 but traffic is still blocked. What’s the single most likely reason, given how NSGs evaluate rules?
  3. True or false: if an inbound flow is allowed, you must add a matching outbound rule for the reply to get back.
  4. A connection is refused instantly (not a timeout). Is the NSG the likely cause? Why or why not?
  5. IP Flow Verify says Allow, yet the connection still fails. Name two places you should look next.

Answers

  1. IP Flow Verify (Network Watcher). It returns the access decision (Allow or Deny) for the exact 5-tuple and the name of the rule that decided it (including DefaultRule_DenyAllInBound when no allow matched).
  2. Your allow rule has a higher priority number than an existing deny. Azure evaluates rules in ascending priority and stops at the first match, so the lower-numbered deny wins. Give the allow a lower number (or remove the deny).
  3. False. NSGs are stateful — the reply to an allowed inbound flow is permitted automatically. A lost reply is almost always asymmetric routing (a UDR), not a missing rule.
  4. No. An NSG silently drops (causing a timeout); it never sends a TCP reset. A refusal means the packet reached the VM but nothing was listening on the port — an application/listener problem. Confirm with ss -tlnp on the VM.
  5. Routing (Next Hop / Effective Routes — a black-holing UDR or a route to a firewall) and the listener on the VM (or a downstream firewall such as Azure Firewall or a PaaS service’s own firewall).

Glossary

Next steps

You can now localise any “blocked traffic” symptom to a hop, confirm it, and fix the one thing that’s wrong. Build outward:

AzureNSGNetworkingTroubleshootingIP Flow VerifyNetwork WatcherEffective Security RulesConnectivity
Need this built for real?

Vinod is a Senior Cloud Architect (22+ yrs) — available for Azure / AWS / GCP architecture, landing zones, and migrations.

Work with me

Comments

Keep Reading