Building a Custom Dynamic DNS Service with Azure
Milestone 2a — DNS Migration and Home Lab Hosting
Published: March 2026 | Tags: Azure, DNS, PowerShell, Home Lab, Dynamic DNS
Overview
This guide covers how to build your own Dynamic DNS (DDNS) service using Azure DNS and a PowerShell script. If you want to host a website from your home lab but your ISP gives you a dynamic public IP address, this is the solution.
Rather than paying for a commercial DDNS service, we use Azure DNS — which we already have as part of our Microsoft 365 / Azure subscription — and a lightweight PowerShell script that runs on a schedule and keeps our DNS record updated automatically.
The Problem
Most home internet connections use a dynamic public IP address — meaning your ISP can change it at any time. Hosting a website at home with a dynamic IP creates two challenges:
The solution: use Azure DNS as your DNS provider and write a script that automatically updates the A record whenever your IP changes. Azure DNS provides REST APIs that make this straightforward.
What You Need — Before You Start
What You Will Have When Done
Step 1 — Create the Azure DNS Zone
In the Azure Portal, click Create a resource and search for DNS Zone.
Figure 1 — Create a resource button in Azure Portal
Figure 2 — Search for DNS Zone in the marketplace
Figure 3 — Select Microsoft DNS Zone
Click Create, then fill in the form:
Figure 4 — DNS Zone creation form
Figure 5 — Review + Create summary
Click Review + Create, wait for validation, then click Create. Once deployment completes:
Figure 6 — Deployment complete
Step 2 — Mirror Your Email DNS Records
If Microsoft 365 is handling your email (as it is for kodakcafe.com), you need to create matching DNS records in Azure DNS before switching your nameservers. This ensures email keeps working without interruption.
Click Record Sets, then Add to create the following three records:
Figure 7 — Creating the MX record for Microsoft 365
Figure 8 — Creating the SPF TXT record
Figure 9 — Creating the autodiscover CNAME record
After adding all three records your DNS zone should look like this:
Figure 10 — DNS zone with all email records in place
Step 3 — Update Nameservers in Squarespace
Now that Azure DNS has all your email records, it is safe to point your domain at Azure. In Squarespace, navigate to your domain DNS settings and update the nameservers:
Figure 11 — Squarespace DNS settings before nameserver change
Figure 12 — Updating nameservers to Azure DNS
Replace the existing nameservers with these four Azure DNS nameservers:
ns1-09.azure-dns.com
ns2-09.azure-dns.net
ns3-09.azure-dns.org
ns4-09.azure-dns.info
Save the changes. Propagation typically takes 15-30 minutes. Verify with:
nslookup -type=ns kodakcafe.com
Expected output — you should see all four Azure nameservers listed. Also verify email is routing correctly:
nslookup -type=mx kodakcafe.com
Figure 13 — DNS propagation confirmed, Azure nameservers active
Step 4 — Create the Home Lab A Record
Add an A record for your home server. We will use a placeholder IP for now — the DDNS script will update it with your real public IP automatically.
Figure 14 — Creating the home A record with placeholder IP
Step 5 — Create the Service Principal
The DDNS script needs to authenticate to Azure and update DNS records. We use a Service Principal — a dedicated identity with the minimum permissions needed. This follows the principle of least privilege.
The Service Principal will have exactly one permission: DNS Zone Contributor on the rg-dns-kodakcafe resource group. Nothing else.
Step 5a — Register the Application in Entra ID
From the Azure Portal home, click Entra ID → App registrations → New registration:
Figure 15 — Entra ID icon on Azure Portal home
Figure 16 — App registration form — name it sp-ddns-kodakcafe
After registering, document the Client ID and Tenant ID — you will need these in the script:
Figure 17 — Client ID and Tenant ID on the app registration overview
Step 5b — Create a Client Secret
Click Certificates & secrets → New client secret. Document the secret value immediately — it is only shown once:
Figure 18 — Client secret value (copy this immediately, it will not be shown again)
Step 5c — Assign DNS Zone Contributor Role
Navigate to your DNS zone resource group (rg-dns-kodakcafe) → Access control (IAM) → Add role assignment:
Figure 19 — Finding the DNS Zone Contributor role
Figure 20 — Selecting the sp-ddns-kodakcafe service principal as the member
Review and assign. The Service Principal now has exactly the permissions it needs — and nothing more:
Figure 21 — Role assignment confirmed: DNS Zone Contributor
Here is the full authentication flow the script uses:
Figure 22 — How the script authenticates and updates DNS
Step 6 — Create and Configure the PowerShell Script
Create a file at C:\ddns\ddnsupdate.ps1 on your home server with the following script. Fill in your own values for TenantId, ClientId, ClientSecret, and SubscriptionId:
$ErrorActionPreference = "Stop"
# ================= CONFIG =================
$TenantId = "your-tenant-id-here"
$ClientId = "your-client-id-here"
$ClientSecret = "your-client-secret-here"
$SubscriptionId = "your-subscription-id-here"
$ResourceGroup = "rg-dns-kodakcafe"
$ZoneName = "kodakcafe.com"
$RecordName = "home"
$Ttl = 300
# ==========================================
$LogDir = "C:\Scripts\DDNS\Logs"
New-Item -ItemType Directory -Path $LogDir -Force | Out-Null
$LogFile = Join-Path $LogDir ("ddns-" + (Get-Date -Format "yyyyMMdd") + ".log")
function Log($msg) {
Add-Content -Path $LogFile -Value "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") $msg"
}
function Get-PublicIP { Invoke-RestMethod "https://api.ipify.org" }
function Get-Token {
$body = @{ grant_type = "client_credentials"; client_id = $ClientId;
client_secret = $ClientSecret; resource = "https://management.azure.com/" }
(Invoke-RestMethod -Method POST -ContentType "application/x-www-form-urlencoded"
-Uri "https://login.microsoftonline.com/$TenantId/oauth2/token" -Body $body).access_token
}
$ip = (Get-PublicIP).ToString().Trim()
Log "Detected public IP: $ip"
$token = Get-Token
$uri = "https://management.azure.com/subscriptions/${SubscriptionId}/resourceGroups/
${ResourceGroup}/providers/Microsoft.Network/dnsZones/${ZoneName}/A/${RecordName}
?api-version=2018-05-01"
$headers = @{ Authorization = "Bearer $token" }
$current = Invoke-RestMethod -Method GET -Uri $uri -Headers $headers
$currentIp = $current.properties.ARecords[0].ipv4Address
Log "Current DNS IP: $currentIp"
if ($currentIp -eq $ip) { Log "No update required."; exit 0 }
$payload = @{ properties = @{ TTL = 300; ARecords = @(@{ ipv4Address = $ip }) } } | ConvertTo-Json -Depth 5
Invoke-RestMethod -Method PUT -Uri $uri -Headers $headers -Body $payload -ContentType "application/json"
Log "DNS updated from $currentIp to $ip"
Test the script manually first:
powershell.exe -ExecutionPolicy Bypass -File C:\ddns\ddnsupdate.ps1
Verify by checking your public IP at whatismyipaddress.com, then running:
nslookup home.kodakcafe.com
The IP addresses should match.
Step 7 — Schedule the Script with Task Scheduler
Open Task Scheduler on your home server. Right-click and select Create Task (not Create Basic Task):
Figure 23 — Task Scheduler — use Create Task, not Create Basic Task
Configure the task:
Figure 24 — General tab — name the task and set security options
On the Triggers tab, add a new trigger set to repeat every 15 minutes indefinitely:
Figure 25 — Trigger configuration — repeat every 15 minutes
On the Actions tab, add a new action:
Figure 26 — Action configuration for the PowerShell script
Save the task. Test it by right-clicking → Run:
Figure 27 — Task running successfully
Verifying Everything Works
Run these three commands to confirm the full setup is working:
# Confirm Azure is your DNS provider
nslookup -type=ns kodakcafe.com
# Confirm email is routing to Microsoft 365
nslookup -type=mx kodakcafe.com
# Confirm home server IP is correct
nslookup home.kodakcafe.com
Migrating from Home Lab to Azure App Service
Milestone 2b — Custom Domain, SSL, and Decommissioning DDNS
Published: March 2026 | Tags: Azure, App Service, Custom Domain, SSL, DNS
Overview
With KodakCafe deployed on Azure App Service (see Milestone 1), the next step is pointing our custom domain kodakcafe.com at the new Azure-hosted site and enabling HTTPS with a free managed SSL certificate.
This guide also covers decommissioning the DDNS setup from Part 1 — since we are no longer hosting the site at home, that infrastructure is no longer needed.
What You Need — Before You Start
Step 1 — Get the App Service IP Address
Run this command in Terminal to get the current IP of your App Service:
nslookup kodakcafe-dnd9fuagfzf8fgfg.eastus-01.azurewebsites.net
The IP address at the end of the output is what we need. In our case: 40.71.11.140
Step 2 — Update DNS Records
In the Azure Portal, navigate to your DNS zone (rg-dns-kodakcafe → kodakcafe.com → Recordsets) and make these three changes:
Change 1 — Update the Root A Record
Click the pencil icon next to the @ A record and update:
Change 2 — Update the www CNAME
Click the pencil icon next to the www CNAME record and update the value to:
kodakcafe-dnd9fuagfzf8fgfg.eastus-01.azurewebsites.net
Change 3 — Delete the home A Record
Click the trash icon next to the home A record and confirm deletion. This record was used for DDNS home lab hosting and is no longer needed.
Your final DNS record set should look like this:
Step 3 — Add Custom Domain Verification Record
Azure requires a TXT record to verify you own the domain before it will issue an SSL certificate. In the App Service portal, go to Settings → Custom Domains and copy the Custom Domain Verification ID.
Then add a new TXT record in your DNS zone:
Step 4 — Add the Custom Domain in App Service
In the App Service portal, go to Settings → Custom Domains → + Add custom domain. Fill in the form:
Azure will show a domain validation table. Both the A record and TXT record should validate. Click Add.
Step 5 — Add SSL Binding
After the custom domain is added, it will show "No binding" status. Click Add binding next to kodakcafe.com:
Click Validate, then Add. Azure will generate and install a free SSL certificate automatically. This takes 2-5 minutes.
Watch the notifications bell in the top right for the completion message:
Step 6 — Verify Everything
Open your browser and navigate to https://kodakcafe.com. You should see:
Also verify from Terminal that DNS is resolving correctly:
nslookup kodakcafe.com 8.8.8.8
This should return 40.71.11.140 (or your App Service IP).
Troubleshooting — DNS Propagation Delays
The most common issue when updating DNS is propagation delay. If Azure shows errors about the A record not matching, it is because DNS servers cached your old record.
Key things to know:
What You Built
Next Steps — Milestone 3
KodakCafe | kodakcafe.com | Technology & Azure Series