Using Azure DNS for dynamic DNS
As a long time user of DynDNS and a generally happy customer, I’ve paid for their dynamic DNS services to provide my regularly changing home IP address to keep my website up and running using a combination of a rather badly named DynDNS domain zone with CNAME records for my actual domain resolving the DynDNS zone.
I’ve avoided taking up their rather expensive Managed DNS services at $7 per month to provide a very simple service but a recent job I’ve been working on has had me looking at all of Microsoft’s Azure services in detail. Despite it being in preview, I decided to take a closer look at Microsoft’s Azure DNS to see what it can do.
In essence, Microsoft Azure DNS allows you to host your domain zone and records in Azure but not to purchase a domain. For example, to use Azure DNS you must use a domain registrar to purchase a domain (or have one already) and then delegate the zone to Azure DNS by altering nameservers of the zone to Microsoft’s. This is usually very straightforward to do but differs between registrars. My domain registrar is 123-reg so their nameserver update page looks as follows. Ignore the nameserver settings here, yours are likely different.
I’m not going to go through the process of signing up for Azure etc. There’s plenty of documentation out there covering the process in graphic detail.
As you know from the title of this post, the reason for doing this is to implement a dynamic DNS solution for your own domain using Azure DNS. Typically, for static IP addresses this is completely unnecessary but in my circumstances, where I host my personal website from my own home network and have a frequently changing IP address, it’s a necessity. Compared to DynDNS’ managed DNS services at $7 per month for their lowest bracket ($84 per year!), Azure DNS is an absolute STEAL, charging only 15 pence per zone, per month for the first 25 zones. This pricing represents a 50% discount over production prices while the service is in preview but even then, if I had 5 zones hosted in Azure, I’d spend less than 25% on DNS costs compared to DynDNS.
Just to re-iterate, Azure DNS is in preview for now. For my purposes (personal use for my own blog) it’s fine. If people can’t reach my site for a short while, it isn’t the end of the world – that said, I don’t expect that to happen.
So, back to it. We’ve got a domain and we have delegated it to Azure DNS by changing the nameservers at our registrar’s site. So how do we use this for Dynamic DNS? Azure DNS exposes three methods of configuration, unfortunately a REST API isn’t one of them but PowerShell is. A short while ago I created a PowerShell script that could be used to update DynDNS records using pure PowerShell so this can be used as a template for integrating with Azure DNS. The trade-off is the need to run a PowerShell script from inside your network. I’ve no doubt there’s a way to integrate this with Azure Automation with Webhooks and it may be a little extension project I use to expand on this article in the future but for now, this is a script that must execute from inside the network with the IP address you will use to update Azure DNS records.
The process itself is very simple; get the current IP address of your Internet connection, compare it with what’s set for the A record in the Azure DNS zone and if it doesn’t match, update the record in the zone. Easy really.
Here’s a script that’ll get that done.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
Function Get-DDWRTExternalIP { $Page = Invoke-WebRequest -UseBasicParsing -Uri "http://192.168.1.1/Status_Internet.live.asp" -Credential (Import-Clixml $ScriptPath"\DDWRTCreds.xml") $WANregex='(?<Address>{wan_ipaddr::(\b(([01]?\d?\d|2[0-4]\d|25[0-5])\.){3}([01]?\d?\d|2[0-4]\d|25[0-5])\b)})' $IPregex='(?<Address>(\b(([01]?\d?\d|2[0-4]\d|25[0-5])\.){3}([01]?\d?\d|2[0-4]\d|25[0-5])\b))' If ($Page.Content -match $WANregex) {$WANAddress = $Matches.Address} Else {Return $false} If ($WANAddress -match $IPregex) {Return $Matches.Address} Else {Return $false} } # Get the invocation path of the current file. $ScriptPath = Split-Path $Script:MyInvocation.MyCommand.Path # Set the expected IP Address. Obtain this from a DNS query or set it statically. $EI = [System.Net.Dns]::GetHostAddresses("lewisroberts.com") | Select-Object -ExpandProperty IPAddressToString # Obtain the IP Address of the Internet connection. $IP = Get-DDWRTExternalIP # If the IP isn't what you expected... If ($IP -ne $EI) { # Login to Azure $Creds = Import-Clixml -Path $ScriptPath"\AzureRMCreds.xml" Login-AzureRmAccount -Credential $Creds # Update the apex record $RecordSet = New-AzureRmDnsRecordSet -Name "@" -RecordType A -ZoneName "lewisroberts.com" -ResourceGroupName "DNS" -Ttl 60 -Overwrite -Force Add-AzureRmDnsRecordConfig -RecordSet $RecordSet -Ipv4Address $IP Set-AzureRmDnsRecordSet -RecordSet $RecordSet } Else { Write-Output "Dynamic address ($IP) and DNS address ($EI) match." } |
Initially it starts out by obtaining the Internet connection’s IP address from the router, you could just as easily do this by obtaining the IP from something like my own tool. https://showextip.azurewebsites.net/ – as it is, asking my own DD-WRT router is less expensive.
I then compare the current IP address with the value currently set on the record I’m interested in – in this case lewisroberts.com – just the apex/root record, not www .
If it isn’t the same, I bounce on to Azure DNS and forcefully update the record and enforce a 60 second TTL.
Using this method I can avoid paying for DynDNS services at all and rather than using CNAMEs for my domain names, I can use actual IP addresses.
– Lewis
Simply brilliant! I used this as a starting point and modified it slightly for my own unique situation.
Great post. I believe that readers should note that storing creds in an xml file works fine for Azure AD accounts but not for Live ID.
Great solution, but I am having problems getting it to work via the Task Scheduler, I have scheduled it to work every morning when the Azure VM starts-up, but it does not change the IP. I think it is to do with either the “Login-AzureRMAccount” or “Update the apex record”, but cannot workout which?
Hi Hemant, the best thing to do is run one line at a time in a PowerShell ISE window and see what the problem is there. As Rob M said, the use of stored credentials is both necessary and a limitation.
To create your own credentials, open a PowerShell window in the same folder as your script and type:
Type in the credentials you would normally use to log on to Azure (assuming these are organisational credentials such as user@something.onmicrosoft.com. As Rob M said, this doesn’t work for Outlook or Live account logins.
The commands will create an encrypted credential file which can be used by the script to authenticate with Azure.
If you’re struggling with the apex record creation, it might be that you need to set up the zone since this isn’t done in the script. It’s assumed you’ve already got a zone and an apex record set up. However you do that is your choice – personally, I did it manually in the portal.
Hi,
You can use your live ID by putting in microsoftaccount\mymailaddress.com.
Also can I ask please how you stored your made your DDWRT credentails file?
Thanks, Dom
Hi Dominic, the answer is in the comment above yours. Basically it’s exactly the same as that comment, only the change of a name. All you’re doing is storing the credentials in an encrypted XML file format. I should point out though that an exported credential that’s encrypted on MachineA cannot be copied and used on MachineB, you would need to create the credential file on MachineB in order to use it there.
Thanks Lewis, scratch what I say about live credentials. Great article!
This is amazing, Is there a way to you can prevent the “confirmation” (Confirm
Are you sure you want to perform this action?
Performing the operation “Creating record set …” on target “updated”.
[Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is “Y”):) from being displayed?
I’ve tried using:
-Confirm:$false
-Confirm:0
-Force
-Overwrite
Any suggestions?
Has anyone had any issues running this in task scheduler in Windows 10? Powershell runs it fine and scheduler reports no issues when triggered however it just will not update! running under an admin context.
Thanks!
Hi,
Thanks for this perfect post and script! 🙂
Nice article! Just want to add, for those of you who have CLI Cisco devices, it’s also possible to update Azure DNS record through HTTP, like we usually do with Dyn (https://docs.microsoft.com/en-us/rest/api/dns/recordsets/createorupdate).