Back to Checks
azure

Subnet has a network security group associated

network_subnet_nsg_associated
Service: networkSeverity: high
OfficialBuilt-inVerified
Categories:internet-exposed
Resource Type:microsoft.network/virtualnetworks/subnets

Azure Virtual Network subnets are evaluated for Network Security Group (NSG) association. Each subnet should have an NSG to enforce inbound and outbound traffic filtering rules. Subnets without NSGs allow all traffic by default.

Prowler logoProwlerprowler-provider-azure

Risk

Subnets without NSGs have no network-level access control. All inbound and outbound traffic is allowed by default, enabling lateral movement, unauthorized access to resources, and data exfiltration across the virtual network.

Run this check with Prowler

Run in Prowler Cloud
Run
prowler azure --checks network_subnet_nsg_associated

Recommendation

Associate a Network Security Group with every subnet. Create NSG rules following least privilege — deny all by default and allow only required traffic. Exclude Azure-managed subnets (GatewaySubnet, AzureFirewallSubnet, AzureBastionSubnet) which have their own security controls.

Remediation

CLI

az network vnet subnet update --resource-group <rg> --vnet-name <vnet> --name <subnet> --network-security-group <nsg_name>

Terraform

resource "azurerm_subnet_network_security_group_association" "<example_resource_name>" {
  subnet_id                 = "<example_resource_id>"  # Critical: associates NSG with subnet
  network_security_group_id = "<example_resource_id>"
}

NativeIaC

resource vnet 'Microsoft.Network/virtualNetworks@2023-09-01' existing = {
  name: '<example_resource_name>'
}

resource subnet 'Microsoft.Network/virtualNetworks/subnets@2023-09-01' = {
  name: '<example_resource_name>'
  parent: vnet
  properties: {
    addressPrefix: '10.0.1.0/24'
    networkSecurityGroup: {
      id: '<example_resource_id>' // Critical: associates NSG with subnet
    }
  }
}

Other

1Sign in to Azure portal
2Go to Virtual networks and select the VNet
3Click on Subnets
4Select the subnet without an NSG
5Under Network security group, select an existing NSG or create a new one
6Click Save

Check Code

from prowler.lib.check.models import Check, Check_Report_Azure
from prowler.providers.azure.services.network.network_client import network_client

# Subnets that are managed by Azure and should not have custom NSGs
EXCLUDED_SUBNET_NAMES = {
    "GatewaySubnet",
    "AzureFirewallSubnet",
    "AzureFirewallManagementSubnet",
    "AzureBastionSubnet",
    "RouteServerSubnet",
}


class network_subnet_nsg_associated(Check):
    """
    Ensure every subnet has a Network Security Group (NSG) associated.

    This check evaluates whether each subnet in every virtual network has an NSG associated to enforce inbound and outbound traffic filtering. Azure-managed subnets (e.g. GatewaySubnet, AzureFirewallSubnet, AzureBastionSubnet) are excluded because they must not have custom NSGs.

    - PASS: The subnet has an NSG associated.
    - FAIL: The subnet does not have an NSG associated.
    """

    def execute(self) -> Check_Report_Azure:
        findings = []

        for subscription_name, vnets in network_client.virtual_networks.items():
            for vnet in vnets:
                for subnet in vnet.subnets:
                    if subnet.name in EXCLUDED_SUBNET_NAMES:
                        continue

                    report = Check_Report_Azure(metadata=self.metadata(), resource=vnet)
                    report.subscription = subscription_name
                    report.resource_name = f"{vnet.name}/{subnet.name}"
                    report.resource_id = subnet.id
                    report.location = vnet.location

                    if subnet.nsg_id:
                        report.status = "PASS"
                        report.status_extended = (
                            f"Subnet '{subnet.name}' in VNet '{vnet.name}' "
                            f"has an NSG associated."
                        )
                    else:
                        report.status = "FAIL"
                        report.status_extended = (
                            f"Subnet '{subnet.name}' in VNet '{vnet.name}' "
                            f"does not have an NSG associated."
                        )

                    findings.append(report)

        return findings