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.

20 Apr 2026 azureprivate-endpointdnsprivate-dnsnetworking

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.

text
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.

bash
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. citeturn359180search1

Create the Private Endpoint for SQL

The safest sequence is zone first, endpoint second, validation third.

bash
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.

bash
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.

bash
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. citeturn359180search1

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. citeturn359180search5turn359180search10turn359180search8

Example rule on a Windows DNS server:

powershell
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:

powershell
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. citeturn359180search1

text
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. citeturn359180search1

Typical symptom:

bash
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

bash
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.

References