The symptom
Your app gets 403 This request is not authorized, Public network access is disabled, or a connection timeout to a PaaS service that has a private endpoint. Resolve the name from the client that's failing:
nslookup mystorageacct.blob.core.windows.net
| Answer | Meaning |
|---|---|
CNAME mystorageacct.privatelink.blob.core.windows.net → 10.x.x.x | DNS works. Look at NSGs, routes or firewalls. |
CNAME to privatelink... → public IP | The private DNS zone isn't linked to this VNet, or has no record |
No privatelink CNAME at all | The client uses DNS that doesn't forward to Azure, or the cache is stale |
1. Does the endpoint have a DNS zone group?
The zone group is what creates and maintains the A record automatically.
az network private-endpoint dns-zone-group list -g rg-net --endpoint-name pe-storage -o table
az network private-dns record-set a list -g rg-dns -z privatelink.blob.core.windows.net -o table
If there's no zone group, add one instead of creating A records by hand:
az network private-endpoint dns-zone-group create -g rg-net --endpoint-name pe-storage \
-n default --private-dns-zone privatelink.blob.core.windows.net --zone-name blob
2. Is the zone linked to the client's VNet?
az network private-dns link vnet list -g rg-dns -z privatelink.blob.core.windows.net \
--query "[].{name:name, vnet:virtualNetwork.id, state:virtualNetworkLinkState}" -o table
In hub-and-spoke designs, link the zone to the VNet whose DNS servers the client uses. That's usually the hub (DNS Private Resolver or your DNS VMs), not every spoke.
3. Check the zone name
Each service and sub-resource has its own zone, and a near miss fails silently. Common ones:
| Service | Zone |
|---|---|
| Blob storage | privatelink.blob.core.windows.net |
| Azure SQL | privatelink.database.windows.net |
| Key Vault | privatelink.vaultcore.azure.net |
| Azure OpenAI / AI services | privatelink.openai.azure.com, privatelink.cognitiveservices.azure.com |
| ACR | privatelink.azurecr.io (plus a regional data endpoint record) |
4. On-premises clients
168.63.129.16 (Azure DNS) is only reachable from inside Azure. On-premises DNS needs a conditional forwarder for the public zone name, such as blob.core.windows.net, pointing to an Azure DNS Private Resolver inbound endpoint or a DNS forwarder VM in Azure.
# On a Windows DNS server
Add-DnsServerConditionalForwarderZone -Name "blob.core.windows.net" -MasterServers 10.0.0.4 -ReplicationScope Forest
Resolve-DnsName mystorageacct.blob.core.windows.net -Server 10.0.0.4
Warning
Forward the public zone name, not
privatelink.blob.core.windows.net. Clients query the public name first, and the CNAME chain only reaches the private zone when the query is answered by Azure DNS.
5. Flush caches
ipconfig /flushdns # Windows client
Clear-DnsServerCache -Force # Windows DNS server
sudo resolvectl flush-caches # Linux with systemd-resolved
Applications such as Java (networkaddress.cache.ttl) and connection pools may hold the old IP until they restart.
Prevent it next time
- Create private DNS zones centrally, and use Azure Policy (DeployIfNotExists) to attach zone groups to new private endpoints.
- Add
nslookupto your deployment smoke tests and fail the deployment if the answer isn't a private IP.