Azure DNS

Published: Apr 30, 2021 by Isaac Johnson

Azure DNS is a simple hosting service Microsoft offers in Azure.  While it does not have TLD registration built in, it does serve and manage namespaces pointed to it.  This can be used for a variety of things including App Service domains.  Let’s point a fresh domain to Azure DNS then use it on an App Service presently running in Azure.

Setup

The first step you need to do is to setup an Azure DNS zone.

then enter the Domain you are adding

and once created, you’ll be presented with a page that shows the NS you will need to add at your registrar.

Updating Registrar

Next we change Gandi.net to use Azure DNS hosts we see above

Using Azure DNS: Appling a CNAME to Azurewebsites endpoints

Say you have an App Service or Azure Function and would like to point your own domain name to it.

Part of adding a custom domain to an App is to verify domain ownership:

Back on Azure DNS side, we add a record set for our app

And to verify, we use the TXT entry from the above Domain Ownership image.

Now we can see the listing as such:

This can take up to 48h to verify.  However, when i tested, i found it was immediately able to validate (likely since Microsoft checks Azure DNS first):

So now that we validated, click add custom domain to finish.

Unfortunately if we want to get valid TLS we have to upgrade to a nonshared tier (not D1 or F1) AND pay $70 or more for an App Service cert:

https://docs.microsoft.com/en-us/azure/app-service/configure-ssl-certificate#import-an-app-service-certificate

That said, we can see that we have standard HTTP now working on an azure function using Azure DNS:

http://tpkfn.tpk.pw/

But Isaac, you say, I don’t want to spend $70 a year to buy a cert for a free tier function.  I get it, i do…  So let’s use LetsEncrpt to get us a free one

Getting Free Certs

Download certbot or install.  Here i’ll install wiht homebrew

$ brew install certbot
Error: homebrew-core is a shallow clone. To `brew update` first run:
  git -C "/usr/local/Homebrew/Library/Taps/homebrew/homebrew-core" fetch --unshallow
This restriction has been made on GitHub's request because updating shallow
clones is an extremely expensive operation due to the tree layout and traffic of
Homebrew/homebrew-core. We don't do this for you automatically to avoid
repeatedly performing an expensive unshallow operation in CI systems (which
should instead be fixed to not use shallow clones). Sorry for the inconvenience!
==> Downloading https://homebrew.bintray.com/bottles/augeas-1.12.0.catalina.bott
==> Downloading from https://d29vzk4ow07wi7.cloudfront.net/00a45b8b446df0a95c2c4
######################################################################## 100.0%
==> Downloading https://homebrew.bintray.com/bottles/dialog-1.3-20201126.catalin
==> Downloading from https://d29vzk4ow07wi7.cloudfront.net/6fc24c87e6cf32e7702c0
######################################################################## 100.0%
==> Downloading https://homebrew.bintray.com/bottles/certbot-1.10.1.catalina.bot
==> Downloading from https://d29vzk4ow07wi7.cloudfront.net/ffce387c91071c6480790
######################################################################## 100.0%
==> Installing dependencies for certbot: augeas and dialog
==> Installing certbot dependency: augeas
==> Pouring augeas-1.12.0.catalina.bottle.tar.gz
==> Caveats
Lenses have been installed to:
  /usr/local/share/augeas/lenses/dist
==> Summary
🍺 /usr/local/Cellar/augeas/1.12.0: 473 files, 3.5MB
==> Installing certbot dependency: dialog
==> Pouring dialog-1.3-20201126.catalina.bottle.tar.gz
🍺 /usr/local/Cellar/dialog/1.3-20201126: 14 files, 919KB
==> Installing certbot
==> Pouring certbot-1.10.1.catalina.bottle.tar.gz
🍺 /usr/local/Cellar/certbot/1.10.1: 1,599 files, 15MB
==> `brew cleanup` has not been run in 30 days, running now...
Removing: /Users/johnsi10/Library/Caches/Homebrew/azure-cli--2.16.0.catalina.bottle.tar.gz... (25.7MB)
Removing: /Users/johnsi10/Library/Caches/Homebrew/ffmpeg--4.3.1_4.catalina.bottle.tar.gz... (20.7MB)
Removing: /Users/johnsi10/Library/Caches/Homebrew/glib--2.66.2_1.catalina.bottle.tar.gz... (4.5MB)
Removing: /Users/johnsi10/Library/Caches/Homebrew/go--1.15.5.catalina.bottle.tar.gz... (153.9MB)
Removing: /Users/johnsi10/Library/Caches/Homebrew/gobject-introspection--1.66.1_1.catalina.bottle.tar.gz... (1.8MB)
Removing: /Users/johnsi10/Library/Caches/Homebrew/libass--0.15.0.catalina.bottle.tar.gz... (216.6KB)
Removing: /Users/johnsi10/Library/Caches/Homebrew/libbluray--1.2.1.catalina.bottle.tar.gz... (1MB)
Removing: /Users/johnsi10/Library/Caches/Homebrew/libtiff--4.1.0_1.catalina.bottle.tar.gz... (1.1MB)
Removing: /Users/johnsi10/Library/Caches/Homebrew/oci-cli--2.16.1.catalina.bottle.tar.gz... (10.9MB)
Removing: /Users/johnsi10/Library/Caches/Homebrew/python@3.8--3.8.6_1.catalina.bottle.tar.gz... (16.8MB)
Removing: /Users/johnsi10/Library/Caches/Homebrew/python@3.8--3.8.6_2.catalina.bottle.tar.gz... (16.8MB)
Removing: /Users/johnsi10/Library/Caches/Homebrew/python@3.9--3.9.0_5.catalina.bottle.tar.gz... (17.9MB)
Removing: /Users/johnsi10/Library/Caches/Homebrew/python@3.9--3.9.0_1.catalina.bottle.tar.gz... (17.9MB)
Removing: /Users/johnsi10/Library/Caches/Homebrew/rke--1.2.3.catalina.bottle.tar.gz... (10.1MB)
Removing: /Users/johnsi10/Library/Caches/Homebrew/sqlite--3.34.0.catalina.bottle.tar.gz... (2.0MB)
Removing: /Users/johnsi10/Library/Caches/Homebrew/thefuck--3.30_1.catalina.bottle.tar.gz... (2.3MB)
Removing: /Users/johnsi10/Library/Caches/Homebrew/x264--r3027_1.catalina.bottle.tar.gz... (2.3MB)
Removing: /Users/johnsi10/Library/Caches/Homebrew/x265--3.4_1.catalina.bottle.tar.gz... (5MB)
Removing: /Users/johnsi10/Library/Logs/Homebrew/terraform... (119B)
==> Caveats
==> augeas
Lenses have been installed to:
  /usr/local/share/augeas/lenses/dist

Verification

$ certbot --version
certbot 1.10.1

Run certbot in manual mode with DNS challenge

$ sudo certbot certonly --manual -d tpkfn.tpk.pw --preferred-challenges dns
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator manual, Installer None
Requesting a certificate for tpkfn.tpk.pw
Performing the following challenges:
dns-01 challenge for tpkfn.tpk.pw

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please deploy a DNS TXT record under the name
_acme-challenge.tpkfn.tpk.pw with the following value:

1L4fqN5JXLjwX4zlLbRzy8ewvh14XmdOsZaR_H-mo2Q

Before continuing, verify the record is deployed.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Press Enter to Continue

Then create in Azure DNS:

Then hit enter in certbot to continue

Waiting for verification...
Cleaning up challenges

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/tpkfn.tpk.pw/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/tpkfn.tpk.pw/privkey.pem
   Your cert will expire on 2021-07-15. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot
   again. To non-interactively renew *all* of your certificates, run
   "certbot renew"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate
   Donating to EFF: https://eff.org/donate-le

And now we have a valid SSL cert:

$ sudo ls /etc/letsencrypt/live/tpkfn.tpk.pw/
README cert.pem	chain.pem	fullchain.pem	privkey.pem

Now we can create a password protected PFX to upload

$ sudo openssl pkcs12 -export -out tpkfn.pfx -inkey /etc/letsencrypt/live/tpkfn.tpk.pw/privkey.pem -in /etc/letsencrypt/live/tpkfn.tpk.pw/cert.pem -certfile /etc/letsencrypt/live/tpkfn.tpk.pw/chain.pem -password pass:tpkftw8869

I just made up a password of tpkftw8869.. Any string will do

$ ls -l *.pfx
-rw-r--r-- 1 root staff 4085 Apr 16 06:49 tpkfn.pfx

Appling the Cert

Back in Azure, in the Function App, Settings/Custom Domains, lets add a binding on the “not secure” area

Upload a pfx file

We then browse and pick the file, and also set the same password (tpkftw8869)

On the next screen, just choose the values in the dropdowns (should just be one)

And click add binding…

We now see that is secured (at least for a year)

Verification

And what’s more, i can see this was holding to my low/free tier dynamic plan EastUSLinuxDynamicPlan (Y1: 0)

The whole lot of which is effectively free

This Y1 plan has some limitations:

$ az appservice plan show -n EastUSLinuxDynamicPlan -g theprincessking
{
  "freeOfferExpirationTime": null,
  "geoRegion": "East US",
  "hostingEnvironmentProfile": null,
  "hyperV": false,
  "id": "/subscriptions/d955c0ba-13dc-44cf-a29a-8fed74cbb22d/resourceGroups/theprincessking/providers/Microsoft.Web/serverfarms/EastUSLinuxDynamicPlan",
  "isSpot": false,
  "isXenon": false,
  "kind": "functionapp",
  "location": "East US",
  "maximumElasticWorkerCount": 1,
  "maximumNumberOfWorkers": 0,
  "name": "EastUSLinuxDynamicPlan",
  "numberOfSites": 1,
  "perSiteScaling": false,
  "provisioningState": "Succeeded",
  "reserved": true,
  "resourceGroup": "theprincessking",
  "sku": {
    "capabilities": null,
    "capacity": 0,
    "family": "Y",
    "locations": null,
    "name": "Y1",
    "size": "Y1",
    "skuCapacity": null,
    "tier": "Dynamic"
  },
  "spotExpirationTime": null,
  "status": "Ready",
  "subscription": "d955c0ba-13dc-44cf-a29a-8fed74cbb22d",
  "tags": null,
  "targetWorkerCount": 0,
  "targetWorkerSizeId": 0,
  "type": "Microsoft.Web/serverfarms",
  "workerTierName": null
}

Since Y1 is not listed when creating an app service plan, i had to do some looking to see that it’s the Dynamic plan associated to the consumption model (pay for what you use)

From https://stackoverflow.com/questions/47522539/server-farm-service-plan-skus

Summary

Here we setup Azure DNS then used it to point a subdomain at a simple App Service plan serverless app.  In order to secure it, cheaply, we used certbot locally to get a free LetsEncrypt SSL cert and applied as a binding to the app enabling properly signed SSL traffic on our DNS to route to our app.

It was pointed out to my by a colleague that Azure does have an “App Service Domain” option now to purchase a new TLD through Azure.  However, based on the Terms of Service, this is really a marketplace option hosted by godaddy

azure dns serverless

Isaac Johnson

Isaac Johnson

Cloud Solutions Architect

Isaac is a CSA and DevOps engineer who focuses on cloud migrations and devops processes. He also is a dad to three wonderful daughters (hence the references to Princess King sprinkled throughout the blog).

Theme built by C.S. Rhymes