When your VNet is connected to your 'on-prem' company/corporate network via an ExpressRoute circuit and you need to resolve private DNS names as well as public DNS names, you need a DNS forwarding solution.
This Terraform module stands up a Virtual Machine Scale Set (VMSS) with two VM's running bind
which will appropriately forward your DNS queries to either your private DNS servers or Azure's public DNS resolution service. These VMSS is fronted with an Azure Load Balancer that has two front-end static IP's which you will then configure in your VNet as Custom DNS servers. Once this is done, any existing VM's (provided you reboot them), or new VM's will use these VIP's as their DNS servers. Below is a diagram of what this TF module will build:
It is at the discretion of the end-user of this Terraform module to determine how they wish to store and maintain the resulting Terraform state
, as well as their source code that calls this module.
For the admin_username
, admin_password
OR public_key
, public_key_username
information that must be provided when calling this TF module, these bits of data should not be stored in any source code repository in plain-text. It recommended that a tool such as SOPS be used to encrypt this data.
module "dns-forwarding" {
source = "disney/azure-dns-forwarding"
version = "<always_specify_version_number>"
lb_front_end_ip_subnet = "/subscriptions/<subid>/resourceGroups/subnet-test/providers/Microsoft.Network/virtualNetworks/vnetname/subnets/subnet1"
load_balancer_static_ip = "10.x.x.x"
vmss_subnet_id = "/subscriptions/<subid>/resourceGroups/subnet-test/providers/Microsoft.Network/virtualNetworks/vnetname/subnets/subnet1"
vnet_cidr = "10.x.x.x/YY"
vnet_location = "westus"
subnet_has_nat_gateway = <true/false>
# Zones to forward to private DNS resolvers
dns_zones = {
"on-prem-zone1.com" = ["10.x.x.x", "10.y.y.y.y"]
"on-prem-zone2.com" = ["10.x.x.x", "10.z.z.z.z"]
}
# Authentication for VMSS Virtual Machines
# Either admin_username & admin_password must be used OR public_key & public_key_username must be used
# The values of these two variables should be encrypted; please see note about use of SOPS above
admin_username = "<username>"
admin_password = "<password>"
}
After you have deployed this module and the var.load_balancer_static_ip
is servicing DNS requests, you must configure both of these static IP's as Custom DNS servers in your VNet configuration which is explained in this article. In Step 4
, add the var.load_balancer_static_ip
as a Custom DNS server to your VNet. Please then refer to the remaining steps, 5 and 6.
Performance of the two DNS forwarders via the Azure LB was measured with 50,000 DNS queries of different names for which the DNS forwarders are authoritative, and 50,000 DNS queries of the same name for which the DNS forwarders are authoritative. The dnsperf tool was used.
The test run with 50,000 different DNS names is meant to show the average performance of the particular size of VM that was used. The test run with 50,000 of the same DNS name is meant to show the "maximum potenantial throughput" of the size of VM that was used.
user@bastion01:~/sample-query-data# time dnsperf -s 10.142.20.73 -c 5 -T 5 -d testdomainname-50000-lines.txt
DNS Performance Testing Tool
Version 2.9.0
[Status] Command line: dnsperf -s 10.142.20.73 -c 5 -T 5 -d testdomainname-50000-lines.txt
[Status] Sending queries (to 10.142.20.73:53)
[Status] Started at: Mon Feb 28 19:50:37 2022
[Status] Stopping after 1 run through file
[Status] Testing complete (end of file)
Statistics:
Queries sent: 50000
Queries completed: 50000 (100.00%)
Queries lost: 0 (0.00%)
Response codes: NOERROR 50000 (100.00%)
Average packet size: request 55, response 71
Run time (s): 9.735678
Queries per second: 5135.749149
Average Latency (s): 0.018821 (min 0.012951, max 1.235112)
Latency StdDev (s): 0.027804
real 0m9.748s
user 0m0.313s
sys 0m0.796s
user@bastion01:~/sample-query-data# time dnsperf -s 10.142.20.73 -c 5 -T 5 -d identical-testdomainname-50000-lines.txt
DNS Performance Testing Tool
Version 2.9.0
[Status] Command line: dnsperf -s 10.142.20.73 -c 5 -T 5 -d identical-testdomainname-50000-lines.txt
[Status] Sending queries (to 10.142.20.73:53)
[Status] Started at: Mon Feb 28 19:56:50 2022
[Status] Stopping after 1 run through file
[Timeout] Query timed out: msg id 8284
[Timeout] Query timed out: msg id 8285
[Status] Testing complete (end of file)
Statistics:
Queries sent: 50000
Queries completed: 49998 (100.00%)
Queries lost: 2 (0.00%)
Response codes: NOERROR 49998 (100.00%)
Average packet size: request 49, response 65
Run time (s): 0.545993
Queries per second: 91572.602579
Average Latency (s): 0.000901 (min 0.000284, max 0.010960)
Latency StdDev (s): 0.000854
real 0m5.460s
user 0m2.477s
sys 0m2.785s
root@bidevlbastion01:~/sample-query-data# time dnsperf -s 10.142.20.73 -c 5 -T 5 -d testdomainname-50000-lines.txt
DNS Performance Testing Tool
Version 2.9.0
[Status] Command line: dnsperf -s 10.142.20.73 -c 5 -T 5 -d testdomainname-50000-lines.txt
[Status] Sending queries (to 10.142.20.73:53)
[Status] Started at: Mon Feb 28 20:32:07 2022
[Status] Stopping after 1 run through file
[Status] Testing complete (end of file)
Statistics:
Queries sent: 50000
Queries completed: 50000 (100.00%)
Queries lost: 0 (0.00%)
Response codes: NOERROR 50000 (100.00%)
Average packet size: request 55, response 71
Run time (s): 9.039720
Queries per second: 5531.144770
Average Latency (s): 0.017878 (min 0.012870, max 1.238351)
Latency StdDev (s): 0.023269
real 0m9.051s
user 0m0.297s
sys 0m0.728s
root@bidevlbastion01:~/sample-query-data# time dnsperf -s 10.142.20.73 -c 5 -T 5 -d identical-testdomainname-50000-lines.txt
DNS Performance Testing Tool
Version 2.9.0
[Status] Command line: dnsperf -s 10.142.20.73 -c 5 -T 5 -d identical-testdomainname-50000-lines.txt
[Status] Sending queries (to 10.142.20.73:53)
[Status] Started at: Mon Feb 28 20:33:07 2022
[Status] Stopping after 1 run through file
[Status] Testing complete (end of file)
Statistics:
Queries sent: 48500
Queries completed: 48500 (100.00%)
Queries lost: 0 (0.00%)
Response codes: NOERROR 48500 (100.00%)
Average packet size: request 49, response 65
Run time (s): 0.475324
Queries per second: 102035.664094
Average Latency (s): 0.000789 (min 0.000290, max 0.009938)
Latency StdDev (s): 0.000718
real 0m0.496s
user 0m0.099s
sys 0m0.274s
Name | Version |
---|---|
terraform | >= 1.1.3 |
azurerm | >= 2.94 |
cloudinit | >= 2.2.0 |
Name | Version |
---|---|
azurerm | 3.20.0 |
cloudinit | 2.2.0 |
Name | Type |
---|---|
azurerm_application_security_group.dns_forwarding | resource |
azurerm_lb.dns_forwarding | resource |
azurerm_lb_backend_address_pool.dns_forwarding | resource |
azurerm_lb_probe.dns_forwarding | resource |
azurerm_lb_rule.dns_forwarding_tcp | resource |
azurerm_lb_rule.dns_forwarding_udp | resource |
azurerm_linux_virtual_machine_scale_set.dns_forwarding | resource |
azurerm_nat_gateway.dns_forwarding | resource |
azurerm_nat_gateway_public_ip_association.dns_forwarding | resource |
azurerm_network_security_group.dns_forwarding | resource |
azurerm_network_security_rule.dns_forwarding | resource |
azurerm_public_ip.dns_forwarding | resource |
azurerm_resource_group.dns_forwarding | resource |
azurerm_subnet_nat_gateway_association.dns_forwarding | resource |
azurerm_shared_image_version.image_from_gallery | data source |
Name | Description | Type | Default | Required |
---|---|---|---|---|
dns_zones | List of DNS Zones who's requests should be forwarded to private, on-prem DNS servers | object |
n/a | yes |
lb_front_end_ip_subnet | Subnet ID of the Load Balancer front end IP addresses | string |
n/a | yes |
load_balancer_static_ip | A static IP that will be the front end IP of the load balancer | string |
n/a | yes |
subnet_has_nat_gateway | The subnet where this module is to be deployed already has a NAT Gateway (required for the VMSS VM's to get access to the Internet) | bool |
n/a | yes |
vmss_subnet_id | The ID of the subnet where you want to place the Virtual Machine Scale Set | string |
n/a | yes |
vnet_cidr | The CIDR notation of the VNet where you are deploying DNS forwarding, such as 10.100.34.0/24 | string |
n/a | yes |
vnet_location | The location, such as 'westus' of the Virtual Network where you want DNS Forwarding services | string |
n/a | yes |
additional_cloud_config_content | Optional additional cloud-config file content to be merged in with main cloud-config. | string |
null |
no |
additional_cloud_config_merge_type | Optional value for the X-Merge-Type header to control cloud-init merging behavior when additional_cloud_config_content is provided. See https://cloudinit.readthedocs.io/en/latest/topics/merging.html for available options. |
string |
null |
no |
admin_password | The admin password of the admin_username for the VM's in the VMSS. Either admin_username & admin_password must be used OR public_key & public_key_username must be used | string |
null |
no |
admin_username | The username of the local administrator on each Virtual Machine Scale Set instance | string |
null |
no |
automatic_instance_repair | Should the VMSS automatically repair unhealthy hosts | bool |
true |
no |
common_tags | n/a | map(string) |
{ "managed_by": "terraform", "project": "Azure DNS forwarding" } |
no |
custom_base_cloudinit | Gives users of this module the option of replacing the entire defult DNS configuration, found in local.base_cloudinit, with their own config. | string |
null |
no |
custom_nsg_rules | Gives users of this module the option of supplying their own NSG rules. | object |
null |
no |
custom_source_image | Use a specified image for the VM's in the Scale Set, as opposed to an image specified from an Azure Compute Gallery | bool |
true |
no |
custom_tags | Map of tags you would like to have added to the common_tags to tag all applicable resources | map(string) |
{} |
no |
dnssec_enable | Configure dnssec-enable setting in /etc/bind/named.conf.options |
string |
"yes" |
no |
dnssec_validation | Configure dnssec-validation setting in /etc/bind/named.conf.options |
string |
"yes" |
no |
grace_period_instance_repair | Amount of time (in minutes, between 30 and 90, defaults to 30 minutes) for which automatic repairs will be delayed. The grace period starts right after the VM is found unhealthy. The time duration should be specified in ISO 8601 format. | string |
"PT30M" |
no |
image_gallery_gallery_name | Name of image gallery where image comes from | string |
null |
no |
image_gallery_image_name | Name of the image from the gallery | string |
null |
no |
image_gallery_name | Name of the image. 'latest' pulls the latest image | string |
"latest" |
no |
image_gallery_resource_group_name | Name of the resource group where the image gallery resides | string |
null |
no |
os_disk_caching | The Type of Caching which should be used for the Internal OS Disk. Possible values are None, ReadOnly and ReadWrite | string |
"None" |
no |
os_disk_size_gb | The Size of the Internal OS Disk in GB, if you wish to vary from the size used in the image this Virtual Machine Scale Set is sourced from | number |
40 |
no |
os_disk_storage_account_type | The Type of Storage Account which should back this the Internal OS Disk. Possible values include Standard_LRS, StandardSSD_LRS and Premium_LRS | string |
"Standard_LRS" |
no |
permitted_cidrs | List of CIDR's to permit inbound to ssh into the backend VM's | list(string) |
[ "10.0.0.0/8" ] |
no |
permitted_to_query_dns_forwarders | List of CIDR blocks that are permitted to query the DNS forwarders via the allowed-query config item in the named.conf.options file |
list(string) |
[10.0.0.0/8] |
no |
public_key | The Public Key which should be used for authentication, which needs to be at least 2048-bit and in ssh-rsa format. Either admin_username & admin_password must be used OR public_key & public_key_username must be used | string |
null |
no |
public_key_username | The Username for which this Public SSH Key should be configured | string |
null |
no |
quantity_of_instances | The number of Virtual Machines in the Scale Set | number |
2 |
no |
querylog | Querylog enabled in named.conf.options | string |
"false" |
no |
resource_group_name | n/a | string |
"rg-dns-forwarding" |
no |
subscription_id_for_image_gallery | The subscription ID of the subscription where the Azure Compute Gallery that stores the image you want to use for the VMSS | string |
null |
no |
user_data_script | Optional cloud-config user-data script. See https://cloudinit.readthedocs.io/en/latest/topics/format.html?highlight=shell#user-data-script for more info. | string |
null |
no |
vm_sku | The SKU of the VM to run the DNS forwarding services | string |
"Standard_D2_v5" |
no |
vmss_image_offer | Must be specified if var.custom_source_image is true. Specifies the offer of the image used to create the virtual machines | string |
null |
no |
vmss_image_publisher | Must be specified if var.custom_source_image is true. Specifies the publisher of the image used to create the virtual machines | string |
null |
no |
vmss_image_sku | Must be specified if var.custom_source_image is true. Specifies the SKU of the image used to create the virtual machines | string |
null |
no |
vmss_image_version | Must be specified if var.custom_source_image is true. Specifies the version of the image used to create the virtual machines. | string |
null |
no |
vmss_name | Virtual Machine Scale Set (VMSS) name | string |
"vmss-dns-forwarding" |
no |
No outputs.
Mitchell L. Cooper
- Justice M. London
- James Philpott