Theoretical Foundation: Create and Configure Network Security Groups (NSGs) and Application Security Groups
1. Initial Intuitionβ
Imagine a modern office building. At the main entrance, there's a security guard who checks who can enter and who can leave, with a list of rules: "visitors only enter through the lobby", "suppliers can only go to the warehouse", "finance staff cannot access the IT server". Each floor can have its own additional rules.
A Network Security Group (NSG) is exactly this security guard: a set of rules that controls which network traffic can enter and exit resources in Azure. Each rule defines: where the traffic comes from, where it's going, which protocol and port, and whether it's allowed or blocked.
An Application Security Group (ASG) is an elegant extension of this concept: instead of writing rules based on individual IP addresses, you group VMs by function (such as "web servers", "database servers") and write rules using these groups. When a new VM joins the group, it automatically inherits all the rules associated with the group.
2. Contextβ
2.1 NSGs and ASGs in the Azure ecosystemβ
2.2 Why NSGs and ASGs existβ
Before NSGs, controlling network traffic in the cloud was done only with traditional firewalls on dedicated appliances. NSGs democratized network control by making it declarative, free (no additional cost beyond infrastructure), and manageable as code.
ASGs emerged to solve the scale problem: in environments with hundreds of VMs, maintaining rules based on individual IPs was impractical. An ASG decouples security identity (the group) from IP addressing.
2.3 What depends on NSGsβ
- SSH/RDP access to VMs: Controlled by NSG inbound rule
- Communication between tiers (web, app, database): Controlled by rules between NSGs/ASGs
- Azure Bastion: Requires specific rules in the Bastion subnet NSG
- Load Balancer probes: Requires explicit permission from IP
168.63.129.16 - Service Endpoints: Work in conjunction with NSG rules
3. Building the Conceptsβ
3.1 Anatomy of an NSG ruleβ
Each NSG rule has five mandatory attributes and one result:
| Attribute | Description | Example |
|---|---|---|
| Priority | Number from 100 to 4096; lower number = higher priority | 100 |
| Name | Unique identifier of the rule in the NSG | Allow-HTTP-Inbound |
| Protocol | TCP, UDP, ICMP or * (any) | TCP |
| Source | IP, CIDR, Service Tag, or ASG where traffic comes from | Internet |
| Destination | IP, CIDR, Service Tag, or ASG destination | 10.0.1.0/24 |
| Source Port Range | Source port (usually *) | * |
| Destination Port Range | Destination port | 80, 443, 3389-3400 |
| Action | Allow or Deny | Allow |
| Direction | Inbound or Outbound | Inbound |
Why is Source Port Range usually
*? TCP connections start with an ephemeral source port (chosen by the operating system, usually above 1024). You rarely know this port beforehand, so you use*at the source and specify only the destination port.
3.2 Default rulesβ
Every NSG is created with a set of default rules that cannot be deleted, only overridden by higher priority rules:
Default Inbound rules:
| Priority | Name | Source | Destination | Port | Action |
|---|---|---|---|---|---|
| 65000 | AllowVnetInBound | VirtualNetwork | VirtualNetwork | * | Allow |
| 65001 | AllowAzureLoadBalancerInBound | AzureLoadBalancer | * | * | Allow |
| 65500 | DenyAllInBound | * | * | * | Deny |
Default Outbound rules:
| Priority | Name | Source | Destination | Port | Action |
|---|---|---|---|---|---|
| 65000 | AllowVnetOutBound | VirtualNetwork | VirtualNetwork | * | Allow |
| 65001 | AllowInternetOutBound | * | Internet | * | Allow |
| 65500 | DenyAllOutBound | * | * | * | Deny |
Non-obvious behavior: By default, all traffic between VMs in the same VNet is allowed (
AllowVnetInBoundandAllowVnetOutBound). And all traffic outbound to the internet is also allowed. For secure environments, you need to add explicit Deny rules with priority lower than 65000 to override these defaults.
3.3 Service Tags: simplifying IP rulesβ
Service Tags are labels that represent sets of IP prefixes from Azure services. Instead of maintaining lists of IPs that change, you use the service tag and Microsoft automatically keeps the list updated.
Most used tags in NSGs:
| Service Tag | Represents |
|---|---|
Internet | All traffic from/to the public internet |
VirtualNetwork | The entire VNet and peered VNets |
AzureLoadBalancer | Azure Load Balancer probe IPs |
AzureCloud | All Azure public IPs |
Storage | Azure Storage IPs (all endpoints) |
Sql | Azure SQL server IPs |
AppService | Azure App Service IPs |
AzureActiveDirectory | Azure AD endpoint IPs |
GatewayManager | VPN/ExpressRoute Gateway management IPs |
3.4 Application Security Groups (ASGs)β
An ASG is an Azure object that groups NICs (network interfaces) from VMs. You then reference these groups in NSG rules instead of individual IPs.
Analogy: An ASG is like a "role" in a company. Instead of writing a rule saying "John, Mary and Peter can access the finance server", you say "employees with the 'Financial Analyst' role can access". When a new analyst is hired, just put them in the group and they automatically gain access.
3.5 ASG restrictionsβ
To use ASGs, there are important restrictions:
- A NIC can be associated with multiple ASGs
- All ASGs referenced in a rule must be in the same VNet
- All ASGs used as source and destination in a rule must be in the same VNet
- You cannot combine ASG with IP address in the same rule (source or destination needs to be one type: ASG, IP/CIDR, or Service Tag)
4. Structural Viewβ
4.1 Traffic evaluation flow (double verification)β
When traffic reaches a VM, Azure evaluates two NSGs in sequence, in the correct order:
For Outbound traffic, the order is reversed: NIC NSG is evaluated first, then Subnet NSG.
Fundamental rule: For traffic to pass, both NSGs need to allow it. If either blocks it, the traffic is denied.
4.2 Where NSGs can be associatedβ
Best practice: Associate NSGs to subnets, not individual NICs. This is simpler to manage. Use NSG on NIC only for specific exceptions that the subnet NSG shouldn't cover.
5. Practical Operationβ
5.1 Complete scenario: three-tier architectureβ
Consider a three-tier application: Web, App and Database.
In this model:
NSG-Webis associated with the Web subnet and allows only HTTP/HTTPS from the internetNSG-Appallows only traffic on port 8080 fromASG-WebServersNSG-DBallows only SQL (port 1433) fromASG-AppServers- Lateral movement between tiers is blocked by default
5.2 Effective Security Rules: effective rules diagnosisβ
A powerful Azure feature is the ability to see the combined effective rules from NSGs on a specific NIC:
In the portal: VM NIC > Effective Security Rules
This shows rules from all associated NSGs (subnet + NIC) in a consolidated view, in evaluation order. It's the main tool for diagnosing why traffic is being blocked or allowed.
5.3 Network Watcher: IP Flow Verifyβ
For more direct connectivity diagnosis:
Azure Network Watcher > IP Flow Verify
You specify:
- Source VM
- Destination IP address
- Port and protocol
- Direction (inbound/outbound)
Azure simulates the flow and reports whether it would be allowed or denied, and which specific rule would be responsible for the decision.
6. Implementation Methodsβ
6.1 Azure Portalβ
When to use: Initial creation, effective rules visualization, visual troubleshooting.
Create NSG: Resource Groups > + Create > Network Security Group
After creating, add rules in Settings > Inbound security rules or Outbound security rules.
Associate to subnet: Virtual Network > Subnets > [subnet] > Network security group
Limitation: Not scalable for multiple NSGs or complex rules.
6.2 Azure CLIβ
Creating NSG:
az network nsg create \
--name NSG-Web \
--resource-group myRG \
--location eastus
Adding inbound rule:
# Allow HTTP and HTTPS from internet
az network nsg rule create \
--nsg-name NSG-Web \
--resource-group myRG \
--name Allow-HTTP-HTTPS \
--priority 100 \
--protocol Tcp \
--direction Inbound \
--source-address-prefixes Internet \
--source-port-ranges '*' \
--destination-address-prefixes '*' \
--destination-port-ranges 80 443 \
--access Allow
# Deny all other inbound (explicit)
az network nsg rule create \
--nsg-name NSG-Web \
--resource-group myRG \
--name Deny-All-Inbound \
--priority 4000 \
--protocol '*' \
--direction Inbound \
--source-address-prefixes '*' \
--source-port-ranges '*' \
--destination-address-prefixes '*' \
--destination-port-ranges '*' \
--access Deny
Associating NSG to a subnet:
az network vnet subnet update \
--vnet-name myVNet \
--name WebSubnet \
--resource-group myRG \
--network-security-group NSG-Web
Creating ASG:
az network asg create \
--name ASG-WebServers \
--resource-group myRG \
--location eastus
Associating VM NIC to an ASG:
az network nic update \
--name myVM-NIC \
--resource-group myRG \
--application-security-groups ASG-WebServers
Creating NSG rule with ASG as source and destination:
az network nsg rule create \
--nsg-name NSG-DB \
--resource-group myRG \
--name Allow-SQL-from-AppServers \
--priority 100 \
--protocol Tcp \
--direction Inbound \
--source-asgs ASG-AppServers \
--source-port-ranges '*' \
--destination-asgs ASG-DatabaseServers \
--destination-port-ranges 1433 \
--access Allow
Checking effective rules:
az network nic show-effective-nsg \
--name myVM-NIC \
--resource-group myRG \
--output table
6.3 Azure PowerShellβ
# Create NSG
$nsg = New-AzNetworkSecurityGroup `
-Name "NSG-Web" `
-ResourceGroupName "myRG" `
-Location "eastus"
# Create rule and add to NSG
$rule = New-AzNetworkSecurityRuleConfig `
-Name "Allow-HTTP-HTTPS" `
-Protocol Tcp `
-Direction Inbound `
-Priority 100 `
-SourceAddressPrefix Internet `
-SourcePortRange '*' `
-DestinationAddressPrefix '*' `
-DestinationPortRange 80, 443 `
-Access Allow
$nsg.SecurityRules.Add($rule)
Set-AzNetworkSecurityGroup -NetworkSecurityGroup $nsg
# Create ASG
$asg = New-AzApplicationSecurityGroup `
-Name "ASG-WebServers" `
-ResourceGroupName "myRG" `
-Location "eastus"
# Associate NIC to ASG
$nic = Get-AzNetworkInterface `
-Name "myVM-NIC" `
-ResourceGroupName "myRG"
$nic.IpConfigurations[0].ApplicationSecurityGroups = @($asg)
Set-AzNetworkInterface -NetworkInterface $nic
6.4 Bicepβ
// Application Security Groups
resource asgWeb 'Microsoft.Network/applicationSecurityGroups@2023-05-01' = {
name: 'ASG-WebServers'
location: location
}
resource asgApp 'Microsoft.Network/applicationSecurityGroups@2023-05-01' = {
name: 'ASG-AppServers'
location: location
}
resource asgDb 'Microsoft.Network/applicationSecurityGroups@2023-05-01' = {
name: 'ASG-DatabaseServers'
location: location
}
// NSG for Web tier
resource nsgWeb 'Microsoft.Network/networkSecurityGroups@2023-05-01' = {
name: 'NSG-Web'
location: location
properties: {
securityRules: [
{
name: 'Allow-HTTP-HTTPS'
properties: {
priority: 100
protocol: 'Tcp'
access: 'Allow'
direction: 'Inbound'
sourceAddressPrefix: 'Internet'
sourcePortRange: '*'
destinationAddressPrefix: '*'
destinationPortRanges: ['80', '443']
}
}
]
}
}
// NSG for Database tier with rule using ASG
resource nsgDb 'Microsoft.Network/networkSecurityGroups@2023-05-01' = {
name: 'NSG-DB'
location: location
properties: {
securityRules: [
{
name: 'Allow-SQL-from-App'
properties: {
priority: 100
protocol: 'Tcp'
access: 'Allow'
direction: 'Inbound'
sourceApplicationSecurityGroups: [{ id: asgApp.id }]
sourcePortRange: '*'
destinationApplicationSecurityGroups: [{ id: asgDb.id }]
destinationPortRange: '1433'
}
}
{
name: 'Deny-All-Inbound'
properties: {
priority: 4000
protocol: '*'
access: 'Deny'
direction: 'Inbound'
sourceAddressPrefix: '*'
sourcePortRange: '*'
destinationAddressPrefix: '*'
destinationPortRange: '*'
}
}
]
}
}
7. Control and Securityβ
7.1 Permissions to manage NSGs and ASGsβ
| Operation | Minimum Role |
|---|---|
| Create/edit NSG and rules | Network Contributor |
| Associate NSG to subnet | Network Contributor on VNet and NSG |
| Create/edit ASG | Network Contributor |
| Associate NIC to ASG | Network Contributor on NIC and ASG |
| View effective rules | Reader + Network Contributor |
7.2 Rules for Azure Load Balancerβ
A common mistake in Load Balancer environments is blocking health probes. The Load Balancer uses IP 168.63.129.16 to check if VMs are healthy. If the NSG blocks this IP, the LB marks VMs as unhealthy and stops sending traffic to them.
The default rule AllowAzureLoadBalancerInBound (priority 65001) allows this, but if you have a Deny All rule with priority lower than 65001, it overrides the default.
Always explicitly include:
az network nsg rule create \
--nsg-name NSG-Web \
--resource-group myRG \
--name Allow-LB-HealthProbe \
--priority 150 \
--protocol '*' \
--direction Inbound \
--source-address-prefixes AzureLoadBalancer \
--source-port-ranges '*' \
--destination-address-prefixes '*' \
--destination-port-ranges '*' \
--access Allow
7.3 NSG Flow Logsβ
NSG Flow Logs record information about IP traffic that passed or was blocked by NSGs. They are fundamental for auditing, forensics, and troubleshooting.
# Enable flow logs (requires Network Watcher and Storage Account)
az network watcher flow-log create \
--location eastus \
--name FlowLog-NSG-Web \
--nsg NSG-Web \
--resource-group myRG \
--storage-account mystorageaccount \
--enabled true \
--retention 7 \
--traffic-analytics true \
--workspace <log-analytics-workspace-id>
With Traffic Analytics enabled, you can visualize traffic patterns, top talkers, and blocked flows in a rich dashboard in Azure Monitor.
8. Decision Makingβ
8.1 NSG on Subnet vs NSG on NICβ
| Situation | Best choice | Reason |
|---|---|---|
| Rules that apply to all VMs in a tier | NSG on subnet | Single rule covers all VMs |
| Exception for a specific VM within the subnet | Additional NSG on NIC | Doesn't affect other VMs in the subnet |
| Simple environment with few VMs | NSG on subnet | Lower operational complexity |
| VM that needs more restrictive rules than the subnet | NSG on NIC in addition to subnet | Dual verification, more restrictive |
| Microsegmentation by workload | ASGs + NSG on subnet | Rules based on function, not IP |
8.2 IP/CIDR vs Service Tag vs ASGβ
| Situation | Use | Reason |
|---|---|---|
| Traffic from/to internet | Service Tag Internet | Covers all internet IPs |
| Communication between application tiers | ASG | Rules based on function, scales without change |
| Traffic from/to specific Azure services | Service Tag (e.g., Storage, Sql) | Microsoft maintains updated IPs |
| Specific partner/customer IP | IP/CIDR | Fixed and specific address |
| Group of VMs with same purpose | ASG | Easier to maintain than IP lists |
8.3 When to use ASGs mandatorilyβ
ASGs are especially necessary when:
- The number of VMs in the group varies frequently (scale sets, automated deployments)
- VM IPs are dynamically assigned (without static IPs)
- The same VM needs to belong to multiple security groups
- You want security rules to be defined by the security team independently of the infrastructure team managing IPs
9. Best Practicesβ
- Associate NSGs with subnets, not NICs, to simplify management. Use NSGs on NIC only for specific exceptions.
- Always include an explicit Deny All rule with high priority (e.g., 4000) instead of relying only on the default 65500 rule. This makes your intention clear and auditable.
- Use Service Tags for Azure-to-Azure traffic instead of maintaining changing IP lists.
- Use ASGs for microsegmentation instead of IPs when resources are dynamic or change frequently.
- Enable NSG Flow Logs on all production NSGs for auditing and troubleshooting.
- Don't block
AzureLoadBalancerif there's a Load Balancer in the architecture. - Number priorities with intervals (100, 200, 300) to allow insertion of new rules without reorganization.
- Name rules descriptively:
Allow-HTTPS-from-Internet,Deny-SSH-from-All,Allow-SQL-from-AppTier. - Use resource tags on NSGs to identify owner, environment, and related workload.
- Periodically review unnecessary rules using Network Watcher and Traffic Analytics to identify unused rules.
10. Common Errorsβ
| Error | Why it happens | How to avoid |
|---|---|---|
| VMs out of health in Load Balancer | Deny All rule with priority lower than 65001 blocks LB probes | Always include Allow AzureLoadBalancer with priority lower than Deny All |
| SSH/RDP blocked unexpectedly | Deny All rule overrides subnet Allow | Check priority order; use IP Flow Verify |
| ASG doesn't work between different VNets | ASGs must be in the same VNet | Use IPs/CIDRs for cross-VNet communication |
| Unwanted traffic allowed by default | AllowVnetInBound allows all intra-VNet traffic | Add explicit Deny rules for microsegmentation |
| Rule with two source types | NSG doesn't accept ASG + IP in the same rule | Separate into two distinct rules |
| NIC associated with ASG from another region | ASG and NIC must be in the same region | Create ASG in the same region as VMs |
| Flow Logs don't appear | Storage Account in different region from Network Watcher | Use Storage Account in the same region |
| Higher priority rule (lower number) blocking | Forgot that lower number = higher priority | Always review priority order after adding rules |
11. Operations and Maintenanceβ
11.1 Diagnosing connectivityβ
Step 1: Use IP Flow Verify in Network Watcher to confirm if NSG is blocking.
Step 2: If blocked, use Effective Security Rules on the NIC to see which specific rule is denying.
Step 3: If allowed by NSG but traffic still doesn't reach, the issue may be in firewall within the VM (Windows Firewall, iptables) or routing.
Step 4: Use Connection Troubleshoot in Network Watcher for end-to-end diagnosis including latency and packet loss.
11.2 Monitoring NSGs with Azure Monitorβ
# Create alert for NSG changes (via Activity Log)
az monitor activity-log alert create \
--name "NSG-rule-change-alert" \
--resource-group myRG \
--condition category=Administrative \
--condition operationName=Microsoft.Network/networkSecurityGroups/securityRules/write \
--action-group myActionGroup
Also configure alerts for:
- Blocked flows above a threshold (Traffic Analytics)
- Access attempts to sensitive ports (22, 3389) from unauthorized IPs
11.3 Important limitsβ
| Resource | Limit |
|---|---|
| NSGs per subscription | 5,000 |
| Rules per NSG | 1,000 |
| NSGs per NIC | 1 |
| NSGs per subnet | 1 |
| ASGs per subscription | 3,000 |
| ASGs per NIC | 20 |
| ASGs per NSG rule (source + destination) | 10 |
12. Integration and Automationβ
12.1 Azure Policy for NSG complianceβ
# Built-in policy: Subnets without NSG should be audited
az policy assignment create \
--name "require-nsg-on-subnets" \
--policy "e71308d3-144b-4262-b144-efdc3cc90517" \
--scope "/subscriptions/<sub-id>"
Custom policy to deny creation of subnets without NSG:
{
"if": {
"allOf": [
{
"field": "type",
"equals": "Microsoft.Network/virtualNetworks/subnets"
},
{
"field": "Microsoft.Network/virtualNetworks/subnets/networkSecurityGroup.id",
"exists": false
},
{
"not": {
"field": "name",
"in": ["GatewaySubnet", "AzureFirewallSubnet", "AzureBastionSubnet"]
}
}
]
},
"then": {
"effect": "deny"
}
}
Note that some special subnets (
GatewaySubnet,AzureFirewallSubnet,AzureBastionSubnet) don't support NSGs and must be excluded from the policy.
12.2 Terraform for NSG with ASGβ
resource "azurerm_application_security_group" "web" {
name = "ASG-WebServers"
location = var.location
resource_group_name = var.resource_group
}
resource "azurerm_application_security_group" "db" {
name = "ASG-DatabaseServers"
location = var.location
resource_group_name = var.resource_group
}
resource "azurerm_network_security_group" "db_nsg" {
name = "NSG-DB"
location = var.location
resource_group_name = var.resource_group
security_rule {
name = "Allow-SQL-from-Web"
priority = 100
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "1433"
source_application_security_group_ids = [azurerm_application_security_group.web.id]
destination_application_security_group_ids = [azurerm_application_security_group.db.id]
}
security_rule {
name = "Deny-All-Inbound"
priority = 4000
direction = "Inbound"
access = "Deny"
protocol = "*"
source_port_range = "*"
destination_port_range = "*"
source_address_prefix = "*"
destination_address_prefix = "*"
}
}
13. Final Summaryβ
Essential concepts:
- An NSG is a list of network security rules that control inbound and outbound traffic of Azure resources.
- Each rule has priority, protocol, source, destination, port, and action (Allow/Deny). Lower priority number = higher precedence.
- NSGs can be associated with subnets (protects all VMs) or NICs (protects a specific VM).
- When both are associated, both are evaluated: for inbound, subnet NSG first; for outbound, NIC NSG first.
- An ASG is a logical grouping of NICs that can be used as source or destination in NSG rules, eliminating the need to maintain IP lists.
Critical differences:
- NSG on subnet vs NSG on NIC: Subnet covers all VMs; NIC covers only one. Both coexist and both must allow for traffic to pass.
- Allow vs Deny: The first matching rule (by priority) is applied. The rest are ignored.
- ASG vs Service Tag vs IP: ASG is for internal Azure resources grouped by function. Service Tag is for Azure services with Microsoft-managed IPs. IP/CIDR is for specific external addresses.
- Default AllowVnetInBound: By default, all traffic between VMs in the same VNet is allowed. For microsegmentation, add explicit Deny rules.
What needs to be remembered:
- NSGs on
GatewaySubnet,AzureFirewallSubnet, andAzureBastionSubnetare not supported. - Always include
Allow AzureLoadBalancerbefore any Deny All rule when there's a Load Balancer. - ASGs must be in the same VNet as associated NICs.
- A NIC can belong to up to 20 ASGs.
- Use
IP Flow VerifyandEffective Security Rulesto diagnose connectivity issues. - NSG Flow Logs are essential for auditing and must be enabled in production.
- Default rules (65000, 65001, 65500) cannot be deleted, only overridden with lower priorities.