Master Azure NSG rules, priorities, and default inbound/outbound traffic controls to secure Azure networks and prevent misconfigurations.
Azure Network Security Group Rules Explained:
Inbound, Outbound, Priority, and Default Rules
Every new Azure subnet ships with an NSG posture most engineers get backwards on their first guess: inbound traffic from the internet is blocked, but VNet-internal traffic and outbound internet access are both allowed, out of the box, by design. Getting the default rules wrong in your head is how "secure by default" assumptions turn into actual exposure — the fix is understanding exactly what the three inbound and three outbound default rules do, and why.
A Network Security Group looks simple — a list of allow and deny rules — right up until a rule that looks correct doesn't behave the way its priority number, its direction, or its source scope implied it would. The mechanics underneath that simple list are precise and worth knowing exactly, not approximately: every rule is evaluated against a five-part identifier called the 5-tuple, processed in strict priority order with the first match winning and nothing evaluated after it, and layered on top of a default rule set that most engineers describe accurately in conversation and then get subtly wrong the first time they actually reason about a specific packet. This guide works through the mechanics precisely enough that "why did this traffic get through" or "why did this traffic get blocked" stops being a mystery and starts being a five-minute trace through a known, deterministic evaluation order.
A Network Security Group is a stateful, Layer 4 packet filter — a list of allow/deny rules Azure evaluates against every packet's five-part identity, attachable directly to a subnet or a network interface with no additional infrastructure required.
- The 5-tuple. Every rule and every packet are compared on exactly five properties: source IP, source port, destination IP, destination port, and protocol. A rule matches a packet only when all five criteria align.
- Stateful. If a connection is allowed in one direction, the return traffic is automatically permitted, regardless of what the rules for the opposite direction say. You don't need a matching outbound rule for the response to an allowed inbound request, or vice versa.
- Layer 4, not Layer 7. NSGs filter on IP, port, and protocol — they have no awareness of application-layer content (URLs, HTTP headers, payload inspection). That's Azure Firewall's or a WAF's job, layered on top.
- Does nothing until attached. Creating an NSG has no effect on any traffic until it's associated with a subnet, a network interface, or both.
Because NSGs are stateful, you don't need to write a matching outbound Allow rule just so an inbound-permitted connection's responses can leave the VM — the platform tracks the connection and permits the return traffic automatically. This is a real simplification worth appreciating explicitly: a purely stateless firewall would require symmetric inbound and outbound rules for every legitimate conversation, roughly doubling the rule-writing burden for no security benefit.
Every NSG maintains two entirely separate rule lists — inbound and outbound — each with its own priority ordering and its own default rules. A packet's direction determines which list applies; the two lists never interact directly (aside from the stateful return-traffic behavior covered above).
| Direction | Governs | Common use |
|---|---|---|
| Inbound | Traffic arriving at the resource — from the internet, from other subnets, from on-premises | Restricting what can initiate a connection TO your VM (RDP, SSH, HTTPS from specific sources) |
| Outbound | Traffic leaving the resource — to the internet, to other subnets, to on-premises | Restricting where your VM can initiate connections TO (data exfiltration prevention, egress control) |
Teams reliably spend far more attention hardening inbound rules than outbound ones, which is backwards from a risk perspective in one specific way: a compromised VM's ability to exfiltrate data or communicate with a command-and-control server is governed by outbound rules, not inbound ones. The default AllowInternetOutBound rule (Section 4) means, out of the box, any VM can reach any internet destination — a real, permissive default that production environments handling sensitive data should deliberately override, not leave unexamined because "we already locked down inbound."
Priority is a number between 100 and 4096 for any custom rule, and it's the single mechanism that resolves conflicts when multiple rules could apply to the same traffic. Lower numbers mean higher priority — checked first — and the first rule whose 5-tuple matches wins outright, with no further rules evaluated afterward.
| Property | Detail |
|---|---|
| Custom rule range | 100 to 4096 |
| Default rule range | 65000 to 65500 — reserved, not usable for custom rules |
| Same priority, same direction | Not allowed — Azure rejects creating two rules with an identical priority and direction |
| Evaluation order | Lowest number first; first 5-tuple match wins; evaluation stops immediately |
| No match found | Falls through to the default rules (Section 4) |
A common, low-effort best practice: space custom rule priorities out (100, 200, 300, rather than 100, 101, 102) specifically so a future rule can be inserted between two existing ones without renumbering anything already in place. Renumbering existing rules to make room is a real, avoidable source of accidental precedence changes — a rule that used to be evaluated before another can silently swap positions if priorities have to be shuffled to fit a new one in.
Every NSG ships with six default rules — three inbound, three outbound — that cannot be deleted, only overridden by a custom rule at a lower (higher-precedence) priority number. Knowing them precisely matters, because the common mental shortcut ("NSGs deny by default") is only half true.
| Rule | Priority | Direction | Effect |
|---|---|---|---|
| AllowVNetInBound | 65000 | Inbound | Allows ALL traffic originating from within the virtual network (including peered VNets) |
| AllowAzureLoadBalancerInBound | 65001 | Inbound | Allows Azure Load Balancer health probe traffic |
| DenyAllInBound | 65500 | Inbound | Denies everything else inbound — this is the only genuinely "deny by default" piece |
| AllowVNetOutBound | 65000 | Outbound | Allows ALL outbound traffic to the virtual network |
| AllowInternetOutBound | 65001 | Outbound | Allows ALL outbound traffic to the internet |
| DenyAllOutBound | 65500 | Outbound | Denies everything else outbound |
Stated precisely: a brand-new NSG with only default rules denies inbound traffic originating from outside the virtual network, but allows all inbound traffic from within the VNet, allows all outbound traffic to the VNet, AND allows all outbound traffic to the internet. Two of those three "allow" defaults surprise people who assumed NSGs behave like a strict, zero-trust-by-default firewall. In particular, AllowInternetOutBound means every VM can reach the internet freely the moment it's created, with no custom rule required — fine for many workloads, a real gap to close deliberately for anything handling sensitive data.
Adding a custom DenyAllInbound-style rule at priority 65400 (still lower than the default DenyAllInBound's 65500, but higher than AllowVNetInBound's 65000) accomplishes nothing new for VNet-internal traffic — AllowVNetInBound at 65000 already matches and wins first. To actually restrict VNet-internal traffic, the deny rule needs a priority number lower than 65000, in the normal custom range (100-4096), so it's evaluated before the default Allow fires.
An NSG can be attached to a subnet, a network interface, or both simultaneously. When both are in play, traffic must pass both independently — Microsoft's own current design guidance states this precisely: the most restrictive combination wins.
| Traffic direction | Evaluation order |
|---|---|
| Inbound | Subnet NSG evaluated first, then NIC NSG — both must allow |
| Outbound | NIC NSG evaluated first, then subnet NSG — both must allow |
Attaching different NSGs to both a subnet and a NIC creates what's accurately described as a double-filter — technically more granular, but in practice, this layering is a frequent, real source of "why is my correctly-configured rule not working" confusion. If the subnet NSG denies traffic on a given port, it doesn't matter how permissive the NIC NSG is — the subnet layer blocks it first, and vice versa for outbound. Section 7 walks a complete, realistic example of exactly this scenario.
Current Azure networking best-practice guidance recommends applying NSGs at the subnet level by default, reserving NIC-level NSGs specifically for cases where individual VMs within the same subnet genuinely need different security policies from their neighbors. Defaulting to NIC-level NSGs everywhere multiplies the number of rule sets to keep consistent and dramatically increases the surface area for exactly the kind of dual-NSG confusion this section describes.
Two features exist specifically to keep NSG rules maintainable as an environment grows, replacing hardcoded IP addresses with names that track Azure's own infrastructure or your application's actual role structure automatically.
| Feature | What it replaces | Example |
|---|---|---|
| Service tags | Hardcoded IP ranges for Azure services or constructs | VirtualNetwork, AzureLoadBalancer, Internet, Storage, Sql — Microsoft maintains these automatically as underlying IPs change |
| Application Security Groups (ASGs) | Hardcoded IP addresses for your own VMs, grouped by role | Group web-tier VMs into AsgWeb, reference AsgWeb in a rule instead of each VM's individual IP |
An ASG-based rule is functionally identical to writing the same rule against every current member's individual IP address — the difference is entirely about maintainability. As VMs are added to or removed from an ASG, every rule referencing that ASG updates its effective scope automatically, without editing the rule itself. One real constraint worth knowing: every network interface in an ASG must exist in the same virtual network as the first network interface assigned to that ASG — you can't mix VMs from different VNets into a single ASG.
Putting the whole model together, precisely: an inbound SSH connection attempt from a specific management IP address, arriving at a VM whose subnet has one NSG and whose NIC has a different NSG.
| Step | What Azure checks | Result |
|---|---|---|
| 1 | Direction: inbound. For inbound traffic, the SUBNET NSG is evaluated first. | Proceed to subnet NSG's rule list |
| 2 | Subnet NSG custom rules, checked lowest priority first, against the packet's 5-tuple | Rule at priority 300: "Deny TCP port 22 from Internet" — matches (source is outside the VNet) |
| 3 | First match found — evaluation stops immediately | Traffic DENIED at the subnet layer. The NIC NSG is never consulted. |
Even though the NIC-level NSG on this same VM has a rule explicitly allowing SSH from this exact management IP range, that rule is irrelevant to the outcome — the connection never reaches NIC-level evaluation at all, because inbound traffic checks the subnet layer first and a match there ends the process. The fix has to be applied at the subnet NSG specifically: either add an explicit Allow rule for the management IP range at a lower priority number than the existing Deny, or remove/adjust the subnet-level Deny rule if it's no longer intended to apply this broadly.
A team hitting this exact scenario often reaches first for the NIC NSG, since that's frequently the more actively-managed, per-VM rule set — and changing it accomplishes nothing, because the subnet layer was never reached. Azure's Effective Security Rules view for a NIC, or Network Watcher's IP Flow Verify tool, both surface exactly which specific rule (and by extension, which layer) is responsible for a given Allow or Deny outcome — use one of these to confirm the actual blocking layer before editing any rule, rather than guessing which NSG to change first.
A genuinely current fact worth flagging explicitly for anyone building monitoring or auditing around NSG rule behavior: NSG flow logs are being retired.
| Milestone | Effect |
|---|---|
| After June 30, 2025 | New NSG flow logs can no longer be created |
| September 30, 2027 | Full retirement — traffic analytics for NSG flow logs stops working, existing NSG flow log resources are deleted |
| Recorded log data | Existing flow log records already written to Azure Storage are NOT deleted, and continue to follow their configured retention policy |
Microsoft's own guidance recommends migrating to virtual network flow logs, positioned as addressing NSG flow logs' limitations. Any team relying on NSG flow logs for security monitoring, traffic analytics, or compliance auditing should plan this migration deliberately rather than discovering the retirement mid-incident when logs unexpectedly stop.
Because new NSG flow logs can no longer be created at all (as of mid-2025), any new NSG deployed today needs virtual network flow logs from the start if flow-level visibility is required — there's no option to defer this decision to closer to the 2027 retirement date for a fresh deployment. For existing NSG flow logs still running, treat the 2027 date as a hard deadline for migration, not a soft target, since traffic analytics support stops working at that point and the flow log resources themselves are deleted.
Decide subnet-level or NIC-level attachment — default to subnet
Apply the NSG at the subnet level unless a specific VM within that subnet genuinely needs a different policy from its neighbors. This avoids the dual-NSG complexity covered in Section 5 and 7 unless there's a real reason to accept it.
Create Application Security Groups for each distinct role in your architecture
Group VMs by function (web tier, app tier, database tier) before writing any rules, so rules can reference roles rather than individual IPs from the start.
Write inbound Allow rules for exactly the traffic each tier needs, using service tags and ASGs
Scope source and destination as narrowly as the actual requirement allows — a specific ASG or service tag rather than * wherever possible. Space priority numbers out (100, 200, 300) to leave room for future insertions.
Explicitly review and override the permissive outbound defaults if the workload requires it
Decide deliberately whether AllowInternetOutBound's default permissiveness is acceptable for this workload. For anything handling sensitive data, add an explicit Deny-Internet-Outbound rule at a custom priority, then allow only the specific destinations actually required.
Verify the final rule set with Effective Security Rules, not just the rules you wrote
Especially if both subnet and NIC NSGs are in play, check the combined effective rule set for the actual NIC in question — this is the true, enforced outcome, not any single NSG's rule list viewed in isolation.
Test with IP Flow Verify from the actual source IPs real traffic will use
Confirm both the intended Allow and the intended Deny behaviors explicitly, from representative source addresses — don't assume correctness from reading the rule list alone.
Configure virtual network flow logs, not NSG flow logs, for ongoing visibility
Given NSG flow logs can no longer be created, set up virtual network flow logs from the start for any new environment needing traffic-level audit visibility.
| Anti-pattern | Why it feels right | Why it isn't |
|---|---|---|
| Assuming NSGs deny everything by default | "Firewalls are deny-by-default" | VNet-internal traffic and outbound internet access are both allowed by default. Only inbound-from-outside-the-VNet is denied out of the box |
| Adding a "Deny All" rule at a priority number above 65000 | "Lower priority number should be more restrictive... wait, higher?" | Any priority at or above 65000 is either invalid or evaluated after the defaults that already allow the traffic — accomplishes nothing for VNet-internal or internet-outbound traffic |
| Attaching different NSGs to subnet and NIC without a specific reason | "More layers of security" | Creates a documented, common source of troubleshooting confusion — traffic must pass both independently, and the more restrictive one silently wins regardless of the other's intent |
| Editing the NIC NSG when a subnet-level rule is actually the block | "NIC rules are usually the more specific, actively-managed ones" | If the subnet layer denies the traffic first (for inbound), the NIC layer is never even evaluated — confirm which layer is responsible before editing either |
| Packing custom rule priorities tightly (100, 101, 102...) | "Simple, sequential numbering" | Leaves no room to insert a new rule between two existing ones without renumbering, risking accidental precedence changes to rules that already work correctly |
| Continuing to rely on NSG flow logs for new deployments | "They've always worked fine" | New NSG flow logs can no longer be created as of mid-2025, and the feature fully retires September 30, 2027 — migrate to virtual network flow logs now, not near the deadline |
Key Takeaways
Frequently Asked Questions
Related FAVRITE Articles
- How to Troubleshoot Azure VM RDP Connection Issues: NSG, Firewall, Public IP, and Bastion Fixes
- Enterprise Network Segmentation in Azure: Architecture Patterns That Actually Scale
- Hub-and-Spoke vs Azure Virtual WAN: Which Network Architecture Wins?
- Azure Front Door Wildcard Revalidation Fix