Skip to content

Commit 2e9d07b

Browse files
eerhardtgithub-actions
authored andcommitted
Allow customizing Azure ContainerApp resources with ProvisioningBuildOptions.
Fix #6496
1 parent 8f3b5b6 commit 2e9d07b

2 files changed

Lines changed: 98 additions & 3 deletions

File tree

src/Aspire.Hosting.Azure.AppContainers/AzureContainerAppsInfrastructure.cs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,18 @@
1111
using Azure.Provisioning.KeyVault;
1212
using Azure.Provisioning.Resources;
1313
using Microsoft.Extensions.Logging;
14+
using Microsoft.Extensions.Options;
1415

1516
namespace Aspire.Hosting.Azure;
1617

1718
/// <summary>
1819
/// Represents the infrastructure for Azure Container Apps within the Aspire Hosting environment.
1920
/// Implements the <see cref="IDistributedApplicationLifecycleHook"/> interface to provide lifecycle hooks for distributed applications.
2021
/// </summary>
21-
internal sealed class AzureContainerAppsInfrastructure(ILogger<AzureContainerAppsInfrastructure> logger, DistributedApplicationExecutionContext executionContext) : IDistributedApplicationLifecycleHook
22+
internal sealed class AzureContainerAppsInfrastructure(
23+
ILogger<AzureContainerAppsInfrastructure> logger,
24+
IOptions<AzureProvisioningOptions> provisioningOptions,
25+
DistributedApplicationExecutionContext executionContext) : IDistributedApplicationLifecycleHook
2226
{
2327
public async Task BeforeStartAsync(DistributedApplicationModel appModel, CancellationToken cancellationToken = default)
2428
{
@@ -48,7 +52,7 @@ public async Task BeforeStartAsync(DistributedApplicationModel appModel, Cancell
4852
continue;
4953
}
5054

51-
var containerApp = await containerAppEnvironmentContext.CreateContainerAppAsync(r, executionContext, cancellationToken).ConfigureAwait(false);
55+
var containerApp = await containerAppEnvironmentContext.CreateContainerAppAsync(r, provisioningOptions.Value, executionContext, cancellationToken).ConfigureAwait(false);
5256

5357
r.Annotations.Add(new DeploymentTargetAnnotation(containerApp));
5458
}
@@ -74,11 +78,12 @@ IManifestExpressionProvider clientId
7478

7579
private readonly Dictionary<IResource, ContainerAppContext> _containerApps = [];
7680

77-
public async Task<AzureBicepResource> CreateContainerAppAsync(IResource resource, DistributedApplicationExecutionContext executionContext, CancellationToken cancellationToken)
81+
public async Task<AzureBicepResource> CreateContainerAppAsync(IResource resource, AzureProvisioningOptions provisioningOptions, DistributedApplicationExecutionContext executionContext, CancellationToken cancellationToken)
7882
{
7983
var context = await ProcessResourceAsync(resource, executionContext, cancellationToken).ConfigureAwait(false);
8084

8185
var provisioningResource = new AzureProvisioningResource(resource.Name, context.BuildContainerApp);
86+
provisioningResource.ProvisioningBuildOptions = provisioningOptions.ProvisioningBuildOptions;
8287

8388
provisioningResource.Annotations.Add(new ManifestPublishingCallbackAnnotation(provisioningResource.WriteToManifest));
8489

tests/Aspire.Hosting.Azure.Tests/AzureContainerAppsTests.cs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
using System.Runtime.CompilerServices;
77
using Aspire.Hosting.ApplicationModel;
88
using Aspire.Hosting.Utils;
9+
using Azure.Provisioning;
910
using Azure.Provisioning.AppContainers;
11+
using Azure.Provisioning.Primitives;
1012
using Microsoft.Extensions.DependencyInjection;
1113
using Xunit;
1214
using Xunit.Abstractions;
@@ -1273,6 +1275,94 @@ param outputs_azure_container_apps_environment_id string
12731275
Assert.Equal(expectedBicep, bicep);
12741276
}
12751277

1278+
[Fact]
1279+
public async Task CanCustomizeWithProvisioningBuildOptions()
1280+
{
1281+
var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish);
1282+
1283+
builder.Services.Configure<AzureProvisioningOptions>(options => options.ProvisioningBuildOptions.InfrastructureResolvers.Insert(0, new MyResourceNamePropertyResolver()));
1284+
builder.AddAzureContainerAppsInfrastructure();
1285+
1286+
builder.AddContainer("api1", "myimage");
1287+
1288+
using var app = builder.Build();
1289+
1290+
await ExecuteBeforeStartHooksAsync(app, default);
1291+
1292+
var model = app.Services.GetRequiredService<DistributedApplicationModel>();
1293+
1294+
var container = Assert.Single(model.GetContainerResources());
1295+
1296+
container.TryGetLastAnnotation<DeploymentTargetAnnotation>(out var target);
1297+
1298+
var resource = target?.DeploymentTarget as AzureProvisioningResource;
1299+
1300+
Assert.NotNull(resource);
1301+
1302+
var (_, bicep) = await ManifestUtils.GetManifestWithBicep(resource);
1303+
1304+
var expectedBicep =
1305+
"""
1306+
@description('The location for the resource(s) to be deployed.')
1307+
param location string = resourceGroup().location
1308+
1309+
param outputs_azure_container_registry_managed_identity_id string
1310+
1311+
param outputs_managed_identity_client_id string
1312+
1313+
param outputs_azure_container_apps_environment_id string
1314+
1315+
resource api1 'Microsoft.App/containerApps@2024-03-01' = {
1316+
name: 'api1-my'
1317+
location: location
1318+
properties: {
1319+
configuration: {
1320+
activeRevisionsMode: 'Single'
1321+
}
1322+
environmentId: outputs_azure_container_apps_environment_id
1323+
template: {
1324+
containers: [
1325+
{
1326+
image: 'myimage:latest'
1327+
name: 'api1'
1328+
env: [
1329+
{
1330+
name: 'AZURE_CLIENT_ID'
1331+
value: outputs_managed_identity_client_id
1332+
}
1333+
]
1334+
}
1335+
]
1336+
scale: {
1337+
minReplicas: 1
1338+
}
1339+
}
1340+
}
1341+
identity: {
1342+
type: 'UserAssigned'
1343+
userAssignedIdentities: {
1344+
'${outputs_azure_container_registry_managed_identity_id}': { }
1345+
}
1346+
}
1347+
}
1348+
""";
1349+
output.WriteLine(bicep);
1350+
Assert.Equal(expectedBicep, bicep);
1351+
}
1352+
1353+
private sealed class MyResourceNamePropertyResolver : DynamicResourceNamePropertyResolver
1354+
{
1355+
public override void ResolveProperties(ProvisionableConstruct construct, ProvisioningBuildOptions options)
1356+
{
1357+
if (construct is ContainerApp app)
1358+
{
1359+
app.Name = app.Name.Value + "-my";
1360+
}
1361+
1362+
base.ResolveProperties(construct, options);
1363+
}
1364+
}
1365+
12761366
[Fact]
12771367
public async Task ExternalEndpointBecomesIngress()
12781368
{

0 commit comments

Comments
 (0)