Azure NSG Priority: When Allow Outranks Deny
A broad low-priority Allow rule matches traffic before a narrower high-priority Deny rule is ever evaluated, because Azure NSGs stop at the first match per 5-tuple. An incident-response Deny appended at priority 400 sits above a pre-existing Allow at priority 100, leaving the block structurally unreachable while flow logs show permitted traffic, letting lateral movement continue undetected.
At a glance
- Unsafe setting
- Deny rule assigned a numerically higher priority than an overlapping pre-existing broad Allow rule
- Failure trigger
- Incident-response Deny rule appended at priority 400 while existing Allow rule at priority 100 already matches traffic
- Blast radius
- Compromised host within the broad 10.0.0.0/8 range reaches the subnet the Deny rule was meant to block, bypassing firewall inspection.
- Recommended control
- Reserve non-overlapping priority bands for Deny versus Allow rules; validate effective rule sets via CLI and enforce with Azure Policy and CI checks.
Fix commands and configuration
az network nsg rule list --nsg-name <nsg> --resource-group <rg> --query "sort_by([].{name:name, priority:priority, access:access, prefix:sourceAddressPrefix}, &priority)"az network nic list-effective-nsg --name <nic> --resource-group <rg>deployIfNotExistsThe Trap
NSG rule priority collision: a broad Allow rule sitting at a low priority number is evaluated before a narrower Deny rule sitting at a higher priority number, so Azure never reaches the Deny at all.
The Default State
Terraform and Bicep modules that generate NSG rules from a list typically assign priority using an incrementing counter such as priority = 100 + (index * 10), seeded from the order rules appear in the source file. Security exception rules — the ones blocking a compromised subnet, a leaked credential’s source IP, or a decommissioned peering range — get appended to the end of that list during an incident response change, landing at priority 400 or 500. The pre-existing “Allow-Corp-VPN-Inbound” rule, written months earlier with source prefix 10.0.0.0/8 to cover a VPN gateway pool, sits at priority 100. Azure evaluates NSG rules in ascending priority order and stops at the first match per 5-tuple; it never inspects the Deny rule at 400 because the Allow at 100 already matched.
The Blast Radius
A host compromised inside a peered VNet with an RFC1918 address inside 10.0.0.0/8 — which covers far more than the intended VPN pool — reaches a subnet the incident Deny rule was supposed to lock down. NSG flow logs show action “A” (Allow) against the exact source and destination pair the security team believed was blocked. Because the packet is permitted at the NSG layer, it never reaches an Azure Firewall or NVA route for inspection, since NSGs on the subnet or NIC evaluate before UDRs redirect traffic. Post-incident review finds the Deny rule “worked” in the portal — it exists, it is enabled — but its priority number (400) is numerically higher than the Allow rule’s (100), so it was structurally unreachable from day one. Lateral movement continues until someone manually diffs the effective rule set.
The Lead Mechanic Fix
Reserve non-overlapping priority bands and enforce them programmatically: Deny rules occupy 100–999, generic Allow rules occupy 1000–4096, and no automation may insert outside its assigned band. Validate before every deploy with az network nsg rule list --nsg-name <nsg> --resource-group <rg> --query "sort_by([].{name:name, priority:priority, access:access, prefix:sourceAddressPrefix}, &priority)", and diff against az network nic list-effective-nsg --name <nic> --resource-group <rg> to confirm the effective, merged rule set — which includes default rules 65000–65500 — matches intent. Add an Azure Policy definition with deployIfNotExists on Microsoft.Network/networkSecurityGroups/securityRules that flags any Allow rule whose address prefix overlaps a lower-numbered Deny rule’s prefix, and fail CI on Terraform plan if the priority attribute for any resource with access = "Deny" is ever greater than an overlapping Allow rule’s priority.