Azure NSG Rule Analyzer

Read an Azure NSG in evaluation order: which rule decides each port, which rules are never reached because something earlier already matched, and what the six default rules at 65000 are letting through underneath.

Paste below, or drop a file anywhere on this panel

Or drop a file anywhere on this panel. Nothing is uploaded: the analysis runs in this tab.

The answer appears here

Paste on the left and press Check the rules. Nothing leaves this tab.

Wanted a different tool?

Examples

Real input you can load into the tool above. Each one shows a different thing going wrong, because that is what the tool is for.

A deny that is never reached

Evaluation stops at the first match, so a catch-all allow at 200 makes the deny at 300 dead. Both are listed in the portal and both look correct.

[{"name":"AllowEverything","priority":200,"direction":"Inbound","access":"Allow","protocol":"*","sourceAddressPrefix":"*","sourcePortRange":"*","destinationAddressPrefix":"*","destinationPortRange":"*"},
 {"name":"DenySQL","priority":300,"direction":"Inbound","access":"Deny","protocol":"Tcp","sourceAddressPrefix":"*","sourcePortRange":"*","destinationAddressPrefix":"*","destinationPortRange":"1433"}]

RDP open to the internet

A source of * is the whole internet, and 3389 is found by scanners within minutes of being exposed

[{"name":"AllowRDP","priority":110,"direction":"Inbound","access":"Allow","protocol":"Tcp","sourceAddressPrefix":"*","sourcePortRange":"*","destinationAddressPrefix":"*","destinationPortRange":"3389"}]

A priority outside the user range

Azure reserves everything below 100 and above 4096 for its own defaults, so this rule will not deploy

[{"name":"AllowHTTPS","priority":50,"direction":"Inbound","access":"Allow","protocol":"Tcp","sourceAddressPrefix":"Internet","sourcePortRange":"*","destinationAddressPrefix":"*","destinationPortRange":"443"}]

An NSG that looks closed

Nothing denies the virtual network, so AllowVnetInBound at 65000 still admits every other subnet and anything peered

[{"name":"AllowHTTPS","priority":100,"direction":"Inbound","access":"Allow","protocol":"Tcp","sourceAddressPrefix":"Internet","sourcePortRange":"*","destinationAddressPrefix":"*","destinationPortRange":"443"}]

Common mistakes

These are the ones that fail silently. The config is accepted, nothing raises an error, and the consequence arrives later.

  1. Adding a deny rule at a higher number to close something

    Evaluation stops at the first match and lower numbers go first, so a deny behind an allow is never reached. Both rules are listed in the portal and both look correct, which is why it survives review.

    Instead:Renumber the deny BELOW the allow, or narrow the allow so it stops matching. The order is the rule.

  2. Assuming an NSG with no rules is closed

    `AllowVnetInBound` at 65000 permits everything from the whole virtual network, including every other subnet, anything peered and anything with a VPN route in. It cannot be deleted and it is already in effect.

    Instead:Write an explicit deny for VirtualNetwork at a high number inside the user range, after the allows that need to keep working.

  3. Checking one NSG

    A subnet NSG and a NIC NSG both apply, and both must allow. Inbound is subnet then NIC, outbound is the reverse. An allow in one and nothing in the other is a deny, and each looks correct on its own.

    Instead:Read the effective security rules on the NIC, which is the flattened view of both. `az network nic list-effective-nsg` prints it.

  4. Leaving egress alone

    `AllowInternetOutBound` at 65001 lets anything in the subnet reach any address. That is the path a compromised host uses to fetch a payload and to send data out, and nothing restricts it by default.

    Instead:Deny outbound inside the user range and allow what is needed. Expect to allow the AzureMonitor and Storage service tags, or the platform's own agents stop working.

  5. Using `*` as a source and reading it as a restriction

    `*`, `0.0.0.0/0` and the `Internet` service tag are the same thing in an inbound rule, and only the last reads as though it names something.

    Instead:Use an address prefix, a service tag that names a real service, or an application security group, which lets a rule name a set of NICs rather than a set of addresses.

An NSG is a decision table, and position decides as much as contents

Azure evaluates rules in priority order and stops at the first match. Three consequences follow, and each one is the opposite of the instinct someone brings from AWS security groups.

First match wins, and the lower number goes first

So a deny at 200 does nothing when an allow at 100 already matched the traffic. AWS security groups have no ordering and no denies at all, so "add a deny rule" is the natural move and it produces a rule that is never reached. Both rules are listed in the portal and both look correct, which is why this survives review.

100  AllowAll   Allow  *    * -> *
200  DenySQL    Deny   Tcp  * -> 1433   never reached

The default rules are already in effect and cannot be deleted

Six of them, at 65000 and above. The one that matters is AllowVnetInBound at 65000, which permits everything from the whole virtual network. An NSG with no inbound rules is not closed to the VNet, it is open to it, including every other subnet, anything peered, and anything with a VPN or ExpressRoute route in. Closing that means writing a deny at a lower number, which is the opposite of what "add it at the end" suggests.

65000  AllowVnetInBound              Allow
65001  AllowAzureLoadBalancerInBound Allow
65500  DenyAllInBound                Deny

65000  AllowVnetOutBound             Allow
65001  AllowInternetOutBound         Allow
65500  DenyAllOutBound               Deny

Two NSGs apply, and both have to allow

Inbound traffic is evaluated at the subnet NSG and then at the network interface NSG. Outbound is the reverse. An allow in one and no allow in the other is a deny, and each NSG looks correct on its own in the portal. This is the most common reason a rule that reads right does nothing, and it is invisible from either rule set alone.

Egress is open by default

AllowInternetOutBound at 65001 lets anything in the subnet reach any address on the internet. That is the path a compromised host uses to fetch a payload and to send data out, and nothing restricts it until you write a rule. Expect to allow the Azure service tags, AzureMonitor and Storage among them, or the platform's own agents stop working.

Service tags are the useful part

Internet, VirtualNetwork, AzureLoadBalancer, Storage, Sql and the rest are maintained by Azure and update themselves as the underlying ranges change. A source of * is the same thing as Internet and reads as though it were narrower. An application security group goes further and lets a rule name a set of NICs rather than a set of addresses, which is the closest Azure gets to an AWS security group reference.

What this cannot see

It reads the rules you paste, so it can say what those rules do and not what is deployed. It cannot see the second NSG, and both must allow. It cannot resolve a service tag to addresses, expand an application security group to its members, or tell you which subnets and NICs this NSG is attached to. It also cannot see Azure Firewall, a route table sending traffic somewhere else, or a private endpoint bypassing the path entirely. For what is actually in effect on a machine, effective security rules on the NIC is the flattened view that answers the question directly.