Skip to content

How to use Azure DNS

Luca Piccirillo edited this page Dec 7, 2025 · 10 revisions

Prerequisites

You need the Azure CLI 2.0 tools to create a service principal for access to your DNS Zone.

Either install Azure CLI 2.0 locally or use the Azure Cloud Shell in Bash mode.

(See the Azure Command-Line Interface (CLI) documentation for more details)

Log-in to Azure

(Not required when using the Azure Cloud Shell)

az login 
[
  {
    "cloudName": "AzureCloud",
    "id": "12345678-9abc-def0-1234-567890abcdef",
    "isDefault": true,
    "name": "myAzureSubscription",
    "state": "Enabled",
    "tenantId": "11111111-2222-3333-4444-555555555555",
    "user": {
      "name": "[email protected]",
      "type": "user"
    }
  }
]

Set your Azure subscription if you have more than one

az account list
[
  {
    "cloudName": "AzureCloud",
    "homeTenantId": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
    "id": "baaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
    "isDefault": true,
    "managedByTenants": [],
    "name": "Subscription A",
    "state": "Enabled",
    "tenantId": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
    "user": {
      "cloudShellID": true,
      "name": "[email protected]",
      "type": "user"
    }
  },
  {
    "cloudName": "AzureCloud",
    "homeTenantId": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
    "id": "caaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
    "isDefault": false,
    "managedByTenants": [],
    "name": "Subscription B",
    "state": "Enabled",
    "tenantId": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
    "user": {
      "cloudShellID": true,
      "name": "[email protected]",
      "type": "user"
    }
  }
]
az account set --subscription "Subscription B"

List your DNS Zones

az network dns zone list
[
  {
    "etag": "00000002-0000-0000-f641-73c64955d301",
    "id": "/subscriptions/12345678-9abc-def0-1234-567890abcdef/resourceGroups/exampledns_rg/providers/Microsoft.Network/dnszones/example.com",
    "location": "global",
    "maxNumberOfRecordSets": 5000,
    "name": "example.com",
    "nameServers": [
      "ns1-02.azure-dns.com.",
      "ns2-02.azure-dns.net.",
      "ns3-02.azure-dns.org.",
      "ns4-02.azure-dns.info."
    ],
    "numberOfRecordSets": 11,
    "resourceGroup": "exampledns_rg",
    "tags": {},
    "type": "Microsoft.Network/dnszones"
  }
]

Create a service principal

The service principal is used to grant acme.sh access to the _acme-challenge TXT record within the DNS Zone using the id value from the previous commands output

(See the az ad sp create-for-rbac documentation for more details)

This grants the "DNS Zone Contributor" role only on the exact scope of the _acme-challenge.example.com:

az ad sp create-for-rbac --name  "AcmeDnsValidator" --role "DNS Zone Contributor" --scopes \
    /subscriptions/12345678-9abc-def0-1234-567890abcdef/resourceGroups/exampledns_rg/providers/Microsoft.Network/dnszones/example.com/TXT/_acme-challenge

If you want to issue certificates for subdomain.example.com the scope changes as follow:

az ad sp create-for-rbac --name  "AcmeDnsValidator" --role "DNS Zone Contributor" --scopes \
    /subscriptions/12345678-9abc-def0-1234-567890abcdef/resourceGroups/exampledns_rg/providers/Microsoft.Network/dnszones/example.com/TXT/_acme-challenge.subdomain
{
  "appId": "3b5033b5-7a66-43a5-b3b9-a36b9e7c25ed",
  "displayName": "AcmeDnsValidator",
  "name": "http://AcmeDnsValidator",
  "password": "e.L8Q~4jGhWHheCKjdRzw3gyBBwOmrTyYF9NYbxs",
  "tenant": "11111111-2222-3333-4444-555555555555"
}
Note: Dealing with multiple DNS Zones

If you are managing certificates for multiple DNS Zones, you can create the service principal with multiple scopes.

For example, if you are managing certificates for both example.com and example.edu, you can create the service principal with both scopes:

az ad sp create-for-rbac --name  "AcmeDnsValidator" --role "DNS Zone Contributor" --scopes \
    /subscriptions/12345678-9abc-def0-1234-567890abcdef/resourceGroups/exampledns_rg/providers/Microsoft.Network/dnszones/example.com/TXT/_acme-challenge \
    /subscriptions/12345678-9abc-def0-1234-567890abcdef/resourceGroups/exampledns2_rg/providers/Microsoft.Network/dnszones/example.edu/TXT/_acme-challenge

Or if the service principal has already been created, you can grant it access to the additional scope:

az ad sp list --filter "displayname eq 'AcmeDnsValidator'" | grep '^    \"id\":'

(The grep above is assuming a json array of nested lists is returned with a tab size of two spaces and is finding the top-level id)

    "id": "daaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
az role assignment create --assignee daaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa --role "DNS Zone Contributor" --scope \
    /subscriptions/12345678-9abc-def0-1234-567890abcdef/resourceGroups/deleteme_rg/providers/Microsoft.Network/dnszones/example.edu/TXT/_acme-challenge
Note: Dealing with multiple credentials

By default acme.sh saves credentials in ~/.acme.sh/account.conf and these credentials are used for all DNS zones.

If you want to use different credentials, use the --accountconf switch to specify a configuration file.

Limit access permissions to TXT records

In Azure DNS you can further narrow down the permissions for the default "DNS Zone Contributor" role to only allow the service principal modification of TXT records type only. This is only useful in special cases where you cannot create all the exact role assignments you need for all your challenges, otherwise it is not recommended as the service principal will be able to access other critical TXT records on the same zone.

(See How to protect DNS zones and records for more details)

Example:

  • Azure Subscription is 12345678-9abc-def0-1234-567890abcdef
  • The resource group of your DNS Zone is exampledns_rg
  • The DNS Zone is example.com
#!/usr/bin/env sh
# Create a custom RBAC role that grants permissions to modify only TXT records
dnscustomrole='{ 
    "Name": "DNS TXT Contributor", 
    "Id": "",
     "IsCustom": true, 
    "Description": "Can manage DNS TXT records only.", 
    "Actions": [ 
        "Microsoft.Network/dnsZones/TXT/*", 
        "Microsoft.Network/dnsZones/read", 
        "Microsoft.Authorization/*/read", 
        "Microsoft.Insights/alertRules/*", 
        "Microsoft.ResourceHealth/availabilityStatuses/read", 
        "Microsoft.Resources/deployments/read", 
        "Microsoft.Resources/subscriptions/resourceGroups/read" 
    ],
    "NotActions": [ 
    ],
    "AssignableScopes": [ 
        "/subscriptions/12345678-9abc-def0-1234-567890abcdef" 
    ] 
}'
az role definition create --role-definition "$dnscustomrole"
# Create a new service principal and grant permissions to modify TXT recornds in the give DNS Zone
az ad sp create-for-rbac --name  "AcmeDnsValidator" --role "DNS TXT Contributor" --scopes "/subscriptions/12345678-9abc-def0-1234-567890abcdef/resourceGroups/exampledns_rg/providers/Microsoft.Network/dnszones/example.com" 

# or  grant an existing service principal permissions to modify TXT recornds in the give DNS Zone
#az role assignment create  --assignee 3b5033b5-7a66-43a5-b3b9-a36b9e7c25ed --scope "/subscriptions/12345678-9abc-def0-1234-567890abcdef/resourceGroups/exampledns_rg/providers/Microsoft.Network/dnszones/example.com" --role "DNS TXT Contributor"

You can now use acme.sh

export AZUREDNS_SUBSCRIPTIONID="12345678-9abc-def0-1234-567890abcdef"
export AZUREDNS_TENANTID="11111111-2222-3333-4444-555555555555"
export AZUREDNS_APPID="3b5033b5-7a66-43a5-b3b9-a36b9e7c25ed"          # appid of the service principal
export AZUREDNS_CLIENTSECRET="e.L8Q~4jGhWHheCKjdRzw3gyBBwOmrTyYF9NYbxs"   # password from creating the service principal

acme.sh --issue --dns dns_azure -d example.com -d www.example.com

Update service principal password

The service principal credentials may eventually expire.

Some acme.sh renewal errors that are signs of the credentials expiring:

  • no acccess token received. Check your Azure settings
  • access denied make sure your Azure settings are correct
az ad sp list --filter "displayname eq 'AcmeDnsValidator'" | grep '^    \"id\":'

(The grep above is assuming a json array of nested lists is returned with a tab size of two spaces and is finding the top-level id)

    "id": "daaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
az ad sp credential reset --id daaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa

Update ~/.acme.sh/account.conf with the new credentials.

(See az ad sp credential for details)

Clone this wiki locally