Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -79,5 +79,5 @@ public override ProvisionableResource AddAsExistingResource(AzureResourceInfrast

IEnumerable<string> IAzurePrivateEndpointTarget.GetPrivateLinkGroupIds() => ["configurationStores"];

string IAzurePrivateEndpointTarget.GetPrivateDnsZoneName() => "privatelink.azconfig.io";
IEnumerable<string> IAzurePrivateEndpointTarget.GetPrivateDnsZoneNames() => ["privatelink.azconfig.io"];
}
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ public override ProvisionableResource AddAsExistingResource(AzureResourceInfrast

IEnumerable<string> IAzurePrivateEndpointTarget.GetPrivateLinkGroupIds() => ["account"];

string IAzurePrivateEndpointTarget.GetPrivateDnsZoneName() => "privatelink.openai.azure.com";
IEnumerable<string> IAzurePrivateEndpointTarget.GetPrivateDnsZoneNames() => ["privatelink.openai.azure.com"];

IEnumerable<KeyValuePair<string, ReferenceExpression>> IResourceWithConnectionString.GetConnectionProperties()
{
Expand Down
2 changes: 1 addition & 1 deletion src/Aspire.Hosting.Azure.CosmosDB/AzureCosmosDBResource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -266,5 +266,5 @@ IEnumerable<KeyValuePair<string, ReferenceExpression>> IResourceWithConnectionSt

IEnumerable<string> IAzurePrivateEndpointTarget.GetPrivateLinkGroupIds() => ["Sql"];

string IAzurePrivateEndpointTarget.GetPrivateDnsZoneName() => "privatelink.documents.azure.com";
IEnumerable<string> IAzurePrivateEndpointTarget.GetPrivateDnsZoneNames() => ["privatelink.documents.azure.com"];
}
Original file line number Diff line number Diff line change
Expand Up @@ -216,5 +216,5 @@ IEnumerable<KeyValuePair<string, ReferenceExpression>> IResourceWithConnectionSt

IEnumerable<string> IAzurePrivateEndpointTarget.GetPrivateLinkGroupIds() => ["namespace"];

string IAzurePrivateEndpointTarget.GetPrivateDnsZoneName() => "privatelink.servicebus.windows.net";
IEnumerable<string> IAzurePrivateEndpointTarget.GetPrivateDnsZoneNames() => ["privatelink.servicebus.windows.net"];
}
2 changes: 1 addition & 1 deletion src/Aspire.Hosting.Azure.KeyVault/AzureKeyVaultResource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -149,5 +149,5 @@ IEnumerable<KeyValuePair<string, ReferenceExpression>> IResourceWithConnectionSt

IEnumerable<string> IAzurePrivateEndpointTarget.GetPrivateLinkGroupIds() => ["vault"];

string IAzurePrivateEndpointTarget.GetPrivateDnsZoneName() => "privatelink.vaultcore.azure.net";
IEnumerable<string> IAzurePrivateEndpointTarget.GetPrivateDnsZoneNames() => ["privatelink.vaultcore.azure.net"];
}
45 changes: 27 additions & 18 deletions src/Aspire.Hosting.Azure.Network/AzurePrivateEndpointExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,13 @@ public static IResourceBuilder<AzurePrivateEndpointResource> AddPrivateEndpoint(
return builder.CreateResourceBuilder(resource);
}

// Get or create the shared Private DNS Zone for this zone name
var zoneName = target.Resource.GetPrivateDnsZoneName();
var dnsZone = GetOrCreatePrivateDnsZone(builder, zoneName, vnet);
resource.DnsZone = dnsZone;
// Get or create the shared Private DNS Zones for this resource type
var zoneNames = target.Resource.GetPrivateDnsZoneNames();
foreach (var zoneName in zoneNames)
{
var dnsZone = GetOrCreatePrivateDnsZone(builder, zoneName, vnet);
resource.DnsZones.Add(dnsZone);
}

// Add annotation to the target's root parent (e.g., storage account) to signal
// that it should deny public network access and to associate the private endpoint
Expand All @@ -95,12 +98,16 @@ void ConfigurePrivateEndpoint(AzureResourceInfrastructure infra)
{
var azureResource = (AzurePrivateEndpointResource)infra.AspireResource;

// Get the shared DNS Zone as an existing resource
var dnsZone = azureResource.DnsZone!;
var dnsZoneIdentifier = dnsZone.GetBicepIdentifier();
var privateDnsZone = PrivateDnsZone.FromExisting(dnsZoneIdentifier);
privateDnsZone.Name = dnsZone.NameOutputReference.AsProvisioningParameter(infra);
infra.Add(privateDnsZone);
// Get the shared DNS Zones as existing resources
var privateDnsZones = new List<(string Identifier, PrivateDnsZone Zone)>();
foreach (var dnsZone in azureResource.DnsZones)
{
var dnsZoneIdentifier = dnsZone.GetBicepIdentifier();
var privateDnsZone = PrivateDnsZone.FromExisting(dnsZoneIdentifier);
privateDnsZone.Name = dnsZone.NameOutputReference.AsProvisioningParameter(infra);
infra.Add(privateDnsZone);
privateDnsZones.Add((dnsZoneIdentifier, privateDnsZone));
}

// Create the Private Endpoint
var endpoint = AzureProvisioningResource.CreateExistingOrNewProvisionableResource(infra,
Expand Down Expand Up @@ -138,15 +145,17 @@ void ConfigurePrivateEndpoint(AzureResourceInfrastructure infra)
{
Name = "default",
Parent = endpoint,
PrivateDnsZoneConfigs =
{
new PrivateDnsZoneConfig
{
Name = dnsZoneIdentifier,
PrivateDnsZoneId = privateDnsZone.Id
}
}
};

foreach (var (identifier, zone) in privateDnsZones)
{
dnsZoneGroup.PrivateDnsZoneConfigs.Add(new PrivateDnsZoneConfig
{
Name = identifier,
PrivateDnsZoneId = zone.Id
});
}

infra.Add(dnsZoneGroup);

// Output the Private Endpoint ID for references
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ public class AzurePrivateEndpointResource(
public IAzurePrivateEndpointTarget Target { get; } = target;

/// <summary>
/// Gets or sets the Private DNS Zone for this endpoint.
/// Gets the Private DNS Zones for this endpoint.
/// </summary>
internal AzurePrivateDnsZoneResource? DnsZone { get; set; }
internal List<AzurePrivateDnsZoneResource> DnsZones { get; } = [];

/// <inheritdoc/>
public override ProvisionableResource AddAsExistingResource(AzureResourceInfrastructure infra)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -316,5 +316,5 @@ IEnumerable<KeyValuePair<string, ReferenceExpression>> IResourceWithConnectionSt

IEnumerable<string> IAzurePrivateEndpointTarget.GetPrivateLinkGroupIds() => ["postgresqlServer"];

string IAzurePrivateEndpointTarget.GetPrivateDnsZoneName() => "privatelink.postgres.database.azure.com";
IEnumerable<string> IAzurePrivateEndpointTarget.GetPrivateDnsZoneNames() => ["privatelink.postgres.database.azure.com"];
}
Original file line number Diff line number Diff line change
Expand Up @@ -253,5 +253,5 @@ IEnumerable<KeyValuePair<string, ReferenceExpression>> IResourceWithConnectionSt

IEnumerable<string> IAzurePrivateEndpointTarget.GetPrivateLinkGroupIds() => ["redisEnterprise"];

string IAzurePrivateEndpointTarget.GetPrivateDnsZoneName() => "privatelink.redis.azure.net";
IEnumerable<string> IAzurePrivateEndpointTarget.GetPrivateDnsZoneNames() => ["privatelink.redis.azure.net"];
}
2 changes: 1 addition & 1 deletion src/Aspire.Hosting.Azure.Search/AzureSearchResource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,5 +93,5 @@ IEnumerable<KeyValuePair<string, ReferenceExpression>> IResourceWithConnectionSt

IEnumerable<string> IAzurePrivateEndpointTarget.GetPrivateLinkGroupIds() => ["searchService"];

string IAzurePrivateEndpointTarget.GetPrivateDnsZoneName() => "privatelink.search.windows.net";
IEnumerable<string> IAzurePrivateEndpointTarget.GetPrivateDnsZoneNames() => ["privatelink.search.windows.net"];
}
Original file line number Diff line number Diff line change
Expand Up @@ -196,5 +196,5 @@ IEnumerable<KeyValuePair<string, ReferenceExpression>> IResourceWithConnectionSt

IEnumerable<string> IAzurePrivateEndpointTarget.GetPrivateLinkGroupIds() => ["namespace"];

string IAzurePrivateEndpointTarget.GetPrivateDnsZoneName() => "privatelink.servicebus.windows.net";
IEnumerable<string> IAzurePrivateEndpointTarget.GetPrivateDnsZoneNames() => ["privatelink.servicebus.windows.net"];
}
2 changes: 1 addition & 1 deletion src/Aspire.Hosting.Azure.SignalR/AzureSignalRResource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,5 +96,5 @@ IEnumerable<KeyValuePair<string, ReferenceExpression>> IResourceWithConnectionSt

IEnumerable<string> IAzurePrivateEndpointTarget.GetPrivateLinkGroupIds() => ["signalr"];

string IAzurePrivateEndpointTarget.GetPrivateDnsZoneName() => "privatelink.service.signalr.net";
IEnumerable<string> IAzurePrivateEndpointTarget.GetPrivateDnsZoneNames() => ["privatelink.service.signalr.net"];
}
4 changes: 2 additions & 2 deletions src/Aspire.Hosting.Azure.Sql/AzureSqlServerResource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@ IEnumerable<KeyValuePair<string, ReferenceExpression>> IResourceWithConnectionSt

IEnumerable<string> IAzurePrivateEndpointTarget.GetPrivateLinkGroupIds() => ["sqlServer"];

string IAzurePrivateEndpointTarget.GetPrivateDnsZoneName() => "privatelink.database.windows.net";
IEnumerable<string> IAzurePrivateEndpointTarget.GetPrivateDnsZoneNames() => ["privatelink.database.windows.net"];

void IAzurePrivateEndpointTargetNotification.OnPrivateEndpointCreated(IResourceBuilder<AzurePrivateEndpointResource> privateEndpoint)
{
Expand Down Expand Up @@ -515,7 +515,7 @@ private sealed class StorageFiles(AzureStorageResource storage) : Resource("file

public IResource Parent => storage;

public string GetPrivateDnsZoneName() => "privatelink.file.core.windows.net";
public IEnumerable<string> GetPrivateDnsZoneNames() => ["privatelink.file.core.windows.net"];

public IEnumerable<string> GetPrivateLinkGroupIds()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ void IResourceWithAzureFunctionsConfig.ApplyAzureFunctionsConfiguration(IDiction

IEnumerable<string> IAzurePrivateEndpointTarget.GetPrivateLinkGroupIds() => ["blob"];

string IAzurePrivateEndpointTarget.GetPrivateDnsZoneName() => "privatelink.blob.core.windows.net";
IEnumerable<string> IAzurePrivateEndpointTarget.GetPrivateDnsZoneNames() => ["privatelink.blob.core.windows.net"];

IEnumerable<KeyValuePair<string, ReferenceExpression>> IResourceWithConnectionString.GetConnectionProperties()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,5 +84,5 @@ IEnumerable<KeyValuePair<string, ReferenceExpression>> IResourceWithConnectionSt

IEnumerable<string> IAzurePrivateEndpointTarget.GetPrivateLinkGroupIds() => ["dfs"];

string IAzurePrivateEndpointTarget.GetPrivateDnsZoneName() => "privatelink.dfs.core.windows.net";
IEnumerable<string> IAzurePrivateEndpointTarget.GetPrivateDnsZoneNames() => ["privatelink.dfs.core.windows.net"];
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ void IResourceWithAzureFunctionsConfig.ApplyAzureFunctionsConfiguration(IDiction

IEnumerable<string> IAzurePrivateEndpointTarget.GetPrivateLinkGroupIds() => ["queue"];

string IAzurePrivateEndpointTarget.GetPrivateDnsZoneName() => "privatelink.queue.core.windows.net";
IEnumerable<string> IAzurePrivateEndpointTarget.GetPrivateDnsZoneNames() => ["privatelink.queue.core.windows.net"];

IEnumerable<KeyValuePair<string, ReferenceExpression>> IResourceWithConnectionString.GetConnectionProperties()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,5 +80,5 @@ IEnumerable<KeyValuePair<string, ReferenceExpression>> IResourceWithConnectionSt

IEnumerable<string> IAzurePrivateEndpointTarget.GetPrivateLinkGroupIds() => ["table"];

string IAzurePrivateEndpointTarget.GetPrivateDnsZoneName() => "privatelink.table.core.windows.net";
IEnumerable<string> IAzurePrivateEndpointTarget.GetPrivateDnsZoneNames() => ["privatelink.table.core.windows.net"];
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,5 +83,5 @@ IEnumerable<KeyValuePair<string, ReferenceExpression>> IResourceWithConnectionSt

IEnumerable<string> IAzurePrivateEndpointTarget.GetPrivateLinkGroupIds() => ["webpubsub"];

string IAzurePrivateEndpointTarget.GetPrivateDnsZoneName() => "privatelink.webpubsub.azure.com";
IEnumerable<string> IAzurePrivateEndpointTarget.GetPrivateDnsZoneNames() => ["privatelink.webpubsub.azure.com"];
}
18 changes: 18 additions & 0 deletions src/Aspire.Hosting.Azure/CompatibilitySuppressions.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- https://learn.microsoft.com/dotnet/fundamentals/package-validation/diagnostic-ids -->
<Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Aspire.Hosting.Azure.IAzurePrivateEndpointTarget.GetPrivateDnsZoneName</Target>
<Left>lib/net8.0/Aspire.Hosting.Azure.dll</Left>
<Right>lib/net8.0/Aspire.Hosting.Azure.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0006</DiagnosticId>
<Target>M:Aspire.Hosting.Azure.IAzurePrivateEndpointTarget.GetPrivateDnsZoneNames</Target>
<Left>lib/net8.0/Aspire.Hosting.Azure.dll</Left>
<Right>lib/net8.0/Aspire.Hosting.Azure.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
</Suppressions>
6 changes: 3 additions & 3 deletions src/Aspire.Hosting.Azure/IAzurePrivateEndpointTarget.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ public interface IAzurePrivateEndpointTarget : IResource
IEnumerable<string> GetPrivateLinkGroupIds();

/// <summary>
/// Gets the private DNS zone name for this resource type (e.g., "privatelink.blob.core.windows.net" for blob storage).
/// Gets the private DNS zone names for this resource type (e.g., "privatelink.blob.core.windows.net" for blob storage).
/// </summary>
/// <returns>The private DNS zone name for the private endpoint.</returns>
string GetPrivateDnsZoneName();
/// <returns>A collection of private DNS zone names for the private endpoint.</returns>
IEnumerable<string> GetPrivateDnsZoneNames();
Comment on lines 24 to +30
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The checked-in public API baseline sources under src/*/api/*.cs don't appear to be updated for this interface change. For example, src/Aspire.Hosting.Azure/api/Aspire.Hosting.Azure.cs still declares IAzurePrivateEndpointTarget.GetPrivateDnsZoneName(), which will cause the public API validation to fail (and may leave other package API baselines out of sync as well). Please regenerate/update the api/*.cs baselines for the affected packages to reflect GetPrivateDnsZoneNames() and the removal of GetPrivateDnsZoneName().

Copilot uses AI. Check for mistakes.
}
7 changes: 6 additions & 1 deletion src/Aspire.Hosting.Foundry/FoundryResource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,12 @@ IEnumerable<KeyValuePair<string, ReferenceExpression>> IResourceWithConnectionSt

IEnumerable<string> IAzurePrivateEndpointTarget.GetPrivateLinkGroupIds() => ["account"];

string IAzurePrivateEndpointTarget.GetPrivateDnsZoneName() => "privatelink.cognitiveservices.azure.com";
IEnumerable<string> IAzurePrivateEndpointTarget.GetPrivateDnsZoneNames() =>
[
"privatelink.services.ai.azure.com",
"privatelink.openai.azure.com",
"privatelink.cognitiveservices.azure.com"
];
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ public void AzureBlobStorageResource_ImplementsIAzurePrivateEndpointTarget()

var target = (IAzurePrivateEndpointTarget)blobs.Resource;
Assert.Equal(["blob"], target.GetPrivateLinkGroupIds());
Assert.Equal("privatelink.blob.core.windows.net", target.GetPrivateDnsZoneName());
Assert.Equal(["privatelink.blob.core.windows.net"], target.GetPrivateDnsZoneNames());
}

[Fact]
Expand All @@ -141,7 +141,7 @@ public void AzureQueueStorageResource_ImplementsIAzurePrivateEndpointTarget()

var target = (IAzurePrivateEndpointTarget)queues.Resource;
Assert.Equal(["queue"], target.GetPrivateLinkGroupIds());
Assert.Equal("privatelink.queue.core.windows.net", target.GetPrivateDnsZoneName());
Assert.Equal(["privatelink.queue.core.windows.net"], target.GetPrivateDnsZoneNames());
}

[Fact]
Expand Down Expand Up @@ -208,4 +208,35 @@ public void AddPrivateEndpoint_CreatesSeparateDnsZones_ForDifferentZoneNames()
// Each DNS Zone should have one VNet Link
Assert.All(dnsZones, z => Assert.Single(z.VNetLinks));
}

[Fact]
public async Task AddPrivateEndpoint_CreatesMultipleDnsZones_ForMultiZoneTarget()
{
using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish);

var vnet = builder.AddAzureVirtualNetwork("myvnet");
var subnet = vnet.AddSubnet("pesubnet", "10.0.1.0/24");
var foundry = builder.AddFoundry("foundry");

var pe = subnet.AddPrivateEndpoint(foundry);

// Foundry returns 3 DNS zone names
var dnsZones = builder.Resources.OfType<AzurePrivateDnsZoneResource>().ToList();
Assert.Equal(3, dnsZones.Count);
Assert.Contains(dnsZones, z => z.ZoneName == "privatelink.services.ai.azure.com");
Assert.Contains(dnsZones, z => z.ZoneName == "privatelink.openai.azure.com");
Assert.Contains(dnsZones, z => z.ZoneName == "privatelink.cognitiveservices.azure.com");

// Each DNS Zone should have one VNet Link
Assert.All(dnsZones, z => Assert.Single(z.VNetLinks));

// The PE resource should reference all 3 DNS zones
Assert.Equal(3, pe.Resource.DnsZones.Count);

// Verify the bicep for the PE resource
var manifest = await AzureManifestUtils.GetManifestWithBicep(pe.Resource);

await Verify(manifest.BicepText, extension: "bicep");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
@description('The location for the resource(s) to be deployed.')
param location string = resourceGroup().location

param privatelink_services_ai_azure_com_outputs_name string

param privatelink_openai_azure_com_outputs_name string

param privatelink_cognitiveservices_azure_com_outputs_name string

param myvnet_outputs_pesubnet_id string

param foundry_outputs_id string

resource privatelink_services_ai_azure_com 'Microsoft.Network/privateDnsZones@2024-06-01' existing = {
name: privatelink_services_ai_azure_com_outputs_name
}

resource privatelink_openai_azure_com 'Microsoft.Network/privateDnsZones@2024-06-01' existing = {
name: privatelink_openai_azure_com_outputs_name
}

resource privatelink_cognitiveservices_azure_com 'Microsoft.Network/privateDnsZones@2024-06-01' existing = {
name: privatelink_cognitiveservices_azure_com_outputs_name
}

resource pesubnet_foundry_pe 'Microsoft.Network/privateEndpoints@2025-05-01' = {
name: take('pesubnet_foundry_pe-${uniqueString(resourceGroup().id)}', 64)
location: location
properties: {
privateLinkServiceConnections: [
{
properties: {
privateLinkServiceId: foundry_outputs_id
groupIds: [
'account'
]
}
name: 'pesubnet-foundry-pe-connection'
}
]
subnet: {
id: myvnet_outputs_pesubnet_id
}
}
tags: {
'aspire-resource-name': 'pesubnet-foundry-pe'
}
}

resource pesubnet_foundry_pe_dnsgroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2025-05-01' = {
name: 'default'
properties: {
privateDnsZoneConfigs: [
{
name: 'privatelink_services_ai_azure_com'
properties: {
privateDnsZoneId: privatelink_services_ai_azure_com.id
}
}
{
name: 'privatelink_openai_azure_com'
properties: {
privateDnsZoneId: privatelink_openai_azure_com.id
}
}
{
name: 'privatelink_cognitiveservices_azure_com'
properties: {
privateDnsZoneId: privatelink_cognitiveservices_azure_com.id
}
}
]
}
parent: pesubnet_foundry_pe
}

output id string = pesubnet_foundry_pe.id

output name string = pesubnet_foundry_pe.name
Loading