Skip to content

Commit bc08c27

Browse files
committedJul 10, 2020
Initial commit.
0 parents  commit bc08c27

8 files changed

+446
-0
lines changed
 

‎README.md

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# Azure Custom DNS with forwarders example
2+
3+
This repository will help set up Azure DNS to function in a hub and spoke model with private DNS zones and use of on-premises DNS resolvers.
4+
5+
- [DNS Forwarding Virtual Machine Scale Set](#dns-forwarding-vmss)
6+
- [Azure Policy for Custom DNS](#azure-policy-for-custom-dns)
7+
8+
## DNS Forwarding VMSS
9+
This ARM template deploys a virtual machine scale set consisting of 3 Ubuntu 18.04 VMs with dnsmasq installed and configured. It is deployed in a stateless configuration, so the VMs can automatically patch and self-heal in the event of a failed instance.
10+
11+
### Deployment instructions
12+
1. Modify parameters.json as appropriate for your environment
13+
- `vmssName`: name of the scaleset
14+
- <details>
15+
<summary>
16+
<code>vnetId</code>: the full resource ID of the virtual network to use
17+
</summary>
18+
<ul>
19+
<li>via <a href="https://portal.azure.com/">Azure Portal</a>: found in the properties blade of the virtual network</li>
20+
<li>via <a href="https://docs.microsoft.com/en-us/cli/azure/">Azure CLI</a>: <code>az network vnet show --resource-group rg-hub-network-centralus --name vnet-hub-centralus-001 --query id -o tsv</code></li>
21+
<li>via <a href="https://docs.microsoft.com/en-us/powershell/azure/">Azure PowerShell</a>: <code>Get-AzVirtualNetwork -ResourceGroupName rg-hub-network-centralus -Name vnet-hub-centralus-001 | Select-Object -ExpandProperty Id</code></li>
22+
</ul>
23+
</details>
24+
- `subnetName`: the name of the subnet
25+
- `stgAcctName`: the name of the storage account to use for boot diagnostics
26+
- `sshUser`: the username to use for admin access via SSH
27+
- `sshKey`: the public key to assign to `sshUser`
28+
2. Modify customData.json to appropriately set up dnsmasq
29+
- add `server=/domain/nameserverIP` lines for each domain you want to forward to on-premises (if you have multiple nameservers, use one line per nameserver)
30+
- if you have multiple domains to forward to the same nameserver, you can use the format `server=/domain1/domain2/nameserverIP`
31+
- leave the last server line intact to forward any non-matching queries to Azure DNS: `server=168.63.129.16`
32+
3. Deploy the template
33+
- <details>
34+
<summary>
35+
Using Azure CLI
36+
</summary>
37+
<pre><code>az deployment group create \
38+
--resource-group rg-hub-dnsfwd-centralus \
39+
--template-file template-vmss.json \
40+
--parameters @parameters.json \
41+
--parameters customData=@customData.yaml</code></pre>
42+
</details>
43+
- <details>
44+
<summary>
45+
Using Azure PowerShell
46+
</summary>
47+
<pre><code>New-AzResourceGroupDeployment `
48+
-ResourceGroupName rg-hub-dnsfwd-centralus `
49+
-TemplateFile .\template-vmss.json `
50+
-TemplateParameterFile .\parameters.json `
51+
-customData $(Get-Content .\customData.yaml -Raw)</code></pre>
52+
</details>
53+
54+
## Azure Policy for Custom DNS
55+
56+
The two policies contained in this repository are useful when setting up Custom DNS in a hub and spoke network.
57+
58+
### [privateDNShubLinkPolicyDeployINE.json](azure-policy/privateDNShubLinkPolicyDeployINE.json)
59+
This policy will automatically deploy a link from any private DNS zones in scope to the hub vnet where your DNS forwarders are running if one does not already exist. This is critical for having your DNS servers able to resolve private DNS zones in a spoke virtual network.
60+
61+
### [vnetDNSServersAppend.json](azure-policy/vnetDNSServersAppend.json)
62+
This policy will ensure that all virtual networks deployed have the DNS servers set to the values specified so that DNS lookups forward to on-premises and private DNS zones correctly.
63+
64+
## On-premises DNS setup
65+
66+
Also provided is [a script](ad-dns/Add-AzureDNSFowarderZones.ps1) that will forward all currently used Azure Private Link DNS domains from an on-premises Active Directory DNS server to the forwarders deployed above. If you create custom private DNS zones in Azure, you will need to set your forwarding up in the same way if you want them resolvable from on premises.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
[CmdletBinding()]
2+
param (
3+
[Parameter()]
4+
[String[]]
5+
$DNSForwarderIPs
6+
)
7+
8+
$AzureDomains = @('api.azureml.ms','azconfig.io','azmk8s.io','azure-automation.net','azurecr.io','azure-devices.net','azurewebsites.net','backup.windowsazure.com','blob.core.windows.net','cassandra.cosmos.azure.com','cognitiveservices.azure.com','database.windows.net','dfs.core.windows.net','documents.azure.com','eventgrid.azure.net','file.core.windows.net','gremlin.cosmos.azure.com','mariadb.database.azure.com','mongo.cosmos.azure.com','monitor.azure.com','mysql.database.azure.com','postgres.database.azure.com','queue.core.windows.net','search.windows.net','service.signalr.net','servicebus.windows.net','table.core.windows.net','table.cosmos.azure.com','vault.azure.net','vaultcore.azure.net','web.core.windows.net')
9+
$DNSForwarderZones = Get-DnsServerZone | Where-Object { $_.ZoneType -eq "Forwarder" }
10+
11+
$AzureDomains | ForEach-Object {
12+
if ($_ -in $DNSForwarderZones.ZoneName) {
13+
Write-Host "Updating DNS forwarder zone ${_}..."
14+
Set-DnsServerConditionalForwarderZone -Name $_ -MasterServers $DNSForwarderIPs
15+
} else {
16+
Write-Host "Adding DNS forwarder zone ${_}..."
17+
Add-DnsServerConditionalForwarderZone -Name $_ -MasterServers $DNSForwarderIPs -ReplicationScope Forest
18+
}
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
{
2+
"properties": {
3+
"displayName": "Private DNS - deployINE virtual network link",
4+
"policyType": "Custom",
5+
"mode": "All",
6+
"description": "Automatically create a link to a selected virtual network when a private DNS zone is created. This is useful when DNS zones need to be linked to a hub virtual network where DNS forwarders have been deployed.",
7+
"parameters": {
8+
"virtualNetworkLinkName": {
9+
"type": "String",
10+
"metadata": {
11+
"displayName": "Virtual Network Link Name",
12+
"description": "The name of the virtual network link to create"
13+
}
14+
},
15+
"targetVnet": {
16+
"type": "String",
17+
"metadata": {
18+
"displayName": "Target virtual network",
19+
"description": "The target virtual network for Private DNS zone connections"
20+
}
21+
}
22+
},
23+
"policyRule": {
24+
"if": {
25+
"field": "type",
26+
"equals": "Microsoft.Network/privateDnsZones"
27+
},
28+
"then": {
29+
"effect": "deployIfNotExists",
30+
"details": {
31+
"type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks",
32+
"name": "[parameters('virtualNetworkLinkName')]",
33+
"roleDefinitionIds": [
34+
"/providers/Microsoft.Authorization/roleDefinitions/4d97b98b-1d4f-4787-a291-c67834d212e7"
35+
],
36+
"existenceCondition": {
37+
"field": "Microsoft.Network/privateDnsZones/virtualNetworkLinks/virtualNetwork.id",
38+
"equals": "[parameters('targetVnet')]"
39+
},
40+
"deployment": {
41+
"properties": {
42+
"mode": "incremental",
43+
"template": {
44+
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
45+
"contentVersion": "1.0.0.0",
46+
"parameters": {
47+
"privateDNSZoneName": {
48+
"type": "string"
49+
},
50+
"virtualNetworkLinkName": {
51+
"type": "string"
52+
},
53+
"targetVnetId": {
54+
"type": "string"
55+
}
56+
},
57+
"resources": [
58+
{
59+
"name": "[concat(parameters('privateDNSZoneName'), '/', parameters('virtualNetworkLinkName'))]",
60+
"type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks",
61+
"apiVersion": "2018-09-01",
62+
"tags": {},
63+
"location": "global",
64+
"properties": {
65+
"virtualNetwork": {
66+
"id": "[parameters('targetVnetId')]"
67+
},
68+
"registrationEnabled": false
69+
}
70+
}
71+
]
72+
},
73+
"parameters": {
74+
"privateDNSZoneName": {
75+
"value": "[field('name')]"
76+
},
77+
"virtualNetworkLinkName": {
78+
"value": "[parameters('virtualNetworkLinkName')]"
79+
},
80+
"targetVnetId": {
81+
"value": "[parameters('targetVnet')]"
82+
}
83+
}
84+
}
85+
}
86+
}
87+
}
88+
}
89+
}
90+
}
+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
{
2+
"properties": {
3+
"displayName": "Custom DNS management - set Virtual Network DNS servers",
4+
"policyType": "Custom",
5+
"mode": "All",
6+
"description": "Custom DNS servers must be set to the AD DC IPs.",
7+
"parameters": {
8+
"customDNSServers": {
9+
"type": "Array",
10+
"metadata": {
11+
"displayName": "Custom DNS servers",
12+
"description": "The list of required custom DNS servers."
13+
}
14+
}
15+
},
16+
"policyRule": {
17+
"if": {
18+
"allOf": [
19+
{
20+
"field": "type",
21+
"equals": "Microsoft.Network/virtualNetworks"
22+
},
23+
{
24+
"anyOf": [
25+
{
26+
"count": {
27+
"field": "Microsoft.Network/virtualNetworks/dhcpOptions.dnsServers[*]"
28+
},
29+
"notEquals": "[length(parameters('customDNSServers'))]"
30+
},
31+
{
32+
"count": {
33+
"field": "Microsoft.Network/virtualNetworks/dhcpOptions.dnsServers[*]",
34+
"where": {
35+
"field": "Microsoft.Network/virtualNetworks/dhcpOptions.dnsServers[*]",
36+
"in": "[parameters('customDNSServers')]"
37+
}
38+
},
39+
"notEquals": "[length(field('Microsoft.Network/virtualNetworks/dhcpOptions.dnsServers[*]'))]"
40+
}
41+
]
42+
}
43+
]
44+
},
45+
"then": {
46+
"effect": "append",
47+
"details": [
48+
{
49+
"field": "Microsoft.Network/virtualNetworks/dhcpOptions.dnsServers",
50+
"value": "[parameters('customDNSServers')]"
51+
}
52+
]
53+
}
54+
}
55+
}
56+
}

‎vmss-dnsfwd/customData.yaml

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#cloud-config
2+
package_update: true
3+
packages:
4+
- dnsmasq
5+
write_files:
6+
- path: /etc/dnsmasq.conf
7+
content: |
8+
no-resolv
9+
server=/mydomain.com/1.2.3.4
10+
server=/mydomain.com/1.2.3.5
11+
server=168.63.129.16
12+
runcmd:
13+
- [ systemctl, daemon-reload ]
14+
- [ systemctl, enable, dnsmasq.service ]
15+
- [ systemctl, start, --no-block, dnsmasq.service ]

‎vmss-dnsfwd/deployCommand

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
az deployment group create \
2+
--resource-group rg-hub-dnsfwd-centralus \
3+
--template-file template-vmss.json \
4+
--parameters @parameters.json \
5+
--parameters customData=@customData.yaml

‎vmss-dnsfwd/parameters.json

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
3+
"contentVersion": "1.0.0.0",
4+
"parameters": {
5+
"vmssName": {
6+
"value": "vmss-dnsfwd-centralus-001"
7+
},
8+
"vnetId": {
9+
"value": "/subscriptions/69924db1-a0fe-43f6-b1ad-86bb3248f4a1/resourceGroups/rg-hub-network-centralus/providers/Microsoft.Network/virtualNetworks/vnet-hub-centralus-001"
10+
},
11+
"subnetName": {
12+
"value": "snet-dnsfwd-centralus-001"
13+
},
14+
"stgAcctName": {
15+
"value": "pahealyhublogs"
16+
},
17+
"sshUser": {
18+
"value": "azureuser"
19+
},
20+
"sshKey": {
21+
"value": ""
22+
}
23+
}
24+
}

‎vmss-dnsfwd/template-vmss.json

+171
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
{
2+
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
3+
"contentVersion": "1.0.0.0",
4+
"parameters": {
5+
"vmssName": {
6+
"metadata": { "description": "Name of the virtual machine scale set" },
7+
"type": "String"
8+
},
9+
"vnetId": {
10+
"metadata": { "description": "Resource ID of the virtual network to deploy into" },
11+
"type": "String"
12+
},
13+
"subnetName": {
14+
"metadata": { "description": "Name of the subnet to use" },
15+
"type": "String"
16+
},
17+
"stgAcctName": {
18+
"metadata": { "description": "Name of the storage account to use for boot diagnostics" },
19+
"type": "String"
20+
},
21+
"sshUser": {
22+
"defaultValue": "azureuser",
23+
"metadata": { "description": "SSH admin username" },
24+
"type": "string"
25+
},
26+
"sshKey": {
27+
"metadata": { "description": "SSH public key for admin access" },
28+
"type": "String"
29+
},
30+
"customData": {
31+
"metadata": { "description": "The custom-data.yaml file to use at build time" },
32+
"type": "String"
33+
}
34+
},
35+
"variables": {},
36+
"resources": [
37+
{
38+
"type": "Microsoft.Compute/virtualMachineScaleSets",
39+
"apiVersion": "2019-07-01",
40+
"name": "[parameters('vmssName')]",
41+
"location": "[resourceGroup().location]",
42+
"sku": {
43+
"name": "Standard_B1s",
44+
"tier": "Standard",
45+
"capacity": 3
46+
},
47+
"zones": [
48+
"1",
49+
"2",
50+
"3"
51+
],
52+
"properties": {
53+
"singlePlacementGroup": false,
54+
"upgradePolicy": {
55+
"mode": "Automatic",
56+
"rollingUpgradePolicy": {
57+
"maxBatchInstancePercent": 20,
58+
"maxUnhealthyInstancePercent": 20,
59+
"maxUnhealthyUpgradedInstancePercent": 20,
60+
"pauseTimeBetweenBatches": "PT0S"
61+
},
62+
"automaticOSUpgradePolicy": {
63+
"enableAutomaticOSUpgrade": true,
64+
"disableAutomaticRollback": false
65+
}
66+
},
67+
"scaleInPolicy": {
68+
"rules": [
69+
"Default"
70+
]
71+
},
72+
"virtualMachineProfile": {
73+
"osProfile": {
74+
"computerNamePrefix": "dnsfwd-",
75+
"customdata": "[base64(parameters('customData'))]",
76+
"adminUsername": "[parameters('sshUser')]",
77+
"linuxConfiguration": {
78+
"disablePasswordAuthentication": true,
79+
"ssh": {
80+
"publicKeys": [
81+
{
82+
"path": "[concat('/home/',parameters('sshUser'),'/.ssh/authorized_keys')]",
83+
"keyData": "[parameters('sshKey')]"
84+
}
85+
]
86+
},
87+
"provisionVMAgent": true
88+
},
89+
"secrets": []
90+
},
91+
"storageProfile": {
92+
"osDisk": {
93+
"createOption": "FromImage",
94+
"caching": "ReadWrite",
95+
"managedDisk": {
96+
"storageAccountType": "Premium_LRS"
97+
},
98+
"diskSizeGB": 30
99+
},
100+
"imageReference": {
101+
"publisher": "Canonical",
102+
"offer": "UbuntuServer",
103+
"sku": "18.04-LTS",
104+
"version": "latest"
105+
}
106+
},
107+
"networkProfile": {
108+
"networkInterfaceConfigurations": [
109+
{
110+
"name": "[concat(parameters('vmssName'), '-nic')]",
111+
"properties": {
112+
"primary": true,
113+
"enableAcceleratedNetworking": false,
114+
"dnsSettings": {
115+
"dnsServers": []
116+
},
117+
"enableIPForwarding": false,
118+
"ipConfigurations": [
119+
{
120+
"name": "[concat(parameters('vmssName'), '-nic-defaultIpConfiguration')]",
121+
"properties": {
122+
"primary": true,
123+
"subnet": {
124+
"id": "[concat(parameters('vnetId'), '/subnets/', parameters('subNetName'))]"
125+
},
126+
"privateIPAddressVersion": "IPv4",
127+
"loadBalancerBackendAddressPools": []
128+
}
129+
}
130+
]
131+
}
132+
}
133+
]
134+
},
135+
"diagnosticsProfile": {
136+
"bootDiagnostics": {
137+
"enabled": true,
138+
"storageUri": "[concat('https://', parameters('stgAcctName'), '.blob.core.windows.net/')]"
139+
}
140+
},
141+
"extensionProfile": {
142+
"extensions": [
143+
{
144+
"name": "HealthExtension",
145+
"properties": {
146+
"autoUpgradeMinorVersion": false,
147+
"publisher": "Microsoft.ManagedServices",
148+
"type": "ApplicationHealthLinux",
149+
"typeHandlerVersion": "1.0",
150+
"settings": {
151+
"protocol": "tcp",
152+
"port": 53
153+
}
154+
}
155+
}
156+
]
157+
},
158+
"priority": "Regular"
159+
},
160+
"overprovision": true,
161+
"doNotRunExtensionsOnOverprovisionedVMs": false,
162+
"zoneBalance": false,
163+
"platformFaultDomainCount": 1,
164+
"automaticRepairsPolicy": {
165+
"enabled": false,
166+
"gracePeriod": "PT30M"
167+
}
168+
}
169+
}
170+
]
171+
}

0 commit comments

Comments
 (0)
Please sign in to comment.