Search code examples
azure-cliazure-rest-apiazure-dns

"az network dns zone export" only exports the first 100 recordsets


Setting this question up to self-answer because I lost too much time solving it, and I want to make sure the answer is put out there for future sufferers to find and avoid the same rabbit-hole I went down...


Issue

I'm trying to use the az network dns zone export command from the Az Cli in a PowerShell script to export the configuration from an Azure DNS Zone, but for some reason it's only exporting the first 100 records if I use az login to connect as a Service Principal with a custom IAM role assigned.

E.g.:

$ErrorActionPreference = "Stop";
Set-StrictMode -Version "Latest";

# see https://github.com/Azure/azure-cli/issues/26052
az config unset core.allow_broker

# log out any existing user
az logout

# log back in as the spn
az login --service-principal `
    --username "my-client-id" `
    --password "my-client-secret" `
    --tenant   "my-tenant-id"

# export the dns zone (only the first 100 records get exported :-(
az network dns zone export --resource-group "my-dns-rg" --name "mydnszone.com"

If I run the az network dns zone export command with my personal account logged in (which is an Owner on My Subscription) it works fine and exports all 575 records, so the problem appears to be permission-related, but it's confusing because the first 100 records are exported as the Service Principal so it has at least some permissions to read them.

Environment

My environment setup is as follows:

  • I have an Azure subscription My Subscription

  • In this subscription I have a public Azure DNS Zone mydnszone.com which has 575 zone records

  • I have a custom IAM role called My DNS Reader Role which has the following Microsoft.Network provider operations allowed:

    Operation Description
    Microsoft.Network/dnsZones/read Get the DNS zone, in JSON format. The zone properties include tags, etag, numberOfRecordSets, and maxNumberOfRecordSets. Note that this command does not retrieve the record sets contained within the zone.
    Microsoft.Network/dnsZones/recordsets/read Gets DNS record sets across types
  • I also have an Azure Service Principal my_dns_reader which has been assigned My DNS Reader Role IAM role scoped to My Subscription


Solution

  • tl;dr

    Permissions required to export all pages of records via the az network dns zone export command are:

    Operation Description
    Microsoft.Network/dnszones/read Get the DNS zone, in JSON format. The zone properties include tags, etag, numberOfRecordSets, and maxNumberOfRecordSets. Note that this command does not retrieve the record sets contained within the zone.
    Microsoft.Network/dnszones/recordsets/read Gets DNS record sets across types
    Microsoft.Network/dnszones/all/read Gets DNS record sets across types

    Summary

    • The Az Cli uses the Record Sets - List By Dns Zone Azure REST endpoint to export a dns zone with the az network dns zone export command.

    • This endpoint uses pagination (with a default page size of 100 recordsets) to return recordsets in a DNS zone. The Microsoft.Network/dnsZones/recordsets/read provider operation grants access to this endpoint.

    • The first page of results includes a nextLink which points to a different endpoint - Record Sets - List All By Dns Zone - for page 2 onwards, and this endpoint requires the Microsoft.Network/dnszones/all/read provider operation.

    • If this provider operation is missing from the current account the "Record Sets - List All By Dns Zone" endpoint returns paginated results but with zero rows of data in it.

    • Adding the Microsoft.Network/dnszones/all/read to my custom IAM role allowed the az network dns zone export command to export all recordsets in the zone.

    Confusingly, no error messages are shown if the Microsoft.Network/dnsZones/recordsets/read provider operation is present but the Microsoft.Network/dnszones/all/read provider operation is missing - instead the "Record Sets - List All By Dns Zone" endpoint returns pagination responses but with zero records included in the data.

    More details

    Running the az network dns zone export command with the --debug flag shows it first makes a call to the Record Sets - List By Dns Zone Azure REST endpoint - e.g.

    https://management.azure.com/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/my-dns-rg/providers/Microsoft.Network/dnsZones/mydnszone.com/recordsets?api-version=2023-07-01-preview
    

    If the current account has the Microsoft.Network/dnszones/recordsets/read provider operation this returns a response in the format:

    {
        "nextLink": "https://management.azure.com:443/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx/resourceGroups/my-dns-rg/providers/Microsoft.Network/dnszones/mydnszone.com/ALL?api-version=2023-07-01-preview&$skipToken=xxxxxxxxxxxxx",
        "value": [
          ... first 100 recordets from the dns zone ...
        ]
      }
    

    (If there are less than 100 records the nextLink field is missing from the response).

    Note that the nextLink ends in /ALL which is the Record Sets - List All By Dns Zone endpoint, so subsequent calls made by the Az Cli to read paginated data hit this endpoint instead of the original "Record Sets - List By Dns Zone".

    Unfortunately, this endpoint requires a different provider operation - namely Microsoft.Network/dnszones/all/read - if this is missing the response is in the following format:

     {
        "nextLink": "https://management.azure.com:443/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx/resourceGroups/my-dns-rg/providers/Microsoft.Network/dnszones/mydnszone.com/ALL?api-version=2023-07-01-preview&$skipToken=xxxxxxxxxxxxx",
        "value": []
      }
    

    Note that pagination still works. For a zone with 575 records the nextLink redirects to a chain of 5 additional pages after the initial request, which is the correct number for a zone with 575 records, but the requests ending in /ALL contain "value": [].

    Epilogue

    When trying to create a minimal custom IAM role for exporting dns zones, be sure to add all the provider operations in the "tl;dr" section at the top of this answer.