Skip to main content

Theoretical Foundation: Map an Existing Custom DNS Name to an App Service


1. Initial Intuition​

Imagine you created a business and have a physical address: "123 Flower Street, SΓ£o Paulo". But on your business cards, you put "www.mybusiness.com.br" because it's easier to remember and more professional. There's a redirection service that, when someone searches for your web address, knows it should go to "123 Flower Street".

In Azure, when you create an App Service, it gets a default address like myapp.azurewebsites.net. It works, but it's not professional and doesn't represent your brand. DNS (Domain Name System) is the internet's "redirection service": you configure your custom domain www.mycompany.com.br to point to the App Service, and then add that custom domain to the App Service so it accepts requests on that name.

Mapping a custom DNS name is the process of doing these two steps: proving to Azure that you own the domain, and then configuring DNS records to point to the App Service.


2. Context​

Why custom domains matter​

By default, all App Services have URLs in the format {name}.azurewebsites.net. This is acceptable for development and testing, but has limitations for production:

  • Doesn't represent brand identity
  • Changes if the app is renamed or recreated
  • Users distrust unrecognized URLs
  • SEO (Search Engine Optimization) is impacted
  • Azure Managed Certificate free SSL certificates don't work without custom domain

Custom domains are a prerequisite for custom TLS certificates and for any production application facing external users.

What depends on custom domains​

  • TLS/SSL certificates: the certificate must be issued for the custom domain
  • HTTPS: without custom domain, HTTPS only works for azurewebsites.net
  • Email and other services: subdomains of the same main domain
  • SEO and analytics: per-domain metrics

3. Building the Concepts​

3.1 How DNS works (essential concepts)​

DNS is a hierarchical name translation system. When a user types www.ecommerce.com.br in the browser, a series of queries happen:

100%
Scroll para zoom Β· Arraste para mover Β· πŸ“± Pinch para zoom no celular

Authoritative DNS is the server that has the definitive answer for a domain. It's where you create records (A, CNAME, TXT, etc.) that control where the domain points.

3.2 Relevant DNS record types​

CNAME (Canonical Name): points a name to another name. www.ecommerce.com.br CNAME to myapp.azurewebsites.net. DNS then resolves myapp.azurewebsites.net to get the final IP.

A Record: points a name directly to an IPv4 address. ecommerce.com.br A to 40.76.100.1.

TXT Record: arbitrary text associated with a DNS name. Used for ownership verification (proving you control the domain).

AAAA Record: same as A Record but for IPv6.

3.3 Critical CNAME limitation for root domain (apex/naked domain)​

The DNS standard doesn't allow using CNAME for the root domain (ecommerce.com.br without subdomain). This occurs because the DNS standard requires the root domain to have SOA and NS records, and CNAME cannot coexist with other records at the same level.

100%
Scroll para zoom Β· Arraste para mover Β· πŸ“± Pinch para zoom no celular

For the root domain, options are:

  1. Use an A Record pointing to the App Service IP (but the IP may change)
  2. Use ALIAS/ANAME Record (if your DNS provider supports it) which works like CNAME but is allowed at apex
  3. Use Azure DNS which supports Alias Records natively
  4. Redirect root domain to www at the DNS provider

3.4 The ownership verification process​

Azure doesn't allow anyone to add any domain to an App Service. Before adding a custom domain, you need to prove you own the domain. Azure offers two methods:

CNAME method (for subdomains): Create a CNAME record from www.ecommerce.com.br pointing to myapp.azurewebsites.net. This proves ownership because only someone who controls the domain's DNS can create this record.

TXT method (recommended to avoid downtime): Create a TXT record at asuid.www.ecommerce.com.br with the App Service Domain Verification ID value. This allows verifying ownership without pointing traffic to the App Service yet (useful for migrations).

The Domain Verification ID is a unique identifier of your App Service that Azure uses to confirm the domain belongs to you. It's found at Portal > App Service > Custom domains > Domain verification ID.

3.5 Complete process for subdomains vs. root domain​

For subdomain (www.ecommerce.com.br):

RecordNameTypeValue
Verificationasuid.wwwTXT<domain-verification-id>
MappingwwwCNAMEmyapp.azurewebsites.net

For root domain (ecommerce.com.br) with Azure DNS:

RecordNameTypeValue
VerificationasuidTXT<domain-verification-id>
Mapping@A (Alias)myapp.azurewebsites.net

4. Structural View​

Complete custom domain mapping flow​

100%
Scroll para zoom Β· Arraste para mover Β· πŸ“± Pinch para zoom no celular

How App Service receives requests with custom domain​

100%
Scroll para zoom Β· Arraste para mover Β· πŸ“± Pinch para zoom no celular

The Host header of the HTTP request is what Azure uses to route to the correct App Service. Multiple App Services can share the same Azure IP; the host header distinguishes which app the request should go to.


5. Practical Operation​

TTL (Time to Live) and DNS propagation​

When you create or modify DNS records, changes aren't immediate. A DNS record's TTL defines how many seconds other servers can cache the response. A TTL of 3600 means it can take up to 1 hour for the change to propagate globally.

Strategy for migration with minimal downtime:

  1. Weeks before: Reduce current record TTL to 60-300 seconds
  2. At migration time: Change record to new destination
  3. Since TTL is small, propagation happens in minutes instead of hours
  4. After confirming it works: increase TTL back to normal (3600+)

Traffic Manager and Front Door with custom domains​

If the App Service is behind Azure Traffic Manager or Azure Front Door, DNS mapping points to these services, not directly to the App Service:

With Azure Front Door:

  • CNAME www.ecommerce.com.br points to ecommerce.azurefd.net
  • Front Door routes to App Service backend
  • TLS certificates are managed by Front Door, not directly by App Service

With Traffic Manager:

  • CNAME www.ecommerce.com.br points to ecommerce.trafficmanager.net
  • Traffic Manager routes to App Service based on policies (latency, priority, etc.)

Non-obvious behaviors​

App Service validates the domain at each verification. When you try to add a custom domain, Azure queries DNS at that moment. If the DNS record hasn't propagated yet (high TTL), verification fails. This isn't an error: you need to wait for propagation.

Minimum tier for custom domains is Shared (D1). The Free tier (F1) doesn't support custom domains. You need at least the Shared tier to add a custom domain.

Wildcard custom domains require wildcard certificate. You can add a wildcard custom domain (*.ecommerce.com.br) to App Service, but you'll need a wildcard certificate to enable HTTPS on it.

Multiple custom domains can point to the same App Service. A single App Service can have dozens of custom domains. For example: www.company.com.br, company.com.br, store.company.com.br, all can point to the same app.

The domain verification ID is specific per App Service, not per domain. The same verification ID is used to verify any domain on that App Service. If you have 5 different domains on the same app, each one's TXT record uses the same verification ID.


6. Implementation Forms​

Azure Portal​

When to use: manual configuration, visual verification, single-use environments

Step-by-step via portal:

  1. Portal > App Service > Custom domains
  2. Copy the Domain verification ID (shown on screen)
  3. Go to your DNS provider (GoDaddy, Registro.br, Cloudflare, etc.)
  4. Create TXT record: asuid.www with value <domain-verification-id>
  5. Create CNAME record: www pointing to myapp.azurewebsites.net
  6. Wait for DNS propagation
  7. Return to portal > + Add custom domain
  8. Type www.ecommerce.com.br
  9. Click Validate; portal confirms records found
  10. If valid: Add custom domain

Azure CLI​

# View App Service Domain Verification ID
az webapp show \
--resource-group "rg-webapp" \
--name "ecommerce-frontend" \
--query "customDomainVerificationId" \
--output tsv

# List existing custom domains
az webapp config hostname list \
--resource-group "rg-webapp" \
--webapp-name "ecommerce-frontend" \
--output table

# Add custom domain to App Service
# (requires DNS records to be already configured and propagated)
az webapp config hostname add \
--resource-group "rg-webapp" \
--webapp-name "ecommerce-frontend" \
--hostname "www.ecommerce.com.br"

# Add root domain (apex domain)
az webapp config hostname add \
--resource-group "rg-webapp" \
--webapp-name "ecommerce-frontend" \
--hostname "ecommerce.com.br"

# Remove custom domain from App Service
az webapp config hostname delete \
--resource-group "rg-webapp" \
--webapp-name "ecommerce-frontend" \
--hostname "www.ecommerce.com.br"

# Check if DNS is configured correctly before adding
# (useful for debugging propagation issues)
az webapp show \
--resource-group "rg-webapp" \
--name "ecommerce-frontend" \
--query "outboundIpAddresses" \
--output tsv

# Check outbound IPs (for creating correct A records)
az webapp show \
--resource-group "rg-webapp" \
--name "ecommerce-frontend" \
--query "possibleOutboundIpAddresses" \
--output tsv

# If using Azure DNS: create records directly via CLI
# Create TXT record for verification
az network dns record-set txt add-record \
--resource-group "rg-dns" \
--zone-name "ecommerce.com.br" \
--record-set-name "asuid.www" \
--value "<domain-verification-id>"

# Create CNAME record for www
az network dns record-set cname set-record \
--resource-group "rg-dns" \
--zone-name "ecommerce.com.br" \
--record-set-name "www" \
--cname "ecommerce-frontend.azurewebsites.net" \
--ttl 300

# Create Alias record (ANAME) for apex domain via Azure DNS
az network dns record-set a create \
--resource-group "rg-dns" \
--zone-name "ecommerce.com.br" \
--name "@" \
--target-resource "/subscriptions/<sub-id>/resourceGroups/rg-webapp/providers/Microsoft.Web/sites/ecommerce-frontend"

# Create verification TXT for apex domain
az network dns record-set txt add-record \
--resource-group "rg-dns" \
--zone-name "ecommerce.com.br" \
--record-set-name "asuid" \
--value "<domain-verification-id>"

Azure PowerShell​

# View Domain Verification ID
(Get-AzWebApp -ResourceGroupName "rg-webapp" -Name "ecommerce-frontend").CustomDomainVerificationId

# Add custom domain
New-AzWebAppCustomHostnameBinding `
-ResourceGroupName "rg-webapp" `
-WebAppName "ecommerce-frontend" `
-Hostname "www.ecommerce.com.br"

# List custom domains
Get-AzWebAppCustomHostnameSsl `
-ResourceGroupName "rg-webapp" `
-WebAppName "ecommerce-frontend"

# If using Azure DNS: create records
# TXT record for verification
$txtRecord = New-AzDnsRecordConfig -Value "<domain-verification-id>"
New-AzDnsRecordSet `
-ResourceGroupName "rg-dns" `
-ZoneName "ecommerce.com.br" `
-Name "asuid.www" `
-RecordType TXT `
-Ttl 300 `
-DnsRecords $txtRecord

# CNAME record for www
$cnameRecord = New-AzDnsRecordConfig -Cname "ecommerce-frontend.azurewebsites.net"
New-AzDnsRecordSet `
-ResourceGroupName "rg-dns" `
-ZoneName "ecommerce.com.br" `
-Name "www" `
-RecordType CNAME `
-Ttl 300 `
-DnsRecords $cnameRecord

Bicep with Azure DNS​

// Azure DNS Zone (if managing DNS in Azure)
resource dnsZone 'Microsoft.Network/dnsZones@2018-05-01' = {
name: 'ecommerce.com.br'
location: 'global'
}

// TXT record for verification (www)
resource txtVerification 'Microsoft.Network/dnsZones/TXT@2018-05-01' = {
parent: dnsZone
name: 'asuid.www'
properties: {
TTL: 300
TXTRecords: [
{
value: [webApp.properties.customDomainVerificationId]
}
]
}
}

// CNAME record for www
resource cnameWww 'Microsoft.Network/dnsZones/CNAME@2018-05-01' = {
parent: dnsZone
name: 'www'
properties: {
TTL: 300
CNAMERecord: {
cname: '${webApp.name}.azurewebsites.net'
}
}
}

// Alias record for apex domain (@ record)
resource aliasApex 'Microsoft.Network/dnsZones/A@2018-05-01' = {
parent: dnsZone
name: '@'
properties: {
TTL: 300
targetResource: {
id: webApp.id // Alias pointing to App Service
}
}
}

// Add custom domain to App Service
resource customHostname 'Microsoft.Web/sites/hostNameBindings@2022-09-01' = {
parent: webApp
name: 'www.ecommerce.com.br'
properties: {
siteName: webApp.name
hostNameType: 'Verified'
}
dependsOn: [cnameWww, txtVerification]
}

7. Control and Security​

Verify DNS propagation before adding domain​

# Check if CNAME is propagated (from multiple perspectives)
# Via Linux/Mac:
dig CNAME www.ecommerce.com.br

# Via Windows:
nslookup -type=CNAME www.ecommerce.com.br

# Via online tool: https://dnschecker.org or https://whatsmydns.net

# Check if verification TXT is propagated
dig TXT asuid.www.ecommerce.com.br

# Check app resolution directly
dig A ecommerce-frontend.azurewebsites.net

Prevent domain takeover​

Domain takeover is when someone creates an App Service and maps a domain that points to a deleted App Service. If www.company.com.br has CNAME to company.azurewebsites.net and the company App Service is deleted, anyone can create a new App Service named company and receive the company's domain traffic.

Protection: always remove DNS records before or together with App Service deletion. Never leave CNAME records pointing to App Services that no longer exist.

# When deleting an app: first check and remove custom domains
az webapp config hostname list \
--resource-group "rg-webapp" \
--webapp-name "ecommerce-frontend" \
--output table

# Remove each custom domain before deleting the app
az webapp config hostname delete \
--resource-group "rg-webapp" \
--webapp-name "ecommerce-frontend" \
--hostname "www.ecommerce.com.br"

--hostname "www.ecommerce.com.br"


---

## 8. Decision Making

### Choosing the verification method

| Situation | Method | Reason |
|---|---|---|
| New domain, new app | CNAME (www) + TXT | One CNAME already verifies and redirects |
| Zero downtime migration | TXT only (first) | Verifies without redirecting traffic yet |
| Root domain (apex) | TXT + A or ALIAS | CNAME not allowed on apex |
| Wildcard | TXT on asuid + wildcard CNAME | Wildcard CNAME covers all subdomains |

### DNS Manager: Azure DNS vs. External Registrar

| Criteria | Azure DNS | External Registrar (GoDaddy, Cloudflare) |
|---|---|---|
| Native App Service integration | Native Alias Records | Requires manual A record |
| Centralized management | Yes, everything in Azure | Separate DNS |
| Propagation | Global DNS, default TTL 3600 | Depends on provider |
| Apex CNAME-like support | Yes (Alias Records) | Depends: Cloudflare yes, GoDaddy no |
| Cost | ~$0.50/zone + $0.40/million queries | Usually included with registration |
| Recommendation | Recommended if possible | Use if domain is already there and works |

---

## 9. Best Practices

**Always use the TXT method for verification in migrations.**
The TXT method verifies ownership without changing where traffic goes. This allows: verifying the domain on the destination App Service, configuring the TLS certificate, testing via local hosts file, and only then changing the CNAME to redirect production traffic.

**Reduce TTL before migrations.**
A TTL of 3600 means propagation of up to 1 hour. Weeks before migration, reduce to 60-300 seconds. After confirming it works, return to 3600.

**Use Azure DNS to leverage Alias Records.**
Azure DNS supports Alias Records natively for Azure resources, including App Service. This solves the apex domain problem without needing static IPs.

**Document all custom domains and their DNS records.**
When an app is migrated or deleted, it's easy to forget to remove DNS records. Maintain an inventory: which domain, which CNAME/A Record, which App Service, creation date.

**Configure alerts for anomalous DNS responses.**
Azure Monitor can alert when a custom domain starts returning errors (NXDOMAIN, SERVFAIL), which may indicate domain expiration, accidental DNS changes, or domain takeover.

---

## 10. Common Errors

| Error | Why it happens | How to avoid |
|---|---|---|
| Verification fails even after configuring DNS | DNS propagation not yet complete | Wait for propagation; check current TTL; use dig/nslookup |
| CNAME created for apex domain without working | DNS RFC doesn't allow CNAME on apex | Use Azure DNS with Alias Records or A record with static IP |
| App Service deleted but CNAME still exists | DNS not updated after deletion | Remove custom domain from app BEFORE deleting; update DNS |
| Free tier without custom domain support | Free tier limitation | Use minimum Shared (D1) for custom domains |
| Wrong Domain Verification ID in TXT | Incorrect ID copy | Copy ID directly from portal, without spaces |
| TXT created with wrong name | `asuid.www` vs `asuid` | For www: `asuid.www`; for apex: `asuid` without subdomain |
| App accessible on azurewebsites.net but not on custom domain | Binding not created in App Service | After configuring DNS, also add domain via CLI/portal |

### The most common production error

Performing migration without reducing TTL in advance. With TTL of 3600, after changing the CNAME to point to the new App Service, some users continue being sent to the old server for up to an hour. In e-commerce, this means lost orders and degraded experience. Always reduce TTL to 60 seconds at least 48 hours before any domain migration.

---

## 11. Operation and Maintenance

### Check status of all custom domains

```bash
# List all apps with their custom domains
az graph query -q "
Resources
| where type == 'microsoft.web/sites'
| mv-expand hostNames = properties.hostNames
| where hostNames !endswith 'azurewebsites.net'
| where hostNames !endswith 'scm.azurewebsites.net'
| project AppName=name, RG=resourceGroup, CustomDomain=hostNames"

# Verify if a domain is correctly mapped
DOMAIN="www.ecommerce.com.br"
EXPECTED_CNAME="ecommerce-frontend.azurewebsites.net"

ACTUAL=$(dig CNAME "$DOMAIN" +short)
if [ "$ACTUAL" = "${EXPECTED_CNAME}." ]; then
echo "DNS OK: $DOMAIN -> $ACTUAL"
else
echo "ALERT: DNS mismatch for $DOMAIN. Expected: $EXPECTED_CNAME, Current: $ACTUAL"
fi

Domain renewal and expiration monitoring​

Custom domains depend on the domain registration being active. If the domain expires at the registrar, all associated custom domains stop working.

Configure alerts in your registrar for automatic renewal and keep payment information updated. Domain expiration is one of the most catastrophic and at the same time most preventable failures.


12. Integration and Automation​

Automatic DNS + custom domain provisioning via pipeline​

# Azure DevOps: create DNS records and custom domain as part of deployment
steps:
- task: AzureCLI@2
displayName: 'Configure DNS and Custom Domain'
inputs:
azureSubscription: 'prod-subscription'
scriptType: 'bash'
scriptLocation: 'inlineScript'
inlineScript: |
APP_NAME="ecommerce-frontend"
RESOURCE_GROUP="rg-webapp"
DNS_ZONE="ecommerce.com.br"
DNS_RG="rg-dns"
SUBDOMAIN="www"

# Get Domain Verification ID
VERIFICATION_ID=$(az webapp show \
--resource-group "$RESOURCE_GROUP" \
--name "$APP_NAME" \
--query "customDomainVerificationId" -o tsv)

# Create TXT record for verification
az network dns record-set txt add-record \
--resource-group "$DNS_RG" \
--zone-name "$DNS_ZONE" \
--record-set-name "asuid.${SUBDOMAIN}" \
--value "$VERIFICATION_ID"

# Create CNAME record
az network dns record-set cname set-record \
--resource-group "$DNS_RG" \
--zone-name "$DNS_ZONE" \
--record-set-name "$SUBDOMAIN" \
--cname "${APP_NAME}.azurewebsites.net" \
--ttl 300

# Wait for propagation (in automated environment, retry may be needed)
sleep 60

# Add custom domain to App Service
az webapp config hostname add \
--resource-group "$RESOURCE_GROUP" \
--webapp-name "$APP_NAME" \
--hostname "${SUBDOMAIN}.${DNS_ZONE}"

echo "Custom domain ${SUBDOMAIN}.${DNS_ZONE} configured successfully"

Terraform for DNS + custom domain​

# Azure DNS Zone
resource "azurerm_dns_zone" "main" {
name = "ecommerce.com.br"
resource_group_name = azurerm_resource_group.dns.name
}

# TXT record for verification
resource "azurerm_dns_txt_record" "verification" {
name = "asuid.www"
zone_name = azurerm_dns_zone.main.name
resource_group_name = azurerm_resource_group.dns.name
ttl = 300

record {
value = azurerm_linux_web_app.main.custom_domain_verification_id
}
}

# CNAME record
resource "azurerm_dns_cname_record" "www" {
name = "www"
zone_name = azurerm_dns_zone.main.name
resource_group_name = azurerm_resource_group.dns.name
ttl = 300
record = "${azurerm_linux_web_app.main.name}.azurewebsites.net"
}

# Custom domain in App Service
resource "azurerm_app_service_custom_hostname_binding" "www" {
hostname = "www.ecommerce.com.br"
app_service_name = azurerm_linux_web_app.main.name
resource_group_name = azurerm_resource_group.app.name

depends_on = [
azurerm_dns_txt_record.verification,
azurerm_dns_cname_record.www
]
}

13. Final Summary​

Essential points:

  • Mapping a custom domain involves two steps: configure DNS records at the provider, and add the domain to App Service
  • Azure requires ownership verification via TXT record (asuid.{subdomain}) before accepting the custom domain
  • For subdomains: use CNAME pointing to {appname}.azurewebsites.net
  • For root domain (apex): CNAME is not allowed by DNS standard; use A record with App Service IP, or Alias Record if DNS provider supports (Azure DNS supports natively)
  • Custom domains require minimum Shared (D1) tier; Free (F1) tier doesn't support

Critical differences:

  • CNAME vs. A Record: CNAME dynamically resolves to App Service current IP (recommended for subdomains); A Record needs manual update if IP changes
  • CNAME vs. Alias/ANAME: CNAME not allowed on apex; Alias/ANAME resolves like CNAME but is allowed on apex (DNS provider specific)
  • CNAME vs. TXT method for verification: CNAME as verification already redirects traffic; TXT verifies without redirecting (ideal for migrations)
  • TTL 300 vs. TTL 3600: Low TTL allows fast migration; high TTL means slow propagation

What needs to be remembered for AZ-104:

  • The Domain Verification ID is in Portal > App Service > Custom domains
  • The verification TXT record for www.domain.com is created at asuid.www (not at www)
  • For apex domain domain.com, the TXT record goes at asuid (no subdomain)
  • CLI command to add: az webapp config hostname add --hostname <domain>
  • Free tier doesn't support custom domains; minimum is Shared
  • DNS propagation can take up to TTL value in seconds; for debugging, use dig or nslookup
  • Domain takeover happens when CNAME points to deleted App Service; always remove DNS when deleting apps