Network segmentation is fundamental to Azure security—isolating workloads limits the blast radius of breaches and enables granular access control. This guide covers implementing defense-in-depth network architecture using Virtual Networks, subnets, Network Security Groups, Application Security Groups, Azure Firewall, and hub-spoke topology.
This article is part of our comprehensive cloud security tips guide, focusing specifically on Azure network security architecture.
Overview
Azure network segmentation involves multiple layers:
- Virtual Networks (VNets): Isolated network boundaries
- Subnets: Logical divisions within VNets
- Network Security Groups (NSGs): Layer 3/4 traffic filtering
- Application Security Groups (ASGs): Logical grouping for NSG rules
- Azure Firewall: Layer 7 firewall with threat intelligence
- Route Tables (UDRs): Custom traffic routing
- VNet Peering: Connecting VNets together
Prerequisites
Before implementing network segmentation:
- Azure subscription with Network Contributor role
- Planned IP address scheme that doesn't overlap with on-premises
- Azure CLI (2.50.0+) or Azure PowerShell
- Understanding of TCP/IP networking concepts
- Documented security requirements and traffic flows
Part 1: Virtual Network Design
Address Space Planning
Enterprise Address Space Example (10.0.0.0/8):
Hub VNet: 10.0.0.0/16
├── Gateway Subnet: 10.0.0.0/24
├── Firewall Subnet: 10.0.1.0/24
├── Bastion Subnet: 10.0.2.0/26
├── Management Subnet: 10.0.3.0/24
└── DNS Subnet: 10.0.4.0/24
Spoke 1 (Production): 10.1.0.0/16
├── Web Tier: 10.1.1.0/24
├── App Tier: 10.1.2.0/24
├── Database Tier: 10.1.3.0/24
└── Private Endpoints: 10.1.4.0/24
Spoke 2 (Development): 10.2.0.0/16
├── Web Tier: 10.2.1.0/24
├── App Tier: 10.2.2.0/24
└── Database Tier: 10.2.3.0/24
Spoke 3 (Staging): 10.3.0.0/16
└── ...
Create Hub VNet
# Create resource group for networking
az group create \
--name rg-networking-hub \
--location eastus
# Create hub VNet
az network vnet create \
--name vnet-hub \
--resource-group rg-networking-hub \
--location eastus \
--address-prefix 10.0.0.0/16 \
--tags environment=hub purpose=networking
# Create hub subnets
az network vnet subnet create \
--name GatewaySubnet \
--resource-group rg-networking-hub \
--vnet-name vnet-hub \
--address-prefix 10.0.0.0/24
az network vnet subnet create \
--name AzureFirewallSubnet \
--resource-group rg-networking-hub \
--vnet-name vnet-hub \
--address-prefix 10.0.1.0/24
az network vnet subnet create \
--name AzureBastionSubnet \
--resource-group rg-networking-hub \
--vnet-name vnet-hub \
--address-prefix 10.0.2.0/26
az network vnet subnet create \
--name snet-management \
--resource-group rg-networking-hub \
--vnet-name vnet-hub \
--address-prefix 10.0.3.0/24
Create Spoke VNets
# Create production spoke resource group
az group create \
--name rg-networking-prod \
--location eastus
# Create production spoke VNet
az network vnet create \
--name vnet-spoke-prod \
--resource-group rg-networking-prod \
--location eastus \
--address-prefix 10.1.0.0/16 \
--tags environment=production
# Create tiered subnets
az network vnet subnet create \
--name snet-web \
--resource-group rg-networking-prod \
--vnet-name vnet-spoke-prod \
--address-prefix 10.1.1.0/24
az network vnet subnet create \
--name snet-app \
--resource-group rg-networking-prod \
--vnet-name vnet-spoke-prod \
--address-prefix 10.1.2.0/24
az network vnet subnet create \
--name snet-database \
--resource-group rg-networking-prod \
--vnet-name vnet-spoke-prod \
--address-prefix 10.1.3.0/24
az network vnet subnet create \
--name snet-private-endpoints \
--resource-group rg-networking-prod \
--vnet-name vnet-spoke-prod \
--address-prefix 10.1.4.0/24 \
--disable-private-endpoint-network-policies true
Part 2: Network Security Groups (NSGs)
Create NSGs for Each Subnet Tier
# Create NSG for web tier
az network nsg create \
--name nsg-web \
--resource-group rg-networking-prod \
--location eastus
# Create NSG for app tier
az network nsg create \
--name nsg-app \
--resource-group rg-networking-prod \
--location eastus
# Create NSG for database tier
az network nsg create \
--name nsg-database \
--resource-group rg-networking-prod \
--location eastus
Configure Web Tier NSG Rules
# Allow HTTPS from internet (via Application Gateway)
az network nsg rule create \
--nsg-name nsg-web \
--resource-group rg-networking-prod \
--name AllowHTTPS \
--priority 100 \
--direction Inbound \
--access Allow \
--protocol Tcp \
--source-address-prefixes Internet \
--destination-port-ranges 443 \
--description "Allow HTTPS from internet"
# Allow HTTP for redirect (optional)
az network nsg rule create \
--nsg-name nsg-web \
--resource-group rg-networking-prod \
--name AllowHTTP \
--priority 110 \
--direction Inbound \
--access Allow \
--protocol Tcp \
--source-address-prefixes Internet \
--destination-port-ranges 80 \
--description "Allow HTTP for HTTPS redirect"
# Allow Azure Load Balancer health probes
az network nsg rule create \
--nsg-name nsg-web \
--resource-group rg-networking-prod \
--name AllowAzureLB \
--priority 120 \
--direction Inbound \
--access Allow \
--protocol "*" \
--source-address-prefixes AzureLoadBalancer \
--destination-port-ranges "*" \
--description "Allow Azure Load Balancer probes"
# Deny all other inbound traffic
az network nsg rule create \
--nsg-name nsg-web \
--resource-group rg-networking-prod \
--name DenyAllInbound \
--priority 4096 \
--direction Inbound \
--access Deny \
--protocol "*" \
--source-address-prefixes "*" \
--destination-port-ranges "*" \
--description "Deny all other inbound"
Configure App Tier NSG Rules
# Allow traffic only from web tier
az network nsg rule create \
--nsg-name nsg-app \
--resource-group rg-networking-prod \
--name AllowFromWebTier \
--priority 100 \
--direction Inbound \
--access Allow \
--protocol Tcp \
--source-address-prefixes 10.1.1.0/24 \
--destination-port-ranges 8080 8443 \
--description "Allow from web tier"
# Allow management from hub
az network nsg rule create \
--nsg-name nsg-app \
--resource-group rg-networking-prod \
--name AllowManagement \
--priority 110 \
--direction Inbound \
--access Allow \
--protocol Tcp \
--source-address-prefixes 10.0.3.0/24 \
--destination-port-ranges 22 3389 \
--description "Allow SSH/RDP from management subnet"
# Deny all other inbound
az network nsg rule create \
--nsg-name nsg-app \
--resource-group rg-networking-prod \
--name DenyAllInbound \
--priority 4096 \
--direction Inbound \
--access Deny \
--protocol "*" \
--source-address-prefixes "*" \
--destination-port-ranges "*"
Configure Database Tier NSG Rules
# Allow SQL from app tier only
az network nsg rule create \
--nsg-name nsg-database \
--resource-group rg-networking-prod \
--name AllowSQLFromAppTier \
--priority 100 \
--direction Inbound \
--access Allow \
--protocol Tcp \
--source-address-prefixes 10.1.2.0/24 \
--destination-port-ranges 1433 3306 5432 \
--description "Allow SQL/MySQL/PostgreSQL from app tier"
# Deny direct internet access
az network nsg rule create \
--nsg-name nsg-database \
--resource-group rg-networking-prod \
--name DenyInternet \
--priority 4000 \
--direction Outbound \
--access Deny \
--protocol "*" \
--destination-address-prefixes Internet \
--destination-port-ranges "*" \
--description "Deny direct internet access"
# Deny all other inbound
az network nsg rule create \
--nsg-name nsg-database \
--resource-group rg-networking-prod \
--name DenyAllInbound \
--priority 4096 \
--direction Inbound \
--access Deny \
--protocol "*" \
--source-address-prefixes "*" \
--destination-port-ranges "*"
Associate NSGs with Subnets
# Associate NSGs with subnets
az network vnet subnet update \
--name snet-web \
--resource-group rg-networking-prod \
--vnet-name vnet-spoke-prod \
--network-security-group nsg-web
az network vnet subnet update \
--name snet-app \
--resource-group rg-networking-prod \
--vnet-name vnet-spoke-prod \
--network-security-group nsg-app
az network vnet subnet update \
--name snet-database \
--resource-group rg-networking-prod \
--vnet-name vnet-spoke-prod \
--network-security-group nsg-database
Part 3: Application Security Groups (ASGs)
ASGs simplify NSG rule management by grouping VMs logically.
Create ASGs
# Create ASGs for each application role
az network asg create \
--name asg-webservers \
--resource-group rg-networking-prod \
--location eastus
az network asg create \
--name asg-appservers \
--resource-group rg-networking-prod \
--location eastus
az network asg create \
--name asg-databases \
--resource-group rg-networking-prod \
--location eastus
az network asg create \
--name asg-jumpboxes \
--resource-group rg-networking-prod \
--location eastus
Create NSG Rules Using ASGs
# Create a new NSG for ASG-based rules
az network nsg create \
--name nsg-asg-based \
--resource-group rg-networking-prod \
--location eastus
# Allow web servers to communicate with app servers
az network nsg rule create \
--nsg-name nsg-asg-based \
--resource-group rg-networking-prod \
--name AllowWebToApp \
--priority 100 \
--direction Inbound \
--access Allow \
--protocol Tcp \
--source-asgs asg-webservers \
--destination-asgs asg-appservers \
--destination-port-ranges 8080 8443 \
--description "Allow web servers to reach app servers"
# Allow app servers to communicate with databases
az network nsg rule create \
--nsg-name nsg-asg-based \
--resource-group rg-networking-prod \
--name AllowAppToDatabase \
--priority 110 \
--direction Inbound \
--access Allow \
--protocol Tcp \
--source-asgs asg-appservers \
--destination-asgs asg-databases \
--destination-port-ranges 1433 3306 5432 \
--description "Allow app servers to reach databases"
# Allow jumpboxes to manage all servers
az network nsg rule create \
--nsg-name nsg-asg-based \
--resource-group rg-networking-prod \
--name AllowJumpboxManagement \
--priority 120 \
--direction Inbound \
--access Allow \
--protocol Tcp \
--source-asgs asg-jumpboxes \
--destination-port-ranges 22 3389 \
--description "Allow jumpbox SSH/RDP access"
Associate VM NICs with ASGs
# When creating a VM, associate its NIC with an ASG
az network nic create \
--name nic-webserver-01 \
--resource-group rg-networking-prod \
--vnet-name vnet-spoke-prod \
--subnet snet-web \
--application-security-groups asg-webservers
# Or update existing NIC
az network nic ip-config update \
--name ipconfig1 \
--nic-name nic-webserver-01 \
--resource-group rg-networking-prod \
--application-security-groups asg-webservers
Part 4: Hub-Spoke Topology with VNet Peering
Create VNet Peerings
# Get VNet resource IDs
HUB_VNET_ID=$(az network vnet show \
--name vnet-hub \
--resource-group rg-networking-hub \
--query id -o tsv)
SPOKE_VNET_ID=$(az network vnet show \
--name vnet-spoke-prod \
--resource-group rg-networking-prod \
--query id -o tsv)
# Create peering from hub to spoke
az network vnet peering create \
--name hub-to-spoke-prod \
--resource-group rg-networking-hub \
--vnet-name vnet-hub \
--remote-vnet $SPOKE_VNET_ID \
--allow-vnet-access true \
--allow-forwarded-traffic true \
--allow-gateway-transit true
# Create peering from spoke to hub
az network vnet peering create \
--name spoke-prod-to-hub \
--resource-group rg-networking-prod \
--vnet-name vnet-spoke-prod \
--remote-vnet $HUB_VNET_ID \
--allow-vnet-access true \
--allow-forwarded-traffic true \
--use-remote-gateways false # Set to true when VPN gateway exists
Verify Peering Status
# Check peering status
az network vnet peering show \
--name hub-to-spoke-prod \
--resource-group rg-networking-hub \
--vnet-name vnet-hub \
--query peeringState -o tsv
# Should return: Connected
az network vnet peering show \
--name spoke-prod-to-hub \
--resource-group rg-networking-prod \
--vnet-name vnet-spoke-prod \
--query peeringState -o tsv
# Should return: Connected
Part 5: Azure Firewall Deployment
Deploy Azure Firewall in Hub
# Create public IP for firewall
az network public-ip create \
--name pip-azfw \
--resource-group rg-networking-hub \
--location eastus \
--allocation-method Static \
--sku Standard
# Create Azure Firewall
az network firewall create \
--name azfw-hub \
--resource-group rg-networking-hub \
--location eastus \
--sku AZFW_VNet \
--tier Premium \
--threat-intel-mode Deny
# Configure firewall with public IP
az network firewall ip-config create \
--firewall-name azfw-hub \
--name fw-ipconfig \
--resource-group rg-networking-hub \
--public-ip-address pip-azfw \
--vnet-name vnet-hub
# Get firewall private IP
FW_PRIVATE_IP=$(az network firewall show \
--name azfw-hub \
--resource-group rg-networking-hub \
--query "ipConfigurations[0].privateIPAddress" -o tsv)
echo "Firewall Private IP: $FW_PRIVATE_IP"
Create Firewall Policy
# Create firewall policy
az network firewall policy create \
--name policy-azfw-hub \
--resource-group rg-networking-hub \
--location eastus \
--sku Premium \
--threat-intel-mode Deny \
--idps-mode Deny
# Create rule collection group
az network firewall policy rule-collection-group create \
--name rcg-application-rules \
--policy-name policy-azfw-hub \
--resource-group rg-networking-hub \
--priority 100
# Add application rules (allow outbound web access)
az network firewall policy rule-collection-group collection add-filter-collection \
--name rc-allow-web \
--policy-name policy-azfw-hub \
--resource-group rg-networking-hub \
--rcg-name rcg-application-rules \
--collection-priority 100 \
--action Allow \
--rule-name "AllowMicrosoftUpdates" \
--rule-type ApplicationRule \
--source-addresses "10.1.0.0/16" \
--protocols https=443 \
--fqdn-tags "WindowsUpdate" "MicrosoftActiveProtectionService"
# Add network rules (allow DNS)
az network firewall policy rule-collection-group collection add-filter-collection \
--name rc-allow-dns \
--policy-name policy-azfw-hub \
--resource-group rg-networking-hub \
--rcg-name rcg-application-rules \
--collection-priority 110 \
--action Allow \
--rule-name "AllowDNS" \
--rule-type NetworkRule \
--source-addresses "10.1.0.0/16" \
--destination-addresses "168.63.129.16" \
--destination-ports 53 \
--ip-protocols UDP TCP
# Associate policy with firewall
az network firewall update \
--name azfw-hub \
--resource-group rg-networking-hub \
--firewall-policy policy-azfw-hub
Create User Defined Routes (UDRs)
Route spoke traffic through the firewall:
# Create route table for spokes
az network route-table create \
--name rt-spoke-to-firewall \
--resource-group rg-networking-prod \
--location eastus \
--disable-bgp-route-propagation true
# Default route to firewall
az network route-table route create \
--route-table-name rt-spoke-to-firewall \
--resource-group rg-networking-prod \
--name route-to-firewall \
--address-prefix 0.0.0.0/0 \
--next-hop-type VirtualAppliance \
--next-hop-ip-address $FW_PRIVATE_IP
# Route to other spokes via firewall
az network route-table route create \
--route-table-name rt-spoke-to-firewall \
--resource-group rg-networking-prod \
--name route-to-spoke-dev \
--address-prefix 10.2.0.0/16 \
--next-hop-type VirtualAppliance \
--next-hop-ip-address $FW_PRIVATE_IP
# Associate route table with spoke subnets
az network vnet subnet update \
--name snet-web \
--resource-group rg-networking-prod \
--vnet-name vnet-spoke-prod \
--route-table rt-spoke-to-firewall
az network vnet subnet update \
--name snet-app \
--resource-group rg-networking-prod \
--vnet-name vnet-spoke-prod \
--route-table rt-spoke-to-firewall
az network vnet subnet update \
--name snet-database \
--resource-group rg-networking-prod \
--vnet-name vnet-spoke-prod \
--route-table rt-spoke-to-firewall
Part 6: Terraform Configuration
Complete infrastructure-as-code implementation:
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.85"
}
}
}
provider "azurerm" {
features {}
}
# Variables
variable "location" {
default = "eastus"
}
variable "hub_address_space" {
default = "10.0.0.0/16"
}
variable "spoke_prod_address_space" {
default = "10.1.0.0/16"
}
# Resource Groups
resource "azurerm_resource_group" "hub" {
name = "rg-networking-hub"
location = var.location
}
resource "azurerm_resource_group" "spoke_prod" {
name = "rg-networking-prod"
location = var.location
}
# Hub VNet
resource "azurerm_virtual_network" "hub" {
name = "vnet-hub"
resource_group_name = azurerm_resource_group.hub.name
location = var.location
address_space = [var.hub_address_space]
}
resource "azurerm_subnet" "firewall" {
name = "AzureFirewallSubnet"
resource_group_name = azurerm_resource_group.hub.name
virtual_network_name = azurerm_virtual_network.hub.name
address_prefixes = ["10.0.1.0/24"]
}
resource "azurerm_subnet" "bastion" {
name = "AzureBastionSubnet"
resource_group_name = azurerm_resource_group.hub.name
virtual_network_name = azurerm_virtual_network.hub.name
address_prefixes = ["10.0.2.0/26"]
}
resource "azurerm_subnet" "management" {
name = "snet-management"
resource_group_name = azurerm_resource_group.hub.name
virtual_network_name = azurerm_virtual_network.hub.name
address_prefixes = ["10.0.3.0/24"]
}
# Spoke VNet
resource "azurerm_virtual_network" "spoke_prod" {
name = "vnet-spoke-prod"
resource_group_name = azurerm_resource_group.spoke_prod.name
location = var.location
address_space = [var.spoke_prod_address_space]
}
resource "azurerm_subnet" "web" {
name = "snet-web"
resource_group_name = azurerm_resource_group.spoke_prod.name
virtual_network_name = azurerm_virtual_network.spoke_prod.name
address_prefixes = ["10.1.1.0/24"]
}
resource "azurerm_subnet" "app" {
name = "snet-app"
resource_group_name = azurerm_resource_group.spoke_prod.name
virtual_network_name = azurerm_virtual_network.spoke_prod.name
address_prefixes = ["10.1.2.0/24"]
}
resource "azurerm_subnet" "database" {
name = "snet-database"
resource_group_name = azurerm_resource_group.spoke_prod.name
virtual_network_name = azurerm_virtual_network.spoke_prod.name
address_prefixes = ["10.1.3.0/24"]
}
# NSGs
resource "azurerm_network_security_group" "web" {
name = "nsg-web"
resource_group_name = azurerm_resource_group.spoke_prod.name
location = var.location
security_rule {
name = "AllowHTTPS"
priority = 100
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "443"
source_address_prefix = "*"
destination_address_prefix = "*"
}
security_rule {
name = "DenyAllInbound"
priority = 4096
direction = "Inbound"
access = "Deny"
protocol = "*"
source_port_range = "*"
destination_port_range = "*"
source_address_prefix = "*"
destination_address_prefix = "*"
}
}
resource "azurerm_network_security_group" "app" {
name = "nsg-app"
resource_group_name = azurerm_resource_group.spoke_prod.name
location = var.location
security_rule {
name = "AllowFromWebTier"
priority = 100
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_ranges = ["8080", "8443"]
source_address_prefix = "10.1.1.0/24"
destination_address_prefix = "*"
}
security_rule {
name = "DenyAllInbound"
priority = 4096
direction = "Inbound"
access = "Deny"
protocol = "*"
source_port_range = "*"
destination_port_range = "*"
source_address_prefix = "*"
destination_address_prefix = "*"
}
}
resource "azurerm_network_security_group" "database" {
name = "nsg-database"
resource_group_name = azurerm_resource_group.spoke_prod.name
location = var.location
security_rule {
name = "AllowSQLFromAppTier"
priority = 100
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_ranges = ["1433", "3306", "5432"]
source_address_prefix = "10.1.2.0/24"
destination_address_prefix = "*"
}
security_rule {
name = "DenyInternet"
priority = 4000
direction = "Outbound"
access = "Deny"
protocol = "*"
source_port_range = "*"
destination_port_range = "*"
source_address_prefix = "*"
destination_address_prefix = "Internet"
}
}
# NSG Associations
resource "azurerm_subnet_network_security_group_association" "web" {
subnet_id = azurerm_subnet.web.id
network_security_group_id = azurerm_network_security_group.web.id
}
resource "azurerm_subnet_network_security_group_association" "app" {
subnet_id = azurerm_subnet.app.id
network_security_group_id = azurerm_network_security_group.app.id
}
resource "azurerm_subnet_network_security_group_association" "database" {
subnet_id = azurerm_subnet.database.id
network_security_group_id = azurerm_network_security_group.database.id
}
# VNet Peering
resource "azurerm_virtual_network_peering" "hub_to_spoke" {
name = "hub-to-spoke-prod"
resource_group_name = azurerm_resource_group.hub.name
virtual_network_name = azurerm_virtual_network.hub.name
remote_virtual_network_id = azurerm_virtual_network.spoke_prod.id
allow_virtual_network_access = true
allow_forwarded_traffic = true
allow_gateway_transit = true
}
resource "azurerm_virtual_network_peering" "spoke_to_hub" {
name = "spoke-prod-to-hub"
resource_group_name = azurerm_resource_group.spoke_prod.name
virtual_network_name = azurerm_virtual_network.spoke_prod.name
remote_virtual_network_id = azurerm_virtual_network.hub.id
allow_virtual_network_access = true
allow_forwarded_traffic = true
use_remote_gateways = false
}
# Azure Firewall
resource "azurerm_public_ip" "firewall" {
name = "pip-azfw"
resource_group_name = azurerm_resource_group.hub.name
location = var.location
allocation_method = "Static"
sku = "Standard"
}
resource "azurerm_firewall" "hub" {
name = "azfw-hub"
resource_group_name = azurerm_resource_group.hub.name
location = var.location
sku_name = "AZFW_VNet"
sku_tier = "Premium"
threat_intel_mode = "Deny"
ip_configuration {
name = "fw-ipconfig"
subnet_id = azurerm_subnet.firewall.id
public_ip_address_id = azurerm_public_ip.firewall.id
}
}
# Route Table
resource "azurerm_route_table" "spoke_to_firewall" {
name = "rt-spoke-to-firewall"
resource_group_name = azurerm_resource_group.spoke_prod.name
location = var.location
route {
name = "route-to-firewall"
address_prefix = "0.0.0.0/0"
next_hop_type = "VirtualAppliance"
next_hop_in_ip_address = azurerm_firewall.hub.ip_configuration[0].private_ip_address
}
}
resource "azurerm_subnet_route_table_association" "web" {
subnet_id = azurerm_subnet.web.id
route_table_id = azurerm_route_table.spoke_to_firewall.id
}
resource "azurerm_subnet_route_table_association" "app" {
subnet_id = azurerm_subnet.app.id
route_table_id = azurerm_route_table.spoke_to_firewall.id
}
Best Practices Summary
- Plan address space carefully - Avoid overlaps with on-premises and other VNets
- Use subnet-level NSGs - Apply to subnets rather than individual NICs
- Implement deny-by-default - Explicit allow rules only
- Leverage ASGs - Simplify rule management for dynamic workloads
- Centralize security in hub - Use Azure Firewall for inspection
- Log everything - Enable NSG flow logs and firewall diagnostics
- Test connectivity - Use Network Watcher for verification
- Document thoroughly - Maintain network diagrams and rule documentation
Troubleshooting
Issue: VMs Cannot Communicate Across Peered VNets
# Verify peering status
az network vnet peering show \
--name spoke-prod-to-hub \
--resource-group rg-networking-prod \
--vnet-name vnet-spoke-prod
# Check effective routes
az network nic show-effective-route-table \
--name nic-vm-01 \
--resource-group rg-networking-prod
Issue: Traffic Not Reaching Firewall
# Verify route table association
az network vnet subnet show \
--name snet-web \
--resource-group rg-networking-prod \
--vnet-name vnet-spoke-prod \
--query routeTable.id
# Check effective security rules
az network nic list-effective-nsg \
--name nic-vm-01 \
--resource-group rg-networking-prod