What it is
A virtual network subnet with no network security group associated. NSGs are Azure's basic packet filter, and a subnet without one enforces no inbound or outbound rules at that layer. Anything deployed into the subnet is protected only by NIC-level NSGs, application firewalls, or topology, if those exist. Often they don't, and the subnet is simply open.
Why it happens
Azure lets you create a subnet with no NSG and never complains. The portal's VNet creation flow doesn't force the association, az network vnet subnet create doesn't require it, and most infrastructure-as-code examples omit it because the deployment works fine without one.
The NSG and the subnet are also separate resources with a separate association step, so they drift apart naturally: a subnet gets added to an existing VNet during an incident, a template gets copied without the networkSecurityGroup block, or an NSG gets detached "temporarily" for troubleshooting and never reattached. Nothing breaks, so nothing gets noticed.
One legitimate wrinkle: a handful of Azure-managed reserved subnets (GatewaySubnet, AzureFirewallSubnet, AzureFirewallManagementSubnet, AzureBastionSubnet, RouteServerSubnet) are special cases where Azure either disallows a user NSG or requires its own documented rule set, and subnets delegated to a PaaS service hand their networking to that service. Those are fine. The problem is every other subnet, the ones carrying your VMs and private endpoints.
What it costs / blast radius
There is no direct cost; NSGs are free. The blast radius is defense-in-depth with a floor missing. If a VM in an NSG-less subnet picks up a public IP, every port it listens on is on the internet with nothing in the path. Inside the network, a compromised workload can reach every port on every neighbor in the subnet, and often across peered networks too, because there is no subnet-level rule to stop lateral movement. When an incident does happen, there are also no NSG flow logs at that layer to reconstruct what moved where. (Authored assessment of Azure behavior.)
See it
Resources
| where type =~ 'microsoft.network/virtualnetworks'
| mv-expand subnet = properties.subnets
| extend subnetName = tostring(subnet.name)
| where subnetName !in~ ('GatewaySubnet', 'AzureFirewallSubnet',
'AzureFirewallManagementSubnet', 'AzureBastionSubnet', 'RouteServerSubnet')
| where isnull(subnet.properties.networkSecurityGroup)
| where isnull(subnet.properties.delegations)
or array_length(subnet.properties.delegations) == 0
| project vnet = name, subnet = subnetName, resourceGroup, subscriptionId,
addressPrefix = tostring(subnet.properties.addressPrefix)az network nsg create \
--resource-group rg-prod --name nsg-app-subnet
az network vnet subnet update \
--resource-group rg-prod --vnet-name corp-vnet \
--name app-subnet --network-security-group nsg-app-subnetHow StratoLens helps
StratoLens checks every subnet in every VNet across all your subscriptions for a missing NSG association, automatically skipping the reserved and delegated subnets that are legitimate special cases, so the findings you see are the real gaps. It runs continuously inside your own tenant; the subnet added during last quarter's incident gets flagged without anyone remembering to look.