How to Set Up Windows Server 2016 Network Security Groups
Network Security Groups (NSGs) in Windows Server 2016 SDN provide a layer of network access control that filters traffic to and from resources in a virtual network. Similar in concept to Azure NSGs, Windows Server 2016 NSGs are implemented as Access Control Lists (ACLs) managed by the SDN Network Controller and enforced at the hypervisor level by the Distributed Firewall. NSGs allow you to define granular inbound and outbound security rules that control which traffic is permitted based on protocol, port, and source or destination address prefix.
This tutorial covers the complete process of creating Network Security Groups on Windows Server 2016 SDN, defining security rules, applying NSGs to virtual network interfaces and subnets, and validating that the rules are correctly enforced.
NSG Architecture in Windows Server 2016 SDN
In Windows Server 2016, an NSG is represented as an Access Control List (ACL) resource in the Network Controller. Each ACL consists of an ordered set of rules that are evaluated in priority order. Rules can allow or deny traffic, and each rule specifies a protocol, source and destination port ranges, and source and destination address prefixes. The Network Controller distributes these ACL policies to the Virtual Filtering Platform (VFP) in the Hyper-V Virtual Switch on each host. VFP enforces the rules at line rate for every packet entering or leaving a VM’s network adapter.
NSG Rule Evaluation
Rules within an NSG are evaluated in ascending priority order — the lowest priority number is evaluated first. The first rule that matches a packet determines the action taken. If no rule matches, a default deny rule is applied (unless you explicitly include a default allow rule with a high priority number). Rules can be applied at both the subnet level and the individual NIC level, and NIC-level rules take precedence over subnet-level rules for the specific interface.
Prerequisites
Ensure your Windows Server 2016 SDN environment is running with the Network Controller operational and at least one virtual network configured. The SDN Host Agent must be installed and running on all Hyper-V hosts. Verify connectivity to the Network Controller:
$uri = "https://nc.contoso.com"
Invoke-RestMethod -Uri "$uri/networking/v1/accessControlLists" -UseDefaultCredentials
Step 1 — Define an NSG for a Web Tier
Create an NSG that represents the security policy for a web server tier. This NSG allows HTTP, HTTPS, and SSH inbound, permits all outbound traffic, and denies everything else inbound:
$headers = @{ "Content-Type" = "application/json" }
$webNsgBody = @{
properties = @{
aclRules = @(
@{
resourceId = "Allow-HTTP-In"
properties = @{
priority = 100
protocol = "TCP"
sourcePortRange = "0-65535"
destinationPortRange = "80"
sourceAddressPrefix = "*"
destinationAddressPrefix = "*"
action = "Allow"
type = "Inbound"
logging = "Enabled"
}
},
@{
resourceId = "Allow-HTTPS-In"
properties = @{
priority = 110
protocol = "TCP"
sourcePortRange = "0-65535"
destinationPortRange = "443"
sourceAddressPrefix = "*"
destinationAddressPrefix = "*"
action = "Allow"
type = "Inbound"
logging = "Enabled"
}
},
@{
resourceId = "Allow-SSH-In"
properties = @{
priority = 120
protocol = "TCP"
sourcePortRange = "0-65535"
destinationPortRange = "22"
sourceAddressPrefix = "10.0.0.0/8"
destinationAddressPrefix = "*"
action = "Allow"
type = "Inbound"
logging = "Enabled"
}
},
@{
resourceId = "Deny-All-Inbound"
properties = @{
priority = 65500
protocol = "All"
sourcePortRange = "0-65535"
destinationPortRange = "0-65535"
sourceAddressPrefix = "*"
destinationAddressPrefix = "*"
action = "Deny"
type = "Inbound"
logging = "Enabled"
}
},
@{
resourceId = "Allow-All-Outbound"
properties = @{
priority = 100
protocol = "All"
sourcePortRange = "0-65535"
destinationPortRange = "0-65535"
sourceAddressPrefix = "*"
destinationAddressPrefix = "*"
action = "Allow"
type = "Outbound"
logging = "Disabled"
}
}
)
}
} | ConvertTo-Json -Depth 7
Invoke-RestMethod `
-Uri "$uri/networking/v1/accessControlLists/WebTier-NSG" `
-Method Put `
-Body $webNsgBody `
-Headers $headers `
-UseDefaultCredentials
Step 2 — Define an NSG for the Database Tier
Create a separate NSG for database servers that only allows connections from the web tier’s subnet:
$dbNsgBody = @{
properties = @{
aclRules = @(
@{
resourceId = "Allow-SQL-From-Web"
properties = @{
priority = 100
protocol = "TCP"
sourcePortRange = "0-65535"
destinationPortRange = "1433"
sourceAddressPrefix = "10.100.1.0/24"
destinationAddressPrefix = "*"
action = "Allow"
type = "Inbound"
logging = "Enabled"
}
},
@{
resourceId = "Deny-All-Inbound-DB"
properties = @{
priority = 65500
protocol = "All"
sourcePortRange = "0-65535"
destinationPortRange = "0-65535"
sourceAddressPrefix = "*"
destinationAddressPrefix = "*"
action = "Deny"
type = "Inbound"
logging = "Enabled"
}
}
)
}
} | ConvertTo-Json -Depth 7
Invoke-RestMethod `
-Uri "$uri/networking/v1/accessControlLists/DbTier-NSG" `
-Method Put `
-Body $dbNsgBody `
-Headers $headers `
-UseDefaultCredentials
Step 3 — Apply NSGs to Subnets
Apply the web tier NSG to the web subnet and the database NSG to the database subnet:
$webSubnetUpdate = @{
properties = @{
addressPrefix = "10.100.1.0/24"
accessControlList = @{ resourceRef = "/accessControlLists/WebTier-NSG" }
}
} | ConvertTo-Json -Depth 4
Invoke-RestMethod `
-Uri "$uri/networking/v1/virtualNetworks/TenantAVNet/subnets/WebSubnet" `
-Method Put `
-Body $webSubnetUpdate `
-Headers $headers `
-UseDefaultCredentials
$dbSubnetUpdate = @{
properties = @{
addressPrefix = "10.100.2.0/24"
accessControlList = @{ resourceRef = "/accessControlLists/DbTier-NSG" }
}
} | ConvertTo-Json -Depth 4
Invoke-RestMethod `
-Uri "$uri/networking/v1/virtualNetworks/TenantAVNet/subnets/DbSubnet" `
-Method Put `
-Body $dbSubnetUpdate `
-Headers $headers `
-UseDefaultCredentials
Step 4 — Override NSG on a Specific VM NIC
Apply a more restrictive NSG to a specific VM that needs individual policy (for example, a management jump server):
$jumpNsgBody = @{
properties = @{
aclRules = @(@{
resourceId = "Allow-RDP-Admin"
properties = @{
priority = 100
protocol = "TCP"
sourcePortRange = "0-65535"
destinationPortRange = "3389"
sourceAddressPrefix = "172.16.0.0/12"
destinationAddressPrefix = "*"
action = "Allow"
type = "Inbound"
logging = "Enabled"
}
})
}
} | ConvertTo-Json -Depth 6
Invoke-RestMethod `
-Uri "$uri/networking/v1/accessControlLists/JumpServer-NSG" `
-Method Put `
-Body $jumpNsgBody `
-Headers $headers `
-UseDefaultCredentials
Step 5 — Verify NSG Rule Distribution
Confirm the Network Controller has distributed the NSG policies to the Hyper-V hosts:
Invoke-RestMethod `
-Uri "$uri/networking/v1/accessControlLists/WebTier-NSG" `
-Method Get `
-UseDefaultCredentials | ConvertTo-Json -Depth 5
On a Hyper-V host, validate the rules in the VFP extension:
vfpctrl /port /list-rule
Step 6 — Test NSG Enforcement
From a host outside the tenant network, test that allowed ports are accessible and blocked ports are denied:
Test-NetConnection -ComputerName 10.100.1.11 -Port 443
Test-NetConnection -ComputerName 10.100.2.5 -Port 1433
The HTTPS test should succeed. The SQL test from outside the web subnet should fail due to the database NSG blocking the source address.
Conclusion
Network Security Groups on Windows Server 2016 SDN provide a scalable, consistent, and centrally managed approach to network security that follows workloads wherever they run in the datacenter. By applying NSGs at both the subnet and NIC level through the Network Controller, administrators can implement defence-in-depth micro-segmentation strategies that significantly reduce the attack surface within the virtual network fabric. Combined with the Distributed Firewall’s hardware-accelerated enforcement in the Hyper-V Virtual Switch, NSGs deliver enterprise-grade security without sacrificing performance.