Networking¶
This guide explains every networking component deployed for AVD on Azure Local — exactly what NSG rules are created and why each one exists, how private endpoints work for AVD control plane isolation, how DNS resolution ties it together, and how each IaC tool implements the resources.
Why Networking Configuration Matters¶
AVD session hosts are Arc-enabled VMs sitting in your Azure Local logical network. They need to talk to several Azure services over the internet to function:
- The AVD broker (to register themselves, receive connection assignments, send heartbeats)
- Entra ID (to validate user tokens for Hybrid Join)
- Azure Monitor (to ship diagnostics and performance counters)
- Windows KMS (to activate the Windows license)
Without proper NSG rules, session hosts can't communicate with these services and AVD breaks. With private endpoints, you can move the AVD broker and workspace traffic off the public internet entirely, keeping it on the Microsoft backbone network — important for compliance and security-sensitive environments.

Open the draw.io source for an editable version.
What Gets Deployed¶
| Azure Resource Type | Resource Name | What It Is |
|---|---|---|
Microsoft.Network/networkSecurityGroups |
${nsg_name} |
Network Security Group attached to the session host subnet. Contains 4 outbound allow rules for AVD-required traffic. |
Microsoft.Network/networkSecurityGroups/securityRules |
4 inline rules (priorities 100-130) | Individual NSG rules allowing outbound HTTPS to Azure service tags + KMS activation. |
Microsoft.Network/privateEndpoints |
${host_pool_name}-pe |
Private endpoint for the AVD Host Pool. Creates a NIC in the PE subnet (in the Azure VNet, not the Azure Local logical network) with a private IP that routes to the AVD connection sub-resource. |
Microsoft.Network/privateEndpoints |
${workspace_name}-pe |
Private endpoint for the AVD Workspace. Creates a NIC in the PE subnet with a private IP that routes to the AVD feed sub-resource. |
Microsoft.Network/privateEndpoints |
${global_workspace_name}-global-pe |
Private endpoint for initial feed discovery. Creates a NIC in the PE subnet with a private IP that routes to the AVD global sub-resource. Only ONE needed across all AVD deployments. |
Microsoft.Network/privateEndpoints/privateDnsZoneGroups |
Auto-created | Links connection and feed PEs to privatelink.wvd.microsoft.com. Links global PE to privatelink-global.wvd.microsoft.com. Automatically creates A records so domain names resolve to private IPs. |
NSG Rules — Every Rule Explained¶
The NSG has 4 outbound rules, each targeting a specific Azure service tag. Service tags are Microsoft-managed IP address groups that automatically update as Azure adds or changes IP ranges.
Rule 1: Allow-AVD-Service (Priority 100)¶
| Property | Value |
|---|---|
| Direction | Outbound |
| Priority | 100 |
| Protocol | TCP |
| Source | * (any address in the subnet) |
| Destination | Service Tag: WindowsVirtualDesktop |
| Destination Port | 443 (HTTPS) |
| Action | Allow |
Why it exists: Session hosts communicate with the AVD control plane over HTTPS 443. This includes:
- Agent registration — When a session host boots, the AVD agent (
RDAgentBootLoader) contactsrdbroker.wvd.microsoft.comto register itself in the host pool - Heartbeats — Every 30 seconds, the agent sends a heartbeat. If 3 consecutive heartbeats are missed, the host shows as "Unavailable" in the portal
- Reverse connect — When a user connects, the broker tells the session host to establish a reverse-connect WebSocket tunnel through the AVD gateway. This tunnel carries the RDP traffic; no inbound ports needed on the session host
- Session orchestration — Load balancing decisions, drain mode signals, and scaling plan commands all flow through this channel
The WindowsVirtualDesktop service tag resolves to ~200 IP ranges across Azure regions. Microsoft maintains it — you don't need to update it manually.
Rule 2: Allow-AzureMonitor (Priority 110)¶
| Property | Value |
|---|---|
| Direction | Outbound |
| Priority | 110 |
| Protocol | TCP |
| Source | * |
| Destination | Service Tag: AzureMonitor |
| Destination Port | 443 |
| Action | Allow |
Why it exists: If monitoring is enabled, session hosts ship telemetry to Azure Monitor / Log Analytics:
- Performance counters — CPU, memory, disk, network via the Azure Monitor Agent (AMA) or legacy Log Analytics agent (MMA)
- Windows Event Logs — Application, System, and AVD-specific event channels (
Microsoft-Windows-TerminalServices-*) - AVD Insights data — Connection quality, session duration, round-trip time
Without this rule, monitoring data never reaches Log Analytics and your AVD Insights workbook shows blank.
Rule 3: Allow-AzureAD (Priority 120)¶
| Property | Value |
|---|---|
| Direction | Outbound |
| Priority | 120 |
| Protocol | TCP |
| Source | * |
| Destination | Service Tag: AzureActiveDirectory |
| Destination Port | 443 |
| Action | Allow |
Why it exists: Required for Entra ID authentication in all identity strategies:
- AD-Only — Even though session hosts don't use Entra ID for login, the AVD agent still needs Entra ID to validate the broker connection token
- Hybrid Join — The
AADLoginForWindowsextension communicates withlogin.microsoftonline.comanddevice.login.microsoftonline.comfor device registration and PRT-based SSO. Azure AD Connect sync validation is also required.
Note: Entra-only join is NOT supported on Azure Local. Only
ad_onlyandhybrid_joinstrategies are available.
The AzureActiveDirectory service tag covers login.microsoftonline.com, graph.microsoft.com, device.login.microsoftonline.com, and related endpoints.
Rule 4: Allow-KMS (Priority 130)¶
| Property | Value |
|---|---|
| Direction | Outbound |
| Priority | 130 |
| Protocol | TCP |
| Source | * |
| Destination | Service Tag: Internet |
| Destination Port | 1688 |
| Action | Allow |
Why it exists: Windows VMs must activate their license via the Azure KMS server at azkms.core.windows.net:1688 (or kms.core.windows.net:1688). If activation fails:
- Windows shows "Activate Windows" watermark on the desktop
- After 30 days, the VM enters reduced-functionality mode
- Some Windows features (personalization, certain Group Policy settings) stop working
This rule uses the Internet service tag with a specific port (1688) rather than a KMS-specific tag because there is no dedicated Azure KMS service tag. Port 1688 is exclusively used by KMS.
Default Deny¶
The NSG's built-in default rules deny all other outbound traffic not explicitly allowed. This means session hosts cannot reach arbitrary internet endpoints — only the four service tags above. If you need additional outbound access (e.g., for application downloads, Windows Update), add rules with priorities above 130.
Private Endpoints — Deep Dive¶
Important — Azure Local Hybrid Architecture: Private endpoints for AVD on Azure Local are an advanced hybrid networking scenario. Unlike standard Azure deployments where session hosts and PEs share the same VNet, on Azure Local the session hosts are on-premises while the private endpoints live in an Azure VNet. This requires ExpressRoute or Site-to-Site VPN connectivity between environments. Most Azure Local AVD deployments work well with the default public endpoints (TLS 1.2 encrypted). Only enable private endpoints if your compliance or security requirements demand it.
What Private Endpoints Do¶
By default, AVD session hosts communicate with the AVD control plane over public endpoints (rdbroker.wvd.microsoft.com, rdweb.wvd.microsoft.com). This traffic goes over the public internet, encrypted via TLS 1.2.
Private endpoints create network interfaces in an Azure Virtual Network with private IP addresses that route to the AVD service. When DNS is configured correctly, the FQDN rdbroker.wvd.microsoft.com resolves to a private IP (e.g., 10.1.2.4) instead of a public IP. All AVD control plane traffic stays on the Microsoft backbone — it never touches the public internet.
Azure Local distinction: On Azure Local, session hosts sit on an on-premises logical network (
AzureStackHCI/logicalNetworks), NOT in an Azure VNet. The private endpoints are deployed into a separate Azure VNet in an Azure region. For on-premises session hosts to reach the private endpoint IPs, you must have ExpressRoute or Site-to-Site VPN connectivity between the Azure Local site and the Azure VNet hosting the PEs. See Azure Local firewall requirements — Private Endpoints for Microsoft's confirmation of this architecture.
Prerequisites for Private Endpoints on Azure Local¶
Before enabling private endpoints, you must have the following infrastructure already deployed:
| Prerequisite | What It Is | Why It's Needed |
|---|---|---|
| Azure VNet | A Virtual Network in an Azure region (e.g., vnet-hub-eastus) |
Private endpoint NICs are created here — they cannot be created in an Azure Local logical network. |
| PE Subnet | A dedicated subnet in the Azure VNet (e.g., snet-pe, /28 minimum) |
Houses the 3 private endpoint NICs. Must have enough IPs (minimum 3). |
| ExpressRoute or Site-to-Site VPN | Hybrid connectivity between your Azure Local site and the Azure VNet | On-premises session hosts need Layer 3 reachability to the PE private IPs in the Azure VNet. |
| Azure DNS Private Resolver | A DNS resolver deployed in the Azure VNet | On-premises DNS servers forward private DNS zone queries to this resolver. 168.63.129.16 is only reachable from within the Azure VNet. |
| Private DNS Zones (2) | privatelink.wvd.microsoft.com AND privatelink-global.wvd.microsoft.com |
Each zone hosts A records for different PE sub-resources. Both must be linked to the Azure VNet. |
Three Private Endpoints, Three Sub-Resources¶
AVD Private Link requires three separate private endpoints with distinct sub-resources:
| Private Endpoint | Sub-Resource | DNS Zone | What It Handles |
|---|---|---|---|
| Host Pool PE | connection |
privatelink.wvd.microsoft.com |
Agent registration, heartbeats, reverse-connect tunnels, session orchestration. This is the session host → broker communication. |
| Workspace PE | feed |
privatelink.wvd.microsoft.com |
The AVD client feed — when a user opens the AVD client and sees their list of desktops/apps, that request goes to the workspace. This is the client → workspace communication. |
| Global PE | global |
privatelink-global.wvd.microsoft.com |
Initial feed discovery — when the AVD client first subscribes, it queries rdweb.wvd.microsoft.com to discover all workspaces. You only need ONE global PE across your entire AVD deployment. Use a dedicated placeholder workspace for this. |
Important: The
globalPE uses a separate DNS zone (privatelink-global.wvd.microsoft.com). Since September 2023, sharing the same DNS zone forglobaland other sub-resources is no longer supported. See Microsoft docs.Important: You cannot control access to the workspace used for the
globalsub-resource. Even if you configure it for private-only access, it remains publicly accessible. Create a separate empty workspace (no application groups) solely for the global PE.
DNS Zone Configuration¶
For private endpoints to work, DNS resolution must return the private IP instead of the public IP. This requires two private DNS zones and a DNS forwarding chain:
- Private DNS Zone 1:
privatelink.wvd.microsoft.com— forconnectionandfeedsub-resources - Private DNS Zone 2:
privatelink-global.wvd.microsoft.com— for theglobalsub-resource - A Records: Automatically created by the
privateDnsZoneGroupwhen each PE is deployed - VNet Link: Both DNS zones must be linked to the Azure VNet containing the PE subnet
- DNS Forwarding Chain (Azure Local specific):
On-prem session host → query: rdbroker.wvd.microsoft.com
→ On-prem DNS server (e.g., AD DC at 10.0.1.10)
→ Conditional forwarder: *.wvd.microsoft.com → Azure DNS Private Resolver (e.g., 10.1.0.4)
→ Azure DNS Private Resolver → Azure Private DNS Zone
→ CNAME: rdbroker.wvd.microsoft.com → rdbroker.privatelink.wvd.microsoft.com
→ A record: rdbroker.privatelink.wvd.microsoft.com → 10.1.2.4 (PE NIC in Azure VNet)
→ Traffic routes: on-prem → ExpressRoute/VPN → Azure VNet → PE NIC → AVD service
Key difference from standard Azure: In a standard Azure deployment, session hosts use Azure DNS (
168.63.129.16) directly because they're in the same VNet. On Azure Local,168.63.129.16is not reachable from on-premises — you must deploy an Azure DNS Private Resolver in the Azure VNet and configure your on-prem DNS servers to forward to it.
Subnet Design¶
Private endpoints need their own subnet in the Azure VNet (not in the Azure Local logical network):
| Property | Recommended Value |
|---|---|
| Subnet Size | /28 (14 usable IPs) — enough for 3 PEs plus room for future growth |
| Subnet Name | snet-pe or snet-private-endpoints |
| Subnet Location | In the Azure VNet that has ExpressRoute/VPN connectivity to Azure Local |
| NSG | Optional on PE subnet (PEs don't need outbound rules — they're the target, not the source) |
| Service Endpoints | Not needed — PEs are different from service endpoints |
Configuration — Every Field Explained¶
networking:
private_endpoints:
enabled: false # Whether to deploy private endpoints for the host pool, workspace, and global feed.
# If false, AVD uses public endpoints (still encrypted via TLS 1.2).
# If true, requires:
# - An Azure VNet with a dedicated PE subnet (NOT the Azure Local logical network)
# - ExpressRoute or Site-to-Site VPN from on-premises to the Azure VNet
# - Azure DNS Private Resolver in the Azure VNet
# - Two private DNS zones (see dns_zone_id and global_dns_zone_id)
subnet_id: "/subscriptions/.../subnets/pe-subnet"
# Full resource ID of the subnet IN AN AZURE VNET where private endpoint NICs are created.
# IMPORTANT: This must be an Azure VNet subnet, NOT an Azure Local logical network subnet.
# The subnet must have enough IP addresses (at least 3 for host pool + workspace + global PEs).
dns_zone_id: "" # Full resource ID of the privatelink.wvd.microsoft.com DNS zone.
# Used for host pool (connection) and workspace (feed) PEs.
# If empty, the private endpoint is created without DNS zone group —
# you must create A records manually or via Azure Policy.
global_dns_zone_id: "" # Full resource ID of the privatelink-global.wvd.microsoft.com DNS zone.
# Used for the global (initial feed discovery) PE.
# This is a SEPARATE zone from dns_zone_id — required since September 2023.
global_workspace_id: "" # Full resource ID of a dedicated placeholder workspace for the global PE.
# This workspace should have NO application groups registered.
# Only ONE global PE is needed across your entire AVD deployment.
nsg:
enabled: true # Whether to create the NSG with AVD outbound rules.
# If false, no NSG is deployed (assumes you manage NSG externally).
name: "hp-pool01-nsg" # Name of the NSG resource. Must be unique within the resource group.
# The NSG is created but NOT automatically associated with a subnet —
# you must attach it to the session host subnet via subnet properties
# or a separate association resource.
What Each IaC Tool Deploys — Resource by Resource¶
Terraform (src/terraform/networking.tf)¶
| Terraform Resource | Azure Resource Created | Condition | What It Does |
|---|---|---|---|
azurerm_network_security_group.avd_nsg[0] |
NSG | var.nsg_enabled == true |
Creates the NSG with 4 inline security_rule blocks (priorities 100-130). Each rule uses a service tag destination. |
azurerm_private_endpoint.host_pool[0] |
Host Pool Private Endpoint | var.private_endpoints_enabled == true |
Creates PE in var.private_endpoint_subnet_id (must be an Azure VNet subnet) with private_service_connection targeting the host pool resource and sub-resource connection. |
azurerm_private_endpoint.workspace[0] |
Workspace Private Endpoint | var.private_endpoints_enabled == true |
Creates PE targeting the workspace resource and sub-resource feed. |
azurerm_private_endpoint.global[0] |
Global Feed Discovery PE | var.private_endpoints_enabled == true |
Creates PE targeting a dedicated placeholder workspace and sub-resource global. Only one needed across all AVD deployments. |
(inline) private_dns_zone_group block |
DNS Zone Group | var.private_dns_zone_id != "" |
Inside connection and feed PEs — links to privatelink.wvd.microsoft.com. |
(inline) private_dns_zone_group block |
Global DNS Zone Group | var.private_dns_global_zone_id != "" |
Inside global PE — links to privatelink-global.wvd.microsoft.com. |
Terraform variables:
nsg_enabled = true
nsg_name = "hp-pool01-nsg"
private_endpoints_enabled = true
private_endpoint_subnet_id = "/subscriptions/.../subnets/snet-pe" # Must be an Azure VNet subnet
private_dns_zone_id = "/subscriptions/.../privateDnsZones/privatelink.wvd.microsoft.com"
private_dns_global_zone_id = "/subscriptions/.../privateDnsZones/privatelink-global.wvd.microsoft.com"
global_workspace_id = "/subscriptions/.../workspaces/ws-global-pe"
Bicep (src/bicep/networking.bicep)¶
Same resources implemented in Bicep:
| Bicep Resource | ARM Type | Notes |
|---|---|---|
nsg resource |
Microsoft.Network/networkSecurityGroups@2023-05-01 |
Contains securityRules array with 4 rules. Same service tags and priorities as Terraform. |
hostPoolPe resource |
Microsoft.Network/privateEndpoints@2023-05-01 |
privateLinkServiceConnections array with groupIds: ['connection']. Subnet must be in an Azure VNet. |
workspacePe resource |
Microsoft.Network/privateEndpoints@2023-05-01 |
privateLinkServiceConnections array with groupIds: ['feed']. |
globalPe resource |
Microsoft.Network/privateEndpoints@2023-05-01 |
privateLinkServiceConnections array with groupIds: ['global']. Only one needed across all AVD deployments. |
hostPoolPeDnsGroup child |
Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2023-05-01 |
Links connection PE to privatelink.wvd.microsoft.com. |
workspacePeDnsGroup child |
Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2023-05-01 |
Links feed PE to privatelink.wvd.microsoft.com. |
globalPeDnsGroup child |
Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2023-05-01 |
Links global PE to privatelink-global.wvd.microsoft.com. |
az deployment group create \
--resource-group rg-avd-prod \
--template-file src/bicep/networking.bicep \
--parameters nsgName='hp-pool01-nsg' \
nsgEnabled=true \
privateEndpointsEnabled=true \
privateEndpointSubnetId='/subscriptions/.../subnets/snet-pe' \
hostPoolId='<host-pool-resource-id>' \
workspaceId='<workspace-resource-id>' \
privateDnsZoneId='/subscriptions/.../privateDnsZones/privatelink.wvd.microsoft.com' \
globalWorkspaceId='<global-workspace-resource-id>' \
privateDnsGlobalZoneId='/subscriptions/.../privateDnsZones/privatelink-global.wvd.microsoft.com'
PowerShell (src/powershell/Configure-AVDNetworking.ps1)¶
The PowerShell script runs these steps in order:
- Loads configuration from YAML
- If
nsg.enabled: Creates NSG viaNew-AzNetworkSecurityGroup - Adds 4 rules via
Add-AzNetworkSecurityRuleConfig— same service tags and priorities - Calls
Set-AzNetworkSecurityGroupto apply the rules - If
private_endpoints.enabled: Creates host pool PE viaNew-AzPrivateEndpointwith-GroupId "connection" - Creates workspace PE with
-GroupId "feed" - Creates global PE with
-GroupId "global"targeting a dedicated placeholder workspace - If DNS zones are specified: creates DNS zone groups via
New-AzPrivateDnsZoneGroup— one forprivatelink.wvd.microsoft.com(connection + feed) and one forprivatelink-global.wvd.microsoft.com(global)
Ansible (src/ansible/roles/avd-networking/tasks/main.yml)¶
Uses azure_rm_securitygroup for the NSG, azure_rm_resource for private endpoints. Tagged as networking.
Troubleshooting¶
| Symptom | Root Cause | Resolution |
|---|---|---|
| Session hosts show "Unavailable" in host pool | NSG blocks outbound 443 to WindowsVirtualDesktop service tag |
Verify NSG rule with priority 100 exists. Test: Test-NetConnection rdbroker.wvd.microsoft.com -Port 443 from session host. |
| AVD Insights workbook shows no data | NSG blocks outbound 443 to AzureMonitor service tag, or monitoring agent not installed |
Verify rule priority 110. Check if AMA/MMA agent is running on session hosts. |
| Hybrid Join fails — "Unable to register device" | NSG blocks outbound 443 to AzureActiveDirectory service tag |
Verify rule priority 120. Test: Test-NetConnection login.microsoftonline.com -Port 443 |
| Windows "Activate Windows" watermark | NSG blocks outbound 1688 to KMS server | Verify rule priority 130. Test: Test-NetConnection azkms.core.windows.net -Port 1688 |
| Private endpoint deployed but FQDN still resolves to public IP | DNS zone group not created, DNS forwarding not configured, or Azure DNS Private Resolver not deployed | Check: nslookup rdbroker.wvd.microsoft.com from session host — should return 10.x.x.x (private IP). If it returns a public IP: (1) verify the privateDnsZoneGroup resource exists, (2) verify both DNS zones are linked to the Azure VNet, (3) verify on-prem DNS forwards *.wvd.microsoft.com to the Azure DNS Private Resolver IP. |
| Users can't see desktops in AVD client after enabling PE | Global PE missing, workspace PE missing, or feed/global sub-resources not configured |
Verify all 3 PEs exist — host pool (connection), workspace (feed), AND global (global). The global PE is required for initial feed discovery. |
| PE deployed but session hosts cannot reach PE IPs | No ExpressRoute/VPN between Azure Local and Azure VNet | On Azure Local, session hosts are on-premises. They need Layer 3 connectivity (ER or S2S VPN) to the Azure VNet hosting the PEs. Verify with Test-NetConnection 10.1.2.4 -Port 443 from a session host. |