File: FrontDoorPrivateLinkRegions.cs
Web Access
Project: src\src\Aspire.Hosting.Azure.FrontDoor\Aspire.Hosting.Azure.FrontDoor.csproj (Aspire.Hosting.Azure.FrontDoor)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Collections.Frozen;
using Azure.Provisioning.Expressions;
 
namespace Aspire.Hosting.Azure;
 
/// <summary>
/// Chooses the region of the Front Door managed private endpoint for an origin in a given region.
/// </summary>
/// <remarks>
/// <para>
/// Front Door Private Link is only available in some regions, and the private endpoint can be in a different region
/// than the origin, at the cost of an extra network hop:
/// https://learn.microsoft.com/azure/frontdoor/private-link#region-availability
/// </para>
/// <para>
/// This table only moves an origin to a supported region in the same Azure geography
/// (https://azure.microsoft.com/explore/global-infrastructure/geographies/), so a remap never changes the data
/// residency boundary of the connection. A region in a geography that has no supported region isn't listed: the
/// deployment fails in Front Door, and the origin's <c>SharedPrivateLinkResource.PrivateLinkLocation</c> can be set in
/// <c>ConfigureInfrastructure</c> to pick a region in another geography on purpose.
/// </para>
/// <para>
/// MAINTENANCE: update this table when Front Door adds or removes Private Link regions or when Azure adds regions. Moving
/// an entry changes the private endpoint of existing deployments, which creates a new connection request that must be
/// approved. Last reviewed against the documentation above on 2026-10-22.
/// </para>
/// </remarks>
internal static class FrontDoorPrivateLinkRegions
{
    public const string BicepVariableName = "privateLinkRegions";
 
    // Keys are the normalized region names (lowercase, no spaces) that the environment outputs. Regions that Front Door
    // Private Link supports aren't listed, because they are used as they are.
    public static FrozenDictionary<string, string> Remapped { get; } = new Dictionary<string, string>(StringComparer.Ordinal)
    {
        // United States
        ["westus"] = "westus2",
        ["northcentralus"] = "centralus",
        ["westcentralus"] = "centralus",
        // Early Updates Access Program (EUAP) canary regions and their STG counterparts stay in the US geography.
        ["eastus2euap"] = "eastus2",
        ["centraluseuap"] = "centralus",
        ["eastusstg"] = "eastus",
        ["southcentralusstg"] = "southcentralus",
        // Canada
        ["canadaeast"] = "canadacentral",
        // Brazil
        ["brazilsoutheast"] = "brazilsouth",
        // United Kingdom
        ["ukwest"] = "uksouth",
        // France
        ["francesouth"] = "francecentral",
        // Germany
        ["germanynorth"] = "germanywestcentral",
        // Norway
        ["norwaywest"] = "norwayeast",
        // Sweden
        ["swedensouth"] = "swedencentral",
        // South Africa
        ["southafricawest"] = "southafricanorth",
        // United Arab Emirates
        ["uaecentral"] = "uaenorth",
        // Australia
        ["australiasoutheast"] = "australiaeast",
        ["australiacentral"] = "australiaeast",
        ["australiacentral2"] = "australiaeast",
        // India
        ["southindia"] = "centralindia",
        ["westindia"] = "centralindia",
        ["jioindiacentral"] = "centralindia",
        ["jioindiawest"] = "centralindia",
        // Japan
        ["japanwest"] = "japaneast",
        // Korea
        ["koreasouth"] = "koreacentral",
        // China
        ["chinaeast"] = "chinaeast3",
        ["chinaeast2"] = "chinaeast3",
        ["chinanorth"] = "chinanorth3",
        ["chinanorth2"] = "chinanorth3",
    }.ToFrozenDictionary(StringComparer.Ordinal);
 
    // The Bicep object that maps a region to the region of its private endpoint:
    //   var privateLinkRegions = {
    //     westus: 'westus2'
    //     ...
    //   }
    public static ObjectExpression CreateBicepObject()
    {
        return new ObjectExpression(
            [.. Remapped.OrderBy(pair => pair.Key, StringComparer.Ordinal).Select(pair => new PropertyExpression(pair.Key, new StringLiteralExpression(pair.Value)))]);
    }
 
    // Looks up the region in the object above and keeps the region when it has no entry: privateLinkRegions[?region] ?? region.
    public static BinaryExpression CreateLookup(string variableName, string regionIdentifier)
    {
        return new BinaryExpression(
            new SafeIndexExpression(new IdentifierExpression(variableName), new IdentifierExpression(regionIdentifier)),
            BinaryBicepOperator.Coalesce,
            new IdentifierExpression(regionIdentifier));
    }
}