diff --git a/src/Aspire.Hosting.Azure.AppContainers/AzureContainerAppEnvironmentResource.cs b/src/Aspire.Hosting.Azure.AppContainers/AzureContainerAppEnvironmentResource.cs index 24551068a3d..1f3737fd220 100644 --- a/src/Aspire.Hosting.Azure.AppContainers/AzureContainerAppEnvironmentResource.cs +++ b/src/Aspire.Hosting.Azure.AppContainers/AzureContainerAppEnvironmentResource.cs @@ -18,6 +18,12 @@ public class AzureContainerAppEnvironmentResource(string name, Action + /// Gets or sets a value indicating whether the Aspire dashboard should be included in the container app environment. + /// Default is true. + /// + internal bool EnableDashboard { get; set; } = true; /// /// Gets the unique identifier of the Container App Environment. diff --git a/src/Aspire.Hosting.Azure.AppContainers/AzureContainerAppExtensions.cs b/src/Aspire.Hosting.Azure.AppContainers/AzureContainerAppExtensions.cs index cf4be56745a..13b6e84164e 100644 --- a/src/Aspire.Hosting.Azure.AppContainers/AzureContainerAppExtensions.cs +++ b/src/Aspire.Hosting.Azure.AppContainers/AzureContainerAppExtensions.cs @@ -152,14 +152,17 @@ public static IResourceBuilder AddAzureCon infra.Add(containerAppEnvironment); - var dashboard = new ContainerAppEnvironmentDotnetComponentResource("aspireDashboard", "2024-10-02-preview") + if (appEnvResource.EnableDashboard) { - Name = "aspire-dashboard", - ComponentType = "AspireDashboard", - Parent = containerAppEnvironment - }; + var dashboard = new ContainerAppEnvironmentDotnetComponentResource("aspireDashboard", "2024-10-02-preview") + { + Name = "aspire-dashboard", + ComponentType = "AspireDashboard", + Parent = containerAppEnvironment + }; - infra.Add(dashboard); + infra.Add(dashboard); + } var managedStorages = new Dictionary(); @@ -348,4 +351,16 @@ public static IResourceBuilder WithAzdReso builder.Resource.UseAzdNamingConvention = true; return builder; } + + /// + /// Configures whether the Aspire dashboard should be included in the container app environment. + /// + /// The AzureContainerAppEnvironmentResource to configure. + /// Whether to include the Aspire dashboard. Default is true. + /// + public static IResourceBuilder WithDashboard(this IResourceBuilder builder, bool enable = true) + { + builder.Resource.EnableDashboard = enable; + return builder; + } } diff --git a/tests/Aspire.Hosting.Azure.Tests/AzureContainerAppsTests.cs b/tests/Aspire.Hosting.Azure.Tests/AzureContainerAppsTests.cs index 860fbda4bb0..eb9bb2fecfb 100644 --- a/tests/Aspire.Hosting.Azure.Tests/AzureContainerAppsTests.cs +++ b/tests/Aspire.Hosting.Azure.Tests/AzureContainerAppsTests.cs @@ -1342,6 +1342,50 @@ await Verify(manifest.ToString(), "json") .AppendContentAsFile(bicep, "bicep"); } + [Fact] + public async Task ContainerAppEnvironmentWithDashboardEnabled() + { + var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish); + + builder.AddAzureContainerAppEnvironment("env") + .WithDashboard(true); + + using var app = builder.Build(); + + await ExecuteBeforeStartHooksAsync(app, default); + + var model = app.Services.GetRequiredService(); + + var containerAppEnvResource = Assert.Single(model.Resources.OfType()); + + var (manifest, bicep) = await GetManifestWithBicep(containerAppEnvResource); + + await Verify(manifest.ToString(), "json") + .AppendContentAsFile(bicep, "bicep"); + } + + [Fact] + public async Task ContainerAppEnvironmentWithDashboardDisabled() + { + var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish); + + builder.AddAzureContainerAppEnvironment("env") + .WithDashboard(false); + + using var app = builder.Build(); + + await ExecuteBeforeStartHooksAsync(app, default); + + var model = app.Services.GetRequiredService(); + + var containerAppEnvResource = Assert.Single(model.Resources.OfType()); + + var (manifest, bicep) = await GetManifestWithBicep(containerAppEnvResource); + + await Verify(manifest.ToString(), "json") + .AppendContentAsFile(bicep, "bicep"); + } + private static Task<(JsonNode ManifestNode, string BicepText)> GetManifestWithBicep(IResource resource) => AzureManifestUtils.GetManifestWithBicep(resource, skipPreparer: true); diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureContainerAppsTests.ContainerAppEnvironmentWithDashboardDisabled.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureContainerAppsTests.ContainerAppEnvironmentWithDashboardDisabled.verified.bicep new file mode 100644 index 00000000000..3196ed813d8 --- /dev/null +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureContainerAppsTests.ContainerAppEnvironmentWithDashboardDisabled.verified.bicep @@ -0,0 +1,79 @@ +@description('The location for the resource(s) to be deployed.') +param location string = resourceGroup().location + +param userPrincipalId string + +param tags object = { } + +resource env_mi 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + name: take('env_mi-${uniqueString(resourceGroup().id)}', 128) + location: location + tags: tags +} + +resource env_acr 'Microsoft.ContainerRegistry/registries@2023-07-01' = { + name: take('envacr${uniqueString(resourceGroup().id)}', 50) + location: location + sku: { + name: 'Basic' + } + tags: tags +} + +resource env_acr_env_mi_AcrPull 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(env_acr.id, env_mi.id, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')) + properties: { + principalId: env_mi.properties.principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d') + principalType: 'ServicePrincipal' + } + scope: env_acr +} + +resource env_law 'Microsoft.OperationalInsights/workspaces@2023-09-01' = { + name: take('envlaw-${uniqueString(resourceGroup().id)}', 63) + location: location + properties: { + sku: { + name: 'PerGB2018' + } + } + tags: tags +} + +resource env 'Microsoft.App/managedEnvironments@2024-03-01' = { + name: take('env${uniqueString(resourceGroup().id)}', 24) + location: location + properties: { + appLogsConfiguration: { + destination: 'log-analytics' + logAnalyticsConfiguration: { + customerId: env_law.properties.customerId + sharedKey: env_law.listKeys().primarySharedKey + } + } + workloadProfiles: [ + { + name: 'consumption' + workloadProfileType: 'Consumption' + } + ] + } + tags: tags +} + +output AZURE_LOG_ANALYTICS_WORKSPACE_NAME string = env_law.name + +output AZURE_LOG_ANALYTICS_WORKSPACE_ID string = env_law.id + +output AZURE_CONTAINER_REGISTRY_NAME string = env_acr.name + +output AZURE_CONTAINER_REGISTRY_ENDPOINT string = env_acr.properties.loginServer + +output AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID string = env_mi.id + +output AZURE_CONTAINER_APPS_ENVIRONMENT_NAME string = env.name + +output AZURE_CONTAINER_APPS_ENVIRONMENT_ID string = env.id + +output AZURE_CONTAINER_APPS_ENVIRONMENT_DEFAULT_DOMAIN string = env.properties.defaultDomain \ No newline at end of file diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureContainerAppsTests.ContainerAppEnvironmentWithDashboardDisabled.verified.json b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureContainerAppsTests.ContainerAppEnvironmentWithDashboardDisabled.verified.json new file mode 100644 index 00000000000..d3afcb328bb --- /dev/null +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureContainerAppsTests.ContainerAppEnvironmentWithDashboardDisabled.verified.json @@ -0,0 +1,7 @@ +{ + "type": "azure.bicep.v0", + "path": "env.module.bicep", + "params": { + "userPrincipalId": "" + } +} \ No newline at end of file diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureContainerAppsTests.ContainerAppEnvironmentWithDashboardEnabled.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureContainerAppsTests.ContainerAppEnvironmentWithDashboardEnabled.verified.bicep new file mode 100644 index 00000000000..2645f7f4d53 --- /dev/null +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureContainerAppsTests.ContainerAppEnvironmentWithDashboardEnabled.verified.bicep @@ -0,0 +1,87 @@ +@description('The location for the resource(s) to be deployed.') +param location string = resourceGroup().location + +param userPrincipalId string + +param tags object = { } + +resource env_mi 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + name: take('env_mi-${uniqueString(resourceGroup().id)}', 128) + location: location + tags: tags +} + +resource env_acr 'Microsoft.ContainerRegistry/registries@2023-07-01' = { + name: take('envacr${uniqueString(resourceGroup().id)}', 50) + location: location + sku: { + name: 'Basic' + } + tags: tags +} + +resource env_acr_env_mi_AcrPull 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(env_acr.id, env_mi.id, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')) + properties: { + principalId: env_mi.properties.principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d') + principalType: 'ServicePrincipal' + } + scope: env_acr +} + +resource env_law 'Microsoft.OperationalInsights/workspaces@2023-09-01' = { + name: take('envlaw-${uniqueString(resourceGroup().id)}', 63) + location: location + properties: { + sku: { + name: 'PerGB2018' + } + } + tags: tags +} + +resource env 'Microsoft.App/managedEnvironments@2024-03-01' = { + name: take('env${uniqueString(resourceGroup().id)}', 24) + location: location + properties: { + appLogsConfiguration: { + destination: 'log-analytics' + logAnalyticsConfiguration: { + customerId: env_law.properties.customerId + sharedKey: env_law.listKeys().primarySharedKey + } + } + workloadProfiles: [ + { + name: 'consumption' + workloadProfileType: 'Consumption' + } + ] + } + tags: tags +} + +resource aspireDashboard 'Microsoft.App/managedEnvironments/dotNetComponents@2024-10-02-preview' = { + name: 'aspire-dashboard' + properties: { + componentType: 'AspireDashboard' + } + parent: env +} + +output AZURE_LOG_ANALYTICS_WORKSPACE_NAME string = env_law.name + +output AZURE_LOG_ANALYTICS_WORKSPACE_ID string = env_law.id + +output AZURE_CONTAINER_REGISTRY_NAME string = env_acr.name + +output AZURE_CONTAINER_REGISTRY_ENDPOINT string = env_acr.properties.loginServer + +output AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID string = env_mi.id + +output AZURE_CONTAINER_APPS_ENVIRONMENT_NAME string = env.name + +output AZURE_CONTAINER_APPS_ENVIRONMENT_ID string = env.id + +output AZURE_CONTAINER_APPS_ENVIRONMENT_DEFAULT_DOMAIN string = env.properties.defaultDomain \ No newline at end of file diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureContainerAppsTests.ContainerAppEnvironmentWithDashboardEnabled.verified.json b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureContainerAppsTests.ContainerAppEnvironmentWithDashboardEnabled.verified.json new file mode 100644 index 00000000000..d3afcb328bb --- /dev/null +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureContainerAppsTests.ContainerAppEnvironmentWithDashboardEnabled.verified.json @@ -0,0 +1,7 @@ +{ + "type": "azure.bicep.v0", + "path": "env.module.bicep", + "params": { + "userPrincipalId": "" + } +} \ No newline at end of file