Networking
Azure Private Endpoints and hybrid DNS without breaking name resolution
A practical runbook-style article on Private Endpoints, private DNS zones, hybrid resolution, validation commands, and the failure patterns that create NXDOMAIN and misleading network diagnostics.
Private Endpoints look straightforward until the first application still resolves a public IP, an on premises DNS server returns the wrong answer, or a private DNS zone starts returning NXDOMAIN for a resource that was never meant to be private. The Private Endpoint itself is often healthy. The failure usually sits in the DNS path around it.
This article focuses on the operational path that matters in production. The objective is not just to create a Private Endpoint. The objective is to make the name resolve correctly from Azure and, when required, from on premises networks without creating contradictory DNS behavior.
What has to work
A safe design needs all of the following to be true at the same time.
The target Azure service keeps its standard FQDN.
Public DNS returns a CNAME that leads toward the privatelink name.
The right private DNS zone exists and is linked to the right virtual networks.
Clients that must resolve privately can reach a resolver path that knows the private zone.
Clients that must resolve publicly do not get trapped by an incomplete private zone.
Example target
The examples below use Azure SQL Database and Storage because they expose the two patterns that break most often.
Service: Azure SQL Database
Public name: appdb.database.windows.net
Private zone: privatelink.database.windows.net
Service: Azure Storage blob
Public name: mystorage.blob.core.windows.net
Private zone: privatelink.blob.core.windows.net Create the private DNS zones
Create the private zones first and link them to the virtual network that hosts the workloads which must resolve the private IP.
RG_NET="rg-network-prod"
LOCATION="westeurope"
VNET_NAME="vnet-hub-prod"
az network private-dns zone create --resource-group $RG_NET --name privatelink.database.windows.net
az network private-dns zone create --resource-group $RG_NET --name privatelink.blob.core.windows.net
az network private-dns link vnet create --resource-group $RG_NET --zone-name privatelink.database.windows.net --name sql-zone-link-hub --virtual-network $VNET_NAME --registration-enabled false
az network private-dns link vnet create --resource-group $RG_NET --zone-name privatelink.blob.core.windows.net --name blob-zone-link-hub --virtual-network $VNET_NAME --registration-enabled false Do not create a single catch all zone and hope the rest will sort itself out. Azure documents recommended private zone names per supported service, and those names matter for the public CNAME chain to resolve correctly. citeturn359180search1
Create the Private Endpoint for SQL
The safest sequence is zone first, endpoint second, validation third.
RG_APP="rg-app-prod"
SQL_SERVER_ID="/subscriptions/<sub-id>/resourceGroups/rg-data-prod/providers/Microsoft.Sql/servers/sql-prod-01"
PE_SUBNET_ID="/subscriptions/<sub-id>/resourceGroups/rg-network-prod/providers/Microsoft.Network/virtualNetworks/vnet-hub-prod/subnets/snet-private-endpoints"
az network private-endpoint create --name pe-sql-prod-01 --resource-group $RG_APP --location westeurope --subnet $PE_SUBNET_ID --private-connection-resource-id $SQL_SERVER_ID --group-id sqlServer --connection-name pe-sql-prod-01-conn
az network private-endpoint dns-zone-group create --resource-group $RG_APP --endpoint-name pe-sql-prod-01 --name default --private-dns-zone privatelink.database.windows.net --zone-name privatelink.database.windows.net The DNS zone group matters. If the endpoint exists but the zone association is missing or wrong, the network object can look correct while clients still resolve the public path.
Validate the DNS records in Azure
Check the private DNS record set and the IP assigned to the endpoint.
az network private-endpoint show --resource-group $RG_APP --name pe-sql-prod-01 --query 'customDnsConfigs[].{fqdn:fqdn,ipAddresses:ipAddresses}' --output table
az network private-dns record-set a list --resource-group $RG_NET --zone-name privatelink.database.windows.net --output table If the endpoint is healthy, you should see the SQL server name mapped to a private IP in the privatelink.database.windows.net zone.
Validate from a workload inside Azure
Run the resolution test from a VM or container that uses the intended DNS path, not from your laptop.
nslookup sql-prod-01.database.windows.net
nslookup sql-prod-01.privatelink.database.windows.net
dig +short sql-prod-01.database.windows.net
dig +short sql-prod-01.privatelink.database.windows.net
Test-NetConnection sql-prod-01.database.windows.net -Port 1433 The expected result is not just that the privatelink name resolves. The public service name must resolve through the CNAME chain to the private IP for the clients that should stay private. Microsoft documents that the public DNS name remains stable and resolves through a CNAME to the private domain name. citeturn359180search1
Hybrid DNS with on premises forwarders
If on premises clients must resolve the private endpoint, they need a path toward Azure for the privatelink zone. The clean design is usually:
on premises DNS server
forwards privatelink.database.windows.net and other required private zones
toward an Azure DNS Private Resolver inbound endpoint or another controlled Azure resolver path
The Azure Private Resolver pattern is the Microsoft-recommended way to avoid hosting and managing custom DNS VMs just for this purpose. citeturn359180search5turn359180search10turn359180search8
Example rule on a Windows DNS server:
Add-DnsServerConditionalForwarderZone -Name "privatelink.database.windows.net" -MasterServers 10.10.0.4 -ReplicationScope "Forest"
Add-DnsServerConditionalForwarderZone -Name "privatelink.blob.core.windows.net" -MasterServers 10.10.0.4 -ReplicationScope "Forest" Example validation from an on premises host:
Resolve-DnsName sql-prod-01.database.windows.net
Resolve-DnsName sql-prod-01.privatelink.database.windows.net
Test-NetConnection sql-prod-01.database.windows.net -Port 1433 Storage is where many designs drift
Storage exposes separate subresources. Blob, file, queue, and table do not share one universal private zone. If you create a Private Endpoint only for blob, that does not make the file endpoint private. Microsoft documents separate zone names by subresource and calls this out explicitly. citeturn359180search1
Blob -> privatelink.blob.core.windows.net
File -> privatelink.file.core.windows.net
Queue -> privatelink.queue.core.windows.net
Table -> privatelink.table.core.windows.net Treat each subresource as its own DNS and connectivity decision.
The NXDOMAIN trap
One of the most misleading failure modes is a private DNS zone linked into a network where queries for a public resource of the same type now hit the private zone first. If there is no corresponding private record, the zone can answer NXDOMAIN instead of allowing a useful public resolution path. Microsoft explicitly warns about this and documents fallback design options. citeturn359180search1
Typical symptom:
nslookup somepublicstorage.blob.core.windows.net
*** dns.corp.local can't find somepublicstorage.blob.core.windows.net: Non-existent domain What usually caused it:
A private zone such as privatelink.blob.core.windows.net was introduced for one storage account, then linked too broadly, and no fallback strategy was implemented for other storage accounts that still resolve publicly.
Checks that should exist in every change window
az network private-endpoint show --resource-group $RG_APP --name pe-sql-prod-01 --output jsonc
az network private-endpoint dns-zone-group list --resource-group $RG_APP --endpoint-name pe-sql-prod-01 --output table
az network private-dns record-set a list --resource-group $RG_NET --zone-name privatelink.database.windows.net --output table
nslookup sql-prod-01.database.windows.net
nslookup sql-prod-01.privatelink.database.windows.net
dig sql-prod-01.database.windows.net
tcpping sql-prod-01.database.windows.net 1433 If one of these checks is missing, the deployment is not complete enough for production validation.
Design rules that keep the setup boring
Keep private DNS zones aligned with Microsoft zone guidance.
Create zones before creating the endpoint.
Use zone groups deliberately instead of hoping the portal defaults are enough.
Forward only the private zones that need private answers.
Validate from each resolver context that matters, not only from a single Azure VM.
Treat Storage subresources as separate design decisions.
Document what should stay public, because a private zone introduced without that rule often becomes the source of NXDOMAIN later.