Home / Azure

AzureHow-toIntermediate

Azure DNS Private Resolver - hybrid DNS between on-premises and Azure without DNS forwarder VMs

Replace DNS forwarder VMs with Azure DNS Private Resolver. An inbound endpoint lets on-premises resolve Private Link zones; an outbound endpoint with a forwarding ruleset lets Azure resolve on-premises domains.

Deepak Bhardwaj5 min read

Applies toAzure DNS Private Resolver; hub-spoke VNets; Azure CLI 2.x with the dns-resolver extension

What you are building

Two flows need to work:

  1. On-premises → Azure: on-premises servers resolve privatelink.* names (Storage, SQL, Key Vault private endpoints) to private IPs.
  2. Azure → on-premises: Azure VMs resolve corp.contoso.com through on-premises DNS servers.

Many estates use a pair of Windows DNS VMs as forwarders for this. Azure DNS Private Resolver is a managed service that does the same job with no VMs to patch.

Component Purpose
Resolver Lives in the hub VNet
Inbound endpoint Private IP that on-premises DNS forwards to
Outbound endpoint Source of queries going to on-premises DNS
Forwarding ruleset Rules such as "corp.contoso.com → 10.10.0.4, 10.10.0.5", linked to VNets

Prerequisites

  • A hub VNet connected to on-premises (VPN or ExpressRoute).
  • Two empty subnets in the hub, each delegated to Microsoft.Network/dnsResolvers. Use /28 or larger. Check the documentation for the current minimum.
  • Private DNS zones (privatelink.blob.core.windows.net and so on) linked to the hub VNet.
az extension add --name dns-resolver --upgrade

az network vnet subnet create -g rg-hub --vnet-name vnet-hub -n snet-dns-in \
  --address-prefixes 10.0.10.0/28 --delegations Microsoft.Network/dnsResolvers
az network vnet subnet create -g rg-hub --vnet-name vnet-hub -n snet-dns-out \
  --address-prefixes 10.0.10.16/28 --delegations Microsoft.Network/dnsResolvers

1. Create the resolver and endpoints

VNET_ID=$(az network vnet show -g rg-hub -n vnet-hub --query id -o tsv)
IN_SUBNET=$(az network vnet subnet show -g rg-hub --vnet-name vnet-hub -n snet-dns-in --query id -o tsv)
OUT_SUBNET=$(az network vnet subnet show -g rg-hub --vnet-name vnet-hub -n snet-dns-out --query id -o tsv)

az dns-resolver create -g rg-hub -n dnspr-hub --location westeurope --id $VNET_ID

az dns-resolver inbound-endpoint create -g rg-hub --dns-resolver-name dnspr-hub -n in-ep \
  --location westeurope \
  --ip-configurations "[{private-ip-allocation-method:Dynamic,id:$IN_SUBNET}]"

az dns-resolver outbound-endpoint create -g rg-hub --dns-resolver-name dnspr-hub -n out-ep \
  --location westeurope --id $OUT_SUBNET

Get the inbound IP for the on-premises forwarders:

az dns-resolver inbound-endpoint show -g rg-hub --dns-resolver-name dnspr-hub -n in-ep \
  --query "ipConfigurations[0].privateIpAddress" -o tsv

Tip

Parameter names in the dns-resolver extension have changed between versions. If a command fails, check az dns-resolver inbound-endpoint create --help for your build.

2. On-premises → Azure

On the on-premises DNS servers, add conditional forwarders for each Private Link zone you use, pointing at the inbound endpoint IP. On Windows DNS:

Add-DnsServerConditionalForwarderZone -Name "blob.core.windows.net" -MasterServers 10.0.10.4 -ReplicationScope Forest
Add-DnsServerConditionalForwarderZone -Name "database.windows.net"  -MasterServers 10.0.10.4 -ReplicationScope Forest
Add-DnsServerConditionalForwarderZone -Name "vault.azure.net"       -MasterServers 10.0.10.4 -ReplicationScope Forest

Forward the public zone name (blob.core.windows.net), not privatelink.blob.core.windows.net. The public name returns a CNAME to the privatelink name, and Azure DNS answers that from the linked private zone.

3. Azure → on-premises

OUT_EP=$(az dns-resolver outbound-endpoint show -g rg-hub --dns-resolver-name dnspr-hub -n out-ep --query id -o tsv)

az dns-resolver forwarding-ruleset create -g rg-hub -n frs-hub --location westeurope \
  --outbound-endpoints "[{id:$OUT_EP}]"

az dns-resolver forwarding-rule create -g rg-hub --ruleset-name frs-hub -n corp \
  --domain-name "corp.contoso.com." --forwarding-rule-state Enabled \
  --target-dns-servers "[{ip-address:10.10.0.4,port:53},{ip-address:10.10.0.5,port:53}]"

The domain name ends with a dot. Then link the ruleset to every VNet that needs to resolve on-premises names (the hub and each spoke):

az dns-resolver vnet-link create -g rg-hub --ruleset-name frs-hub -n link-spoke-app \
  --id $(az network vnet show -g rg-spoke-app -n vnet-spoke-app --query id -o tsv)

Spoke VNets keep using Azure-provided DNS (168.63.129.16). The ruleset link is what sends corp.contoso.com to on-premises. You don't need custom DNS servers on the spokes.

4. Test

From an on-premises server:

Resolve-DnsName mystorageacct.blob.core.windows.net
# expect a CNAME to mystorageacct.privatelink.blob.core.windows.net and a 10.x address

From an Azure VM in a spoke:

nslookup fileserver01.corp.contoso.com
dig +short mystorageacct.blob.core.windows.net

Troubleshooting

Symptom Check
On-premises gets the public IP Conditional forwarder missing, or the private zone not linked to the hub VNet
On-premises gets a timeout Firewall or NSG blocking UDP/TCP 53 to the inbound endpoint subnet
Azure VM can't resolve corp names Ruleset not linked to that VNet, or the VNet uses custom DNS servers that bypass Azure DNS
Works for some spokes only Each spoke needs its own ruleset link

Migrating from forwarder VMs

  1. Build the resolver alongside the existing VMs.
  2. Add the inbound IP as an extra forwarder on-premises, and test.
  3. Switch spoke VNets from custom DNS (the VM IPs) back to Azure-provided DNS and link the ruleset. Each VM picks up the change at its next DHCP renewal or reboot.
  4. Remove the old forwarder VMs once the queries hitting them drop to zero.

Checklist

  • Two delegated subnets in the hub
  • Inbound and outbound endpoints created
  • On-premises conditional forwarders for each Private Link zone
  • Forwarding ruleset with on-premises domains, linked to all VNets
  • Spokes use Azure-provided DNS
  • Both directions tested