Control Traffic with Network Security Groups

Published:18 February 2026 - 5 min. read

Audit your Active Directory for weak passwords and risky accounts. Run your free Specops scan now!

You’d think controlling network traffic in Azure would require a dedicated firewall appliance, complex routing tables, and a networking certification. It doesn’t. Azure Network Security Groups (NSGs) give you stateful, Layer 4 traffic filtering that you can attach directly to subnets or network interfaces–no extra infrastructure required.

What NSGs Actually Do

An NSG is a set of allow/deny rules that Azure evaluates against a 5-tuple: source IP, source port, destination IP, destination port, and protocol. Every packet hitting your resources gets checked against these rules in priority order, lowest number first. The moment a packet matches a rule, processing stops.

Property What It Controls
Priority Order of evaluation (100-4096, lower = first)
Source/Destination IP, CIDR, service tag, or application security group
Protocol TCP, UDP, ICMP, or Any
Action Allow or Deny

The key detail that separates NSGs from traditional router ACLs: they’re stateful. Allow inbound traffic on port 443, and the return traffic flows back automatically. You don’t need a matching outbound rule for the response. That alone cuts your rule count roughly in half compared to stateless firewalls.

Prerequisites

Before you start creating NSGs, confirm you have these ready:

  • An Azure subscription with at least Network Contributor permissions

  • Azure CLI installed and authenticated (az login)

  • A resource group and virtual network already deployed

Verify your CLI is ready:

az account show --query "{subscription:name, tenantId:tenantId}" --output table

Create Your First NSG

Start by creating an NSG and attaching it to a subnet. Subnet-level attachment is the recommended approach–it applies rules consistently to every resource in that subnet without requiring per-VM configuration.

# Create the NSG
az network nsg create \
  --resource-group myResourceGroup \
  --name mySubnet-nsg \
  --location eastus

# Attach it to a subnet
az network vnet subnet update \
  --resource-group myResourceGroup \
  --vnet-name myVNet \
  --name mySubnet \
  --network-security-group mySubnet-nsg

Every new NSG comes with default security rules that you can’t delete but can override with higher-priority custom rules.

Default Rule Priority What It Does
AllowVNetInBound 65000 Permits traffic within the VNet and peered VNets
AllowAzureLoadBalancerInBound 65001 Allows health probes from Azure Load Balancer
DenyAllInBound 65500 Blocks everything else inbound
AllowVNetOutBound 65000 Permits outbound traffic within the VNet
AllowInternetOutBound 65001 Allows all outbound internet traffic
DenyAllOutBound 65500 Blocks everything else outbound

That AllowInternetOutBound rule is worth noting. It means every VM can reach the internet by default–fine for dev environments, but you’ll want to override it in production with a deny rule at a lower priority number.


Warning: Attaching different NSGs to both a subnet and a NIC creates a double-filter. Inbound traffic must pass the subnet NSG first, then the NIC NSG. Outbound traffic goes the other direction. This layering sounds secure but usually just creates troubleshooting headaches.


Add Custom Security Rules

Now add rules that match your actual workload. Here’s how to allow inbound HTTPS traffic:

az network nsg rule create \
  --resource-group myResourceGroup \
  --nsg-name mySubnet-nsg \
  --name AllowHTTPS \
  --priority 100 \
  --direction Inbound \
  --access Allow \
  --protocol Tcp \
  --source-address-prefixes '*' \
  --source-port-ranges '*' \
  --destination-address-prefixes '*' \
  --destination-port-ranges 443

And block direct SSH access from the internet:

az network nsg rule create \
  --resource-group myResourceGroup \
  --nsg-name mySubnet-nsg \
  --name DenySSHFromInternet \
  --priority 200 \
  --direction Inbound \
  --access Deny \
  --protocol Tcp \
  --source-address-prefixes Internet \
  --source-port-ranges '*' \
  --destination-address-prefixes '*' \
  --destination-port-ranges 22

Leave gaps between priority numbers (100, 200, 300) so you can insert rules later without renumbering your entire stack.

Verify your rules are in place:

az network nsg rule list \
  --resource-group myResourceGroup \
  --nsg-name mySubnet-nsg \
  --output table

This lists every rule in priority order, so you can confirm your custom rules sit above the default deny-all at 65500.

If you’re managing environments with hundreds of IP addresses, you’ll bump into the 1,000 rules per NSG limit. Augmented security rules help here–they let you specify up to 4,000 IP addresses or ranges in a single rule’s source or destination field, condensing what would be hundreds of individual rules into one. Use comma-separated values in --source-address-prefixes or --destination-address-prefixes:

az network nsg rule create \
  --resource-group myResourceGroup \
  --nsg-name mySubnet-nsg \
  --name AllowPartnerIPs \
  --priority 250 \
  --direction Inbound \
  --access Allow \
  --protocol Tcp \
  --source-address-prefixes 10.1.0.0/24 10.2.0.0/24 192.168.5.0/24 \
  --destination-port-ranges 443

Use Service Tags Instead of IP Addresses

Hardcoding IP addresses into NSG rules is a maintenance trap. Azure IP ranges change, and your rules go stale the moment Microsoft updates their infrastructure. Service tags solve this by representing groups of IP prefixes that Azure maintains automatically.

Service Tag What It Covers
Internet All public IP space outside the VNet
VirtualNetwork VNet address space, peered VNets, on-premises via gateway
AzureCloud All Azure datacenter IPs (regionable: AzureCloud.EastUS)
Storage Azure Storage endpoints (regionable: Storage.WestUS)
Sql Azure SQL Database endpoints

Restrict a subnet to only reach Azure Storage in a specific region:

az network nsg rule create \
  --resource-group myResourceGroup \
  --nsg-name mySubnet-nsg \
  --name AllowStorageWestUS \
  --priority 300 \
  --direction Outbound \
  --access Allow \
  --protocol Tcp \
  --source-address-prefixes '*' \
  --source-port-ranges '*' \
  --destination-address-prefixes Storage.WestUS \
  --destination-port-ranges 443

Pro Tip: You can list all available service tags for your region with az network list-service-tags --location eastus. The output is verbose, so pipe it through jq to filter what you need.


Group VMs with Application Security Groups

Application Security Groups (ASGs) let you write NSG rules based on workload roles instead of IP addresses. Tag your web servers as “WebServers” and your database servers as “DbServers,” then write rules referencing those groups. When you add a new database server, you add its NIC to the ASG–the rules apply automatically.

Create ASGs and use them in a rule:

# Create ASGs
az network asg create --resource-group myResourceGroup --name WebServers --location eastus
az network asg create --resource-group myResourceGroup --name DbServers --location eastus

# Allow web tier to reach database tier on SQL port
az network nsg rule create \
  --resource-group myResourceGroup \
  --nsg-name mySubnet-nsg \
  --name AllowWebToDb \
  --priority 400 \
  --direction Inbound \
  --access Allow \
  --protocol Tcp \
  --source-asgs WebServers \
  --destination-asgs DbServers \
  --destination-port-ranges 1433

After creating the ASGs, assign your VM’s network interface to the appropriate group:

# Add a VM's NIC to the WebServers ASG
az network nic ip-config update \
  --resource-group myResourceGroup \
  --nic-name myWebVM-nic \
  --name ipconfig1 \
  --application-security-groups WebServers

Deploy a new web server next month? Add its NIC to the WebServers ASG and your existing rules apply immediately. No rule edits, no IP address hunting.

ASGs have two constraints worth knowing: they’re scoped to a single virtual network (no spanning across peered VNets), and each subscription supports up to 3,000 ASGs.

Troubleshoot with IP Flow Verify

You configured your rules, but traffic still isn’t flowing. Before you start reading through dozens of rules manually, use IP Flow Verify in Azure Network Watcher. It tests a synthetic packet against all active NSG rules and tells you exactly which rule is allowing or denying traffic.

az network watcher test-ip-flow \
  --direction Inbound \
  --protocol Tcp \
  --local 10.0.0.4:443 \
  --remote 203.0.113.50:60000 \
  --vm myVM \
  --nic myVM-nic \
  --resource-group myResourceGroup

The output tells you Access: Allow or Access: Deny and names the specific rule responsible. No guessing, no manually tracing through rule priority chains.

For a broader view of all effective rules applied to a network interface, use NSG Diagnostics. It aggregates default rules, subnet rules, and NIC rules into a single view–essential when you need to spot conflicts between layers.


Key Insight: Microsoft is retiring NSG Flow Logs in September 2027. New NSG Flow Log creation was disabled in June 2025. Migrate to VNet Flow Logs now–they cover traffic at the VNet level regardless of NSG attachment, and you configure them once per VNet instead of per NSG.


What to Do Next

You now have an NSG attached to a subnet, custom rules filtering traffic by port and protocol, service tags keeping your IP ranges current, and ASGs grouping your workloads by role. That covers the fundamentals.

From here, consider tightening your configuration. Override AllowInternetOutBound with explicit deny rules for production subnets. Set up VNet Flow Logs and connect them to Traffic Analytics so you can see which VMs are talking to unexpected destinations. And if you’re running an N-tier architecture, create separate subnets and NSGs per tier so your web servers can’t directly reach your database subnet.

NSGs won’t replace a full Azure Firewall deployment for deep packet inspection or application-layer filtering. But for Layer 4 traffic control across your VNets, they’re the first thing you should configure–and often the only thing you need.

Hate ads? Want to support the writer? Get many of our tutorials packaged as an ATA Guidebook.

Explore ATA Guidebooks

Looks like you're offline!