The concept of Availability Zones is well explained in the Microsoft documentation. These are different physical locations within a region that have separate power, cooling, and network infrastructure. When building a resource, it can often be decided in which Availability Zone the resource should be built. For higher availability, the resource can be set up twice in separate physical locations and a running service can be ensured even in the event of a cooling failure. The following questions often follow:

  • Is the Availability Zone 1 the most used zone?
  • Are the most free resources in Availability Zone 3 available?
  • Should a company always use the same Availability Zone in every subscription and in every region?

Each major Azure region consists of multiple physical zones. When creating a new subscription, the logical Availability Zones are mapped to the physical zones. Thus, the subscriptions (also of the same tenant) can have different mappings in the same availability zones of the same location. In this way, Microsoft ensures that the data centers are evenly utilized. However, this behavior also causes the problem that resources in different subscriptions are not guaranteed to be in the same AZ, even if they have the same number.

The mapping of a logical zone to a physical zone cannot be determined. However, the REST API checkZonePeers can be used to check how the mapping between 2 subscriptions is in relation to a location. Thus, a resource can be set up in the same or different physical zone (depending on the target scenario) in 2 different subscriptions. For easier use I created the following cmdlet and put it in Github:

[CmdletBinding()]
param (
  [parameter(Mandatory = $true)]
  [ValidateNotNullOrEmpty()]
  [String]
  $SubscriptionId1,

  [parameter(Mandatory = $true)]
  [ValidateNotNullOrEmpty()]
  [String]
  $SubscriptionId2,

  [parameter(Mandatory = $false)]
  [ValidateNotNullOrEmpty()]
  [String]
  $Location = "westeurope"
)

$body = @{
    "location" = "$Location"
    "subscriptionIds" = @(
      "subscriptions/$SubscriptionId2" 
    )
}

.\Register-AvailabilityZonePeeringFeature -SubscriptionId $SubscriptionId1

$auth=Get-AzAccessToken
$authHeader= $auth.token 
$url = "https://management.azure.com/subscriptions/$SubscriptionId1/providers/Microsoft.Resources/checkZonePeers/?api-version=2020-01-01"
$res = Invoke-RestMethod `
    -Method Post `
    -Headers @{"Authorization"="Bearer $authHeader"} `
    -ContentType "application/json; charset=utf-8" `
    -ResponseHeadersVariable respHeaders `
    -StatusCodeVariable statusCode `
    -Body (ConvertTo-Json $body -Depth 10) `
    -Uri $url `
    -SkipHttpErrorCheck
if($statusCode -eq 200){
  $azinfo = @()
  $res.availabilityZonePeers | % {
    $azinfo += [pscustomobject] [ordered] @{
      Subscription1 = $res.subscriptionId
      Subscription1AZ = $_.availabilityZone
      Location = $res.location
      Subscription2 = $_.peers.subscriptionId
      Subscription2AZ = $_.peers.availabilityZone
    }
  }
  return ($azinfo | Format-Table)
}
Write-Host -ForegroundColor Red "Error: $($res.error.message)"
return $res.error

The script also uses a second cmdlet (line 26) that activates the AvailabilityZonePeering feature (with Register-AzProviderFeature) in the source subscription, since the REST call fails without this feature. If the call was successful, the information is in the availabilityZonePeers part of the response object. The information are divided into individual rows (lines 42-50) for a better overview. The calls look like this:

.\Get-AZ.ps1 -Location "westeurope" -SubscriptionId1 36463593-ebaa-... -SubscriptionId2 27d910a3-0d26-...

Subscription1     Subscription1AZ Location   Subscription2     Subscription2AZ
-------------     --------------- --------   -------------     ---------------
36463593-ebaa-... 1               westeurope 27d910a3-0d26-... 1
36463593-ebaa-... 2               westeurope 27d910a3-0d26-... 3
36463593-ebaa-... 3               westeurope 27d910a3-0d26-... 2


.\Get-AZ.ps1 -Location "centraluseuap" -SubscriptionId1 36463593-ebaa-... -SubscriptionId2 27d910a3-0d26-...

Subscription1     Subscription1AZ Location      Subscription2     Subscription2AZ
-------------     --------------- --------      -------------     ---------------
36463593-ebaa-... 1               centraluseuap 27d910a3-0d26-... 2
36463593-ebaa-... 2               centraluseuap 27d910a3-0d26-... 1

So the subscriptions in westeurope have the same physical zone at AZ1 and different physical zones at AZ2 and AZ3. In the location centraluseuap are only two physical zones and AZ1 maps to different physical zones in both subscriptions.