From 3aa796dc9552356db4bd4be4ad7e4feeaa5dfd63 Mon Sep 17 00:00:00 2001 From: Shilpi Rachna Date: Thu, 25 Sep 2025 22:45:55 -0700 Subject: [PATCH 01/17] Added support for Aspire dashboard in App Service --- .../AzureAppServiceEnvironmentExtensions.cs | 55 ++++++++++++- .../AzureAppServiceEnvironmentResource.cs | 5 ++ .../AzureAppServiceEnvironmentUtility.cs | 77 +++++++++++++++++++ .../AzureAppServiceWebsiteContext.cs | 15 ++++ 4 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentUtility.cs diff --git a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentExtensions.cs b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentExtensions.cs index a03ea9c56ce..9c8ce7576a4 100644 --- a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentExtensions.cs +++ b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentExtensions.cs @@ -7,6 +7,7 @@ using Aspire.Hosting.Lifecycle; using Azure.Provisioning; using Azure.Provisioning.AppService; +using Azure.Provisioning.Authorization; using Azure.Provisioning.ContainerRegistry; using Azure.Provisioning.Expressions; using Azure.Provisioning.Roles; @@ -64,6 +65,12 @@ public static IResourceBuilder AddAzureAppSe infra.Add(identity); + var contributorIdentity = new UserAssignedIdentity(Infrastructure.NormalizeBicepIdentifier($"{prefix}-contributor-mi")) + { + }; + + infra.Add(contributorIdentity); + ContainerRegistryService? containerRegistry = null; #pragma warning disable ASPIRECOMPUTE001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. if (resource.TryGetLastAnnotation(out var registryReferenceAnnotation) && registryReferenceAnnotation.Registry is AzureProvisioningResource registry) @@ -96,7 +103,9 @@ public static IResourceBuilder AddAzureAppSe Tier = "Premium" }, Kind = "Linux", - IsReserved = true + IsReserved = true, + // Enable per-site scaling so each app service can scale independently + IsPerSiteScaling = true }; infra.Add(plan); @@ -111,6 +120,40 @@ public static IResourceBuilder AddAzureAppSe Value = plan.Id }); + // Add Website Contributor role assignment + var rgRaId = BicepFunction.GetSubscriptionResourceId( + "Microsoft.Authorization/roleDefinitions", + "de139f84-1756-47ae-9be6-808fbbe84772"); + var rgRaName = BicepFunction.CreateGuid(BicepFunction.GetResourceGroup().Id, contributorIdentity.Id, rgRaId); + var rgRa = new RoleAssignment(Infrastructure.NormalizeBicepIdentifier($"{prefix}_ra")) + { + Name = rgRaName, + PrincipalType = RoleManagementPrincipalType.ServicePrincipal, + PrincipalId = contributorIdentity.PrincipalId, + RoleDefinitionId = rgRaId, + }; + + infra.Add(rgRa); + + // Add Reader role assignment + var rgRaId2 = BicepFunction.GetSubscriptionResourceId( + "Microsoft.Authorization/roleDefinitions", + "acdd72a7-3385-48ef-bd42-f606fba81ae7"); + var rgRaName2 = BicepFunction.CreateGuid(BicepFunction.GetResourceGroup().Id, contributorIdentity.Id, rgRaId2); + + var rgRa2 = new RoleAssignment(Infrastructure.NormalizeBicepIdentifier($"{prefix}_ra2")) + { + Name = rgRaName2, + PrincipalType = RoleManagementPrincipalType.ServicePrincipal, + PrincipalId = contributorIdentity.PrincipalId, + RoleDefinitionId = rgRaId2 + }; + + infra.Add(rgRa2); + + // Add aspire dashboard website + var website = AzureAppServiceEnvironmentUtility.AddDashboard(infra, identity, contributorIdentity, plan.Id); + infra.Add(new ProvisioningOutput("AZURE_CONTAINER_REGISTRY_NAME", typeof(string)) { Value = containerRegistry.Name @@ -131,6 +174,16 @@ public static IResourceBuilder AddAzureAppSe { Value = identity.ClientId }); + + infra.Add(new ProvisioningOutput("AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_NAME", typeof(string)) + { + Value = identity.Name + }); + + infra.Add(new ProvisioningOutput("DASHBOARD_URI", typeof(string)) + { + Value = BicepFunction.Interpolate($"https://{AzureAppServiceEnvironmentUtility.DashboardHostName}.azurewebsites.net") + }); }); if (!builder.ExecutionContext.IsPublishMode) diff --git a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentResource.cs b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentResource.cs index 2b88713c98d..4347f99461e 100644 --- a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentResource.cs +++ b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentResource.cs @@ -32,6 +32,11 @@ public class AzureAppServiceEnvironmentResource(string name, Action public BicepOutputReference NameOutputReference => new("name", this); + /// + /// Gets the URI of the App Service Environment dashboard. + /// + public BicepOutputReference DashboardUriReference => new("DASHBOARD_URI", this); + ReferenceExpression IAzureContainerRegistry.ManagedIdentityId => ReferenceExpression.Create($"{ContainerRegistryManagedIdentityId}"); diff --git a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentUtility.cs b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentUtility.cs new file mode 100644 index 00000000000..62602e3e28d --- /dev/null +++ b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentUtility.cs @@ -0,0 +1,77 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Azure.Core; +using Azure.Provisioning; +using Azure.Provisioning.AppService; +using Azure.Provisioning.Expressions; +using Azure.Provisioning.Resources; +using Azure.Provisioning.Roles; + +namespace Aspire.Hosting.Azure.AppService; + +internal static class AzureAppServiceEnvironmentUtility +{ + const string ResourceName = "dashboard"; + + public static BicepValue DashboardHostName => BicepFunction.Take( + BicepFunction.Interpolate($"{BicepFunction.ToLower(ResourceName)}-{BicepFunction.GetUniqueString(BicepFunction.GetResourceGroup().Id)}"), 60); + + public static WebSite AddDashboard(AzureResourceInfrastructure infra, + UserAssignedIdentity otelIdentity, + UserAssignedIdentity contributorIdentity, + BicepValue appServicePlanId) + { + var acrClientIdParameter = otelIdentity.ClientId; + var contributorMidParameter = contributorIdentity.Id; + var contributorClientIdParameter = contributorIdentity.ClientId; + + var webSite = new WebSite("webapp") + { + // Use the host name as the name of the web app + Name = DashboardHostName, + AppServicePlanId = appServicePlanId, + // Aspire dashboards are created with a new kind aspiredashboard + Kind = "app,linux,aspiredashboard", + SiteConfig = new SiteConfigProperties() + { + LinuxFxVersion = "ASPIREDASHBOARD|1.0", + AcrUserManagedIdentityId = acrClientIdParameter, + UseManagedIdentityCreds = true, + IsHttp20Enabled = true, + Http20ProxyFlag = 1, + // Setting NumberOfWorkers to 1 to ensure dashboard runs of 1 instance + NumberOfWorkers = 1, + // IsAlwaysOn set to true ensures the app is always running + IsAlwaysOn = true, + AppSettings = [] + }, + Identity = new ManagedServiceIdentity() + { + ManagedServiceIdentityType = ManagedServiceIdentityType.UserAssigned, + UserAssignedIdentities = [] + } + }; + + //var acrMid = BicepFunction.Interpolate($"{acrMidParameter}").Compile().ToString(); + //webSite.Identity.UserAssignedIdentities[acrMid] = new UserAssignedIdentityDetails(); + var contributorMid = BicepFunction.Interpolate($"{contributorMidParameter}").Compile().ToString(); + webSite.Identity.UserAssignedIdentities[contributorMid] = new UserAssignedIdentityDetails(); + + // Security is handled by app service platform + webSite.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "Dashboard__Frontend__AuthMode", Value = "Unsecured" }); + webSite.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "Dashboard__Otlp__AuthMode", Value = "Unsecured" }); + webSite.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "Dashboard__ResourceServiceClient__AuthMode", Value = "Unsecured" }); + // Dashboard ports + webSite.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "WEBSITES_PORT", Value = "5000" }); + webSite.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "HTTP20_ONLY_PORT", Value = "4317" }); + // Enable SCM preloading to ensure dashboard is always available + webSite.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "WEBSITE_START_SCM_WITH_PRELOAD", Value = "true" }); + // Appsettings related to managed identity for auth + webSite.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "AZURE_CLIENT_ID", Value = contributorClientIdParameter }); + webSite.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "ALLOWED_MANAGED_IDENTITIES", Value = acrClientIdParameter }); + infra.Add(webSite); + + return webSite; + } +} diff --git a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceWebsiteContext.cs b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceWebsiteContext.cs index b8cab789a47..616fb7b3a79 100644 --- a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceWebsiteContext.cs +++ b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceWebsiteContext.cs @@ -212,6 +212,7 @@ public void BuildWebSite(AzureResourceInfrastructure infra) var acrMidParameter = environmentContext.Environment.ContainerRegistryManagedIdentityId.AsProvisioningParameter(infra); var acrClientIdParameter = environmentContext.Environment.ContainerRegistryClientId.AsProvisioningParameter(infra); var containerImage = AllocateParameter(new ContainerImageReference(Resource, environmentContext.ServiceProvider)); + var dashboardUri = environmentContext.Environment.DashboardUriReference.AsProvisioningParameter(infra); var webSite = new WebSite("webapp") { @@ -224,6 +225,9 @@ public void BuildWebSite(AzureResourceInfrastructure infra) LinuxFxVersion = "SITECONTAINERS", AcrUserManagedIdentityId = acrClientIdParameter, UseManagedIdentityCreds = true, + // Setting NumberOfWorkers to maximum allowed value for Premium SKU + // https://learn.microsoft.com/en-us/azure/app-service/manage-scale-up + NumberOfWorkers = 30, AppSettings = [] }, Identity = new ManagedServiceIdentity() @@ -323,6 +327,8 @@ static FunctionCallExpression Join(BicepExpression args, string delimeter) => } #pragma warning restore ASPIREPROBES001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. + AddDashboardSettings(webSite, acrClientIdParameter, dashboardUri); + infra.Add(webSite); // Allow users to customize the web app here @@ -362,6 +368,15 @@ private ProvisioningParameter AllocateParameter(IManifestExpressionProvider para { return parameter.AsProvisioningParameter(Infra, isSecure: secretType == SecretType.Normal); } + private void AddDashboardSettings(WebSite webSite, ProvisioningParameter acrClientIdParameter, ProvisioningParameter dashboardUri) + { + webSite.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "OTEL_SERVICE_NAME", Value = resource.Name }); + webSite.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "OTEL_EXPORTER_OTLP_PROTOCOL", Value = "grpc" }); + webSite.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "OTEL_EXPORTER_OTLP_ENDPOINT", Value = "http://localhost:6001" }); + webSite.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "WEBSITE_ENABLE_ASPIRE_OTEL_SIDECAR", Value = "true" }); + webSite.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "OTEL_COLLECTOR_URL", Value = dashboardUri }); + webSite.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "OTEL_CLIENT_ID", Value = acrClientIdParameter }); + } enum SecretType { From 4626741803ff9d88e133057fa5d4ab36fc577585 Mon Sep 17 00:00:00 2001 From: Shilpi Rachna Date: Fri, 26 Sep 2025 02:32:41 -0700 Subject: [PATCH 02/17] Fixed failing tests --- ...tainerAppToProjectResources.verified.bicep | 31 ++++++- ...ntainerAppToProjectResources.verified.json | 5 +- ...mentAddsEnvironmentResource.verified.bicep | 90 ++++++++++++++++++- ...ServiceToContainerResources.verified.bicep | 31 ++++++- ...pServiceToContainerResources.verified.json | 7 +- ...renceExistingAppServicePlan.verified.bicep | 90 ++++++++++++++++++- ...pportBaitAndSwitchResources.verified.bicep | 31 ++++++- ...upportBaitAndSwitchResources.verified.json | 7 +- ...esAreResolvedAcrossProjects.verified.bicep | 31 ++++++- ...cesAreResolvedAcrossProjects.verified.json | 5 +- ...s.KeyvaultReferenceHandling.verified.bicep | 29 +++++- ...ts.KeyvaultReferenceHandling.verified.json | 5 +- ...ServiceEnvironmentsSupported.verified.json | 6 +- ...iceTests.ResourceWithProbes.verified.bicep | 31 ++++++- ...andledWithAllocateParameter.verified.bicep | 31 ++++++- ...HandledWithAllocateParameter.verified.json | 5 +- 16 files changed, 402 insertions(+), 33 deletions(-) diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsDeploymentTargetWithContainerAppToProjectResources.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsDeploymentTargetWithContainerAppToProjectResources.verified.bicep index be4ba9aacbd..0748b06814d 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsDeploymentTargetWithContainerAppToProjectResources.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsDeploymentTargetWithContainerAppToProjectResources.verified.bicep @@ -1,4 +1,4 @@ -@description('The location for the resource(s) to be deployed.') +@description('The location for the resource(s) to be deployed.') param location string = resourceGroup().location param env_outputs_azure_container_registry_endpoint string @@ -11,6 +11,8 @@ param env_outputs_azure_container_registry_managed_identity_client_id string param api_containerimage string +param env_outputs_dashboard_uri string + param api_containerport string resource mainContainer 'Microsoft.Web/sites/sitecontainers@2024-11-01' = { @@ -30,6 +32,7 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { properties: { serverFarmId: env_outputs_planid siteConfig: { + numberOfWorkers: 30 linuxFxVersion: 'SITECONTAINERS' acrUseManagedIdentityCreds: true acrUserManagedIdentityID: env_outputs_azure_container_registry_managed_identity_client_id @@ -54,6 +57,30 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { name: 'HTTP_PORTS' value: api_containerport } + { + name: 'OTEL_SERVICE_NAME' + value: 'api' + } + { + name: 'OTEL_EXPORTER_OTLP_PROTOCOL' + value: 'grpc' + } + { + name: 'OTEL_EXPORTER_OTLP_ENDPOINT' + value: 'http://localhost:6001' + } + { + name: 'WEBSITE_ENABLE_ASPIRE_OTEL_SIDECAR' + value: 'true' + } + { + name: 'OTEL_COLLECTOR_URL' + value: env_outputs_dashboard_uri + } + { + name: 'OTEL_CLIENT_ID' + value: env_outputs_azure_container_registry_managed_identity_client_id + } ] webSocketsEnabled: true } @@ -64,4 +91,4 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { '${env_outputs_azure_container_registry_managed_identity_id}': { } } } -} \ No newline at end of file +} diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsDeploymentTargetWithContainerAppToProjectResources.verified.json b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsDeploymentTargetWithContainerAppToProjectResources.verified.json index 3cdded0b976..22e6a4cb8b1 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsDeploymentTargetWithContainerAppToProjectResources.verified.json +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsDeploymentTargetWithContainerAppToProjectResources.verified.json @@ -1,4 +1,4 @@ -{ +{ "type": "azure.bicep.v0", "path": "api.module.bicep", "params": { @@ -7,6 +7,7 @@ "env_outputs_azure_container_registry_managed_identity_id": "{env.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID}", "env_outputs_azure_container_registry_managed_identity_client_id": "{env.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_CLIENT_ID}", "api_containerimage": "{api.containerImage}", + "env_outputs_dashboard_uri": "{env.outputs.DASHBOARD_URI}", "api_containerport": "{api.containerPort}" } -} \ No newline at end of file +} diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsEnvironmentResource.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsEnvironmentResource.verified.bicep index ecca0504dd1..edbe543526c 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsEnvironmentResource.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsEnvironmentResource.verified.bicep @@ -1,4 +1,4 @@ -@description('The location for the resource(s) to be deployed.') +@description('The location for the resource(s) to be deployed.') param location string = resourceGroup().location param userPrincipalId string = '' @@ -11,6 +11,11 @@ resource env_mi 'Microsoft.ManagedIdentity/userAssignedIdentities@2024-11-30' = tags: tags } +resource env_contributor_mi 'Microsoft.ManagedIdentity/userAssignedIdentities@2024-11-30' = { + name: take('env_contributor_mi-${uniqueString(resourceGroup().id)}', 128) + location: location +} + resource env_acr 'Microsoft.ContainerRegistry/registries@2025-04-01' = { name: take('envacr${uniqueString(resourceGroup().id)}', 50) location: location @@ -34,6 +39,7 @@ resource env_asplan 'Microsoft.Web/serverfarms@2024-11-01' = { name: take('envasplan-${uniqueString(resourceGroup().id)}', 60) location: location properties: { + perSiteScaling: true reserved: true } kind: 'Linux' @@ -43,6 +49,82 @@ resource env_asplan 'Microsoft.Web/serverfarms@2024-11-01' = { } } +resource env_ra 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(resourceGroup().id, env_contributor_mi.id, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772')) + properties: { + principalId: env_contributor_mi.properties.principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772') + principalType: 'ServicePrincipal' + } +} + +resource env_ra2 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(resourceGroup().id, env_contributor_mi.id, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')) + properties: { + principalId: env_contributor_mi.properties.principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') + principalType: 'ServicePrincipal' + } +} + +resource webapp 'Microsoft.Web/sites@2024-11-01' = { + name: take('${toLower('dashboard')}-${uniqueString(resourceGroup().id)}', 60) + location: location + properties: { + serverFarmId: env_asplan.id + siteConfig: { + numberOfWorkers: 1 + linuxFxVersion: 'ASPIREDASHBOARD|1.0' + acrUseManagedIdentityCreds: true + acrUserManagedIdentityID: env_mi.properties.clientId + appSettings: [ + { + name: 'Dashboard__Frontend__AuthMode' + value: 'Unsecured' + } + { + name: 'Dashboard__Otlp__AuthMode' + value: 'Unsecured' + } + { + name: 'Dashboard__ResourceServiceClient__AuthMode' + value: 'Unsecured' + } + { + name: 'WEBSITES_PORT' + value: '5000' + } + { + name: 'HTTP20_ONLY_PORT' + value: '4317' + } + { + name: 'WEBSITE_START_SCM_WITH_PRELOAD' + value: 'true' + } + { + name: 'AZURE_CLIENT_ID' + value: env_contributor_mi.properties.clientId + } + { + name: 'ALLOWED_MANAGED_IDENTITIES' + value: env_mi.properties.clientId + } + ] + alwaysOn: true + http20Enabled: true + http20ProxyFlag: 1 + } + } + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${env_contributor_mi.id}': { } + } + } + kind: 'app,linux,aspiredashboard' +} + output name string = env_asplan.name output planId string = env_asplan.id @@ -53,4 +135,8 @@ output AZURE_CONTAINER_REGISTRY_ENDPOINT string = env_acr.properties.loginServer output AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID string = env_mi.id -output AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_CLIENT_ID string = env_mi.properties.clientId \ No newline at end of file +output AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_CLIENT_ID string = env_mi.properties.clientId + +output AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_NAME string = env_mi.name + +output DASHBOARD_URI string = 'https://${take('${toLower('dashboard')}-${uniqueString(resourceGroup().id)}', 60)}.azurewebsites.net' diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddDockerfileWithAppServiceInfrastructureAddsDeploymentTargetWithAppServiceToContainerResources.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddDockerfileWithAppServiceInfrastructureAddsDeploymentTargetWithAppServiceToContainerResources.verified.bicep index d08f805592e..cf1c94ef78d 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddDockerfileWithAppServiceInfrastructureAddsDeploymentTargetWithAppServiceToContainerResources.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddDockerfileWithAppServiceInfrastructureAddsDeploymentTargetWithAppServiceToContainerResources.verified.bicep @@ -1,4 +1,4 @@ -@description('The location for the resource(s) to be deployed.') +@description('The location for the resource(s) to be deployed.') param location string = resourceGroup().location param env_outputs_azure_container_registry_endpoint string @@ -11,6 +11,8 @@ param env_outputs_azure_container_registry_managed_identity_client_id string param api_containerimage string +param env_outputs_dashboard_uri string + resource mainContainer 'Microsoft.Web/sites/sitecontainers@2024-11-01' = { name: 'main' properties: { @@ -28,6 +30,7 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { properties: { serverFarmId: env_outputs_planid siteConfig: { + numberOfWorkers: 30 linuxFxVersion: 'SITECONTAINERS' acrUseManagedIdentityCreds: true acrUserManagedIdentityID: env_outputs_azure_container_registry_managed_identity_client_id @@ -36,6 +39,30 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { name: 'PORT' value: '85' } + { + name: 'OTEL_SERVICE_NAME' + value: 'api' + } + { + name: 'OTEL_EXPORTER_OTLP_PROTOCOL' + value: 'grpc' + } + { + name: 'OTEL_EXPORTER_OTLP_ENDPOINT' + value: 'http://localhost:6001' + } + { + name: 'WEBSITE_ENABLE_ASPIRE_OTEL_SIDECAR' + value: 'true' + } + { + name: 'OTEL_COLLECTOR_URL' + value: env_outputs_dashboard_uri + } + { + name: 'OTEL_CLIENT_ID' + value: env_outputs_azure_container_registry_managed_identity_client_id + } ] } } @@ -45,4 +72,4 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { '${env_outputs_azure_container_registry_managed_identity_id}': { } } } -} \ No newline at end of file +} diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddDockerfileWithAppServiceInfrastructureAddsDeploymentTargetWithAppServiceToContainerResources.verified.json b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddDockerfileWithAppServiceInfrastructureAddsDeploymentTargetWithAppServiceToContainerResources.verified.json index 4006e252c4f..429d17cf3a1 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddDockerfileWithAppServiceInfrastructureAddsDeploymentTargetWithAppServiceToContainerResources.verified.json +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddDockerfileWithAppServiceInfrastructureAddsDeploymentTargetWithAppServiceToContainerResources.verified.json @@ -1,4 +1,4 @@ -{ +{ "type": "azure.bicep.v0", "path": "api.module.bicep", "params": { @@ -6,6 +6,7 @@ "env_outputs_planid": "{env.outputs.planId}", "env_outputs_azure_container_registry_managed_identity_id": "{env.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID}", "env_outputs_azure_container_registry_managed_identity_client_id": "{env.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_CLIENT_ID}", - "api_containerimage": "{api.containerImage}" + "api_containerimage": "{api.containerImage}", + "env_outputs_dashboard_uri": "{env.outputs.DASHBOARD_URI}" } -} \ No newline at end of file +} diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceEnvironmentCanReferenceExistingAppServicePlan.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceEnvironmentCanReferenceExistingAppServicePlan.verified.bicep index ecca0504dd1..edbe543526c 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceEnvironmentCanReferenceExistingAppServicePlan.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceEnvironmentCanReferenceExistingAppServicePlan.verified.bicep @@ -1,4 +1,4 @@ -@description('The location for the resource(s) to be deployed.') +@description('The location for the resource(s) to be deployed.') param location string = resourceGroup().location param userPrincipalId string = '' @@ -11,6 +11,11 @@ resource env_mi 'Microsoft.ManagedIdentity/userAssignedIdentities@2024-11-30' = tags: tags } +resource env_contributor_mi 'Microsoft.ManagedIdentity/userAssignedIdentities@2024-11-30' = { + name: take('env_contributor_mi-${uniqueString(resourceGroup().id)}', 128) + location: location +} + resource env_acr 'Microsoft.ContainerRegistry/registries@2025-04-01' = { name: take('envacr${uniqueString(resourceGroup().id)}', 50) location: location @@ -34,6 +39,7 @@ resource env_asplan 'Microsoft.Web/serverfarms@2024-11-01' = { name: take('envasplan-${uniqueString(resourceGroup().id)}', 60) location: location properties: { + perSiteScaling: true reserved: true } kind: 'Linux' @@ -43,6 +49,82 @@ resource env_asplan 'Microsoft.Web/serverfarms@2024-11-01' = { } } +resource env_ra 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(resourceGroup().id, env_contributor_mi.id, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772')) + properties: { + principalId: env_contributor_mi.properties.principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772') + principalType: 'ServicePrincipal' + } +} + +resource env_ra2 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(resourceGroup().id, env_contributor_mi.id, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')) + properties: { + principalId: env_contributor_mi.properties.principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') + principalType: 'ServicePrincipal' + } +} + +resource webapp 'Microsoft.Web/sites@2024-11-01' = { + name: take('${toLower('dashboard')}-${uniqueString(resourceGroup().id)}', 60) + location: location + properties: { + serverFarmId: env_asplan.id + siteConfig: { + numberOfWorkers: 1 + linuxFxVersion: 'ASPIREDASHBOARD|1.0' + acrUseManagedIdentityCreds: true + acrUserManagedIdentityID: env_mi.properties.clientId + appSettings: [ + { + name: 'Dashboard__Frontend__AuthMode' + value: 'Unsecured' + } + { + name: 'Dashboard__Otlp__AuthMode' + value: 'Unsecured' + } + { + name: 'Dashboard__ResourceServiceClient__AuthMode' + value: 'Unsecured' + } + { + name: 'WEBSITES_PORT' + value: '5000' + } + { + name: 'HTTP20_ONLY_PORT' + value: '4317' + } + { + name: 'WEBSITE_START_SCM_WITH_PRELOAD' + value: 'true' + } + { + name: 'AZURE_CLIENT_ID' + value: env_contributor_mi.properties.clientId + } + { + name: 'ALLOWED_MANAGED_IDENTITIES' + value: env_mi.properties.clientId + } + ] + alwaysOn: true + http20Enabled: true + http20ProxyFlag: 1 + } + } + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${env_contributor_mi.id}': { } + } + } + kind: 'app,linux,aspiredashboard' +} + output name string = env_asplan.name output planId string = env_asplan.id @@ -53,4 +135,8 @@ output AZURE_CONTAINER_REGISTRY_ENDPOINT string = env_acr.properties.loginServer output AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID string = env_mi.id -output AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_CLIENT_ID string = env_mi.properties.clientId \ No newline at end of file +output AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_CLIENT_ID string = env_mi.properties.clientId + +output AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_NAME string = env_mi.name + +output DASHBOARD_URI string = 'https://${take('${toLower('dashboard')}-${uniqueString(resourceGroup().id)}', 60)}.azurewebsites.net' diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceSupportBaitAndSwitchResources.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceSupportBaitAndSwitchResources.verified.bicep index a4c2c942b80..6d914146902 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceSupportBaitAndSwitchResources.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceSupportBaitAndSwitchResources.verified.bicep @@ -1,4 +1,4 @@ -@description('The location for the resource(s) to be deployed.') +@description('The location for the resource(s) to be deployed.') param location string = resourceGroup().location param env_outputs_azure_container_registry_endpoint string @@ -11,6 +11,8 @@ param env_outputs_azure_container_registry_managed_identity_client_id string param api_containerimage string +param env_outputs_dashboard_uri string + resource mainContainer 'Microsoft.Web/sites/sitecontainers@2024-11-01' = { name: 'main' properties: { @@ -28,6 +30,7 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { properties: { serverFarmId: env_outputs_planid siteConfig: { + numberOfWorkers: 30 linuxFxVersion: 'SITECONTAINERS' acrUseManagedIdentityCreds: true acrUserManagedIdentityID: env_outputs_azure_container_registry_managed_identity_client_id @@ -52,6 +55,30 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { name: 'PORT' value: '80' } + { + name: 'OTEL_SERVICE_NAME' + value: 'api' + } + { + name: 'OTEL_EXPORTER_OTLP_PROTOCOL' + value: 'grpc' + } + { + name: 'OTEL_EXPORTER_OTLP_ENDPOINT' + value: 'http://localhost:6001' + } + { + name: 'WEBSITE_ENABLE_ASPIRE_OTEL_SIDECAR' + value: 'true' + } + { + name: 'OTEL_COLLECTOR_URL' + value: env_outputs_dashboard_uri + } + { + name: 'OTEL_CLIENT_ID' + value: env_outputs_azure_container_registry_managed_identity_client_id + } ] } } @@ -61,4 +88,4 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { '${env_outputs_azure_container_registry_managed_identity_id}': { } } } -} \ No newline at end of file +} diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceSupportBaitAndSwitchResources.verified.json b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceSupportBaitAndSwitchResources.verified.json index 4006e252c4f..429d17cf3a1 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceSupportBaitAndSwitchResources.verified.json +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceSupportBaitAndSwitchResources.verified.json @@ -1,4 +1,4 @@ -{ +{ "type": "azure.bicep.v0", "path": "api.module.bicep", "params": { @@ -6,6 +6,7 @@ "env_outputs_planid": "{env.outputs.planId}", "env_outputs_azure_container_registry_managed_identity_id": "{env.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID}", "env_outputs_azure_container_registry_managed_identity_client_id": "{env.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_CLIENT_ID}", - "api_containerimage": "{api.containerImage}" + "api_containerimage": "{api.containerImage}", + "env_outputs_dashboard_uri": "{env.outputs.DASHBOARD_URI}" } -} \ No newline at end of file +} diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.EndpointReferencesAreResolvedAcrossProjects.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.EndpointReferencesAreResolvedAcrossProjects.verified.bicep index 055a4988c46..7d7106ac072 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.EndpointReferencesAreResolvedAcrossProjects.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.EndpointReferencesAreResolvedAcrossProjects.verified.bicep @@ -1,4 +1,4 @@ -@description('The location for the resource(s) to be deployed.') +@description('The location for the resource(s) to be deployed.') param location string = resourceGroup().location param env_outputs_azure_container_registry_endpoint string @@ -11,6 +11,8 @@ param env_outputs_azure_container_registry_managed_identity_client_id string param project2_containerimage string +param env_outputs_dashboard_uri string + param project2_containerport string resource mainContainer 'Microsoft.Web/sites/sitecontainers@2024-11-01' = { @@ -30,6 +32,7 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { properties: { serverFarmId: env_outputs_planid siteConfig: { + numberOfWorkers: 30 linuxFxVersion: 'SITECONTAINERS' acrUseManagedIdentityCreds: true acrUserManagedIdentityID: env_outputs_azure_container_registry_managed_identity_client_id @@ -58,6 +61,30 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { name: 'services__project1__http__0' value: 'http://${take('${toLower('project1')}-${uniqueString(resourceGroup().id)}', 60)}.azurewebsites.net' } + { + name: 'OTEL_SERVICE_NAME' + value: 'project2' + } + { + name: 'OTEL_EXPORTER_OTLP_PROTOCOL' + value: 'grpc' + } + { + name: 'OTEL_EXPORTER_OTLP_ENDPOINT' + value: 'http://localhost:6001' + } + { + name: 'WEBSITE_ENABLE_ASPIRE_OTEL_SIDECAR' + value: 'true' + } + { + name: 'OTEL_COLLECTOR_URL' + value: env_outputs_dashboard_uri + } + { + name: 'OTEL_CLIENT_ID' + value: env_outputs_azure_container_registry_managed_identity_client_id + } ] } } @@ -67,4 +94,4 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { '${env_outputs_azure_container_registry_managed_identity_id}': { } } } -} \ No newline at end of file +} diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.EndpointReferencesAreResolvedAcrossProjects.verified.json b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.EndpointReferencesAreResolvedAcrossProjects.verified.json index 133bb4d92b4..c4f63ce4f70 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.EndpointReferencesAreResolvedAcrossProjects.verified.json +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.EndpointReferencesAreResolvedAcrossProjects.verified.json @@ -1,4 +1,4 @@ -{ +{ "type": "azure.bicep.v0", "path": "project2.module.bicep", "params": { @@ -7,6 +7,7 @@ "env_outputs_azure_container_registry_managed_identity_id": "{env.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID}", "env_outputs_azure_container_registry_managed_identity_client_id": "{env.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_CLIENT_ID}", "project2_containerimage": "{project2.containerImage}", + "env_outputs_dashboard_uri": "{env.outputs.DASHBOARD_URI}", "project2_containerport": "{project2.containerPort}" } -} \ No newline at end of file +} diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.KeyvaultReferenceHandling.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.KeyvaultReferenceHandling.verified.bicep index d02597b2721..cb77709d867 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.KeyvaultReferenceHandling.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.KeyvaultReferenceHandling.verified.bicep @@ -1,4 +1,4 @@ -@description('The location for the resource(s) to be deployed.') +@description('The location for the resource(s) to be deployed.') param location string = resourceGroup().location param env_outputs_azure_container_registry_endpoint string @@ -11,6 +11,8 @@ param env_outputs_azure_container_registry_managed_identity_client_id string param api_containerimage string +param env_outputs_dashboard_uri string + param mydb_kv_outputs_name string param kvName string @@ -58,6 +60,7 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { serverFarmId: env_outputs_planid keyVaultReferenceIdentity: api_identity_outputs_id siteConfig: { + numberOfWorkers: 30 linuxFxVersion: 'SITECONTAINERS' acrUseManagedIdentityCreds: true acrUserManagedIdentityID: env_outputs_azure_container_registry_managed_identity_client_id @@ -86,6 +89,30 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { name: 'AZURE_CLIENT_ID' value: api_identity_outputs_clientid } + { + name: 'OTEL_SERVICE_NAME' + value: 'api' + } + { + name: 'OTEL_EXPORTER_OTLP_PROTOCOL' + value: 'grpc' + } + { + name: 'OTEL_EXPORTER_OTLP_ENDPOINT' + value: 'http://localhost:6001' + } + { + name: 'WEBSITE_ENABLE_ASPIRE_OTEL_SIDECAR' + value: 'true' + } + { + name: 'OTEL_COLLECTOR_URL' + value: env_outputs_dashboard_uri + } + { + name: 'OTEL_CLIENT_ID' + value: env_outputs_azure_container_registry_managed_identity_client_id + } ] } } diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.KeyvaultReferenceHandling.verified.json b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.KeyvaultReferenceHandling.verified.json index d44c762d50b..ac0ae225a11 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.KeyvaultReferenceHandling.verified.json +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.KeyvaultReferenceHandling.verified.json @@ -1,4 +1,4 @@ -{ +{ "type": "azure.bicep.v0", "path": "api.module.bicep", "params": { @@ -7,10 +7,11 @@ "env_outputs_azure_container_registry_managed_identity_id": "{env.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID}", "env_outputs_azure_container_registry_managed_identity_client_id": "{env.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_CLIENT_ID}", "api_containerimage": "{api.containerImage}", + "env_outputs_dashboard_uri": "{env.outputs.DASHBOARD_URI}", "mydb_kv_outputs_name": "{mydb-kv.outputs.name}", "kvName": "{kvName.value}", "sharedRg": "{sharedRg.value}", "api_identity_outputs_id": "{api-identity.outputs.id}", "api_identity_outputs_clientid": "{api-identity.outputs.clientId}" } -} \ No newline at end of file +} diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.MultipleAzureAppServiceEnvironmentsSupported.verified.json b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.MultipleAzureAppServiceEnvironmentsSupported.verified.json index ab5ddeb4b2f..894bf24503d 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.MultipleAzureAppServiceEnvironmentsSupported.verified.json +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.MultipleAzureAppServiceEnvironmentsSupported.verified.json @@ -1,4 +1,4 @@ -{ +{ "$schema": "https://json.schemastore.org/aspire-8.0.json", "resources": { "env1": { @@ -26,6 +26,7 @@ "env1_outputs_azure_container_registry_managed_identity_id": "{env1.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID}", "env1_outputs_azure_container_registry_managed_identity_client_id": "{env1.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_CLIENT_ID}", "servicea_containerimage": "{ServiceA.containerImage}", + "env1_outputs_dashboard_uri": "{env1.outputs.DASHBOARD_URI}", "servicea_containerport": "{ServiceA.containerPort}" } }, @@ -62,6 +63,7 @@ "env2_outputs_azure_container_registry_managed_identity_id": "{env2.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID}", "env2_outputs_azure_container_registry_managed_identity_client_id": "{env2.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_CLIENT_ID}", "serviceb_containerimage": "{ServiceB.containerImage}", + "env2_outputs_dashboard_uri": "{env2.outputs.DASHBOARD_URI}", "serviceb_containerport": "{ServiceB.containerPort}" } }, @@ -88,4 +90,4 @@ } } } -} \ No newline at end of file +} diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.ResourceWithProbes.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.ResourceWithProbes.verified.bicep index 81903b3ad5c..7bd7ccb2f68 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.ResourceWithProbes.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.ResourceWithProbes.verified.bicep @@ -1,4 +1,4 @@ -@description('The location for the resource(s) to be deployed.') +@description('The location for the resource(s) to be deployed.') param location string = resourceGroup().location param env_outputs_azure_container_registry_endpoint string @@ -11,6 +11,8 @@ param env_outputs_azure_container_registry_managed_identity_client_id string param project1_containerimage string +param env_outputs_dashboard_uri string + param project1_containerport string resource mainContainer 'Microsoft.Web/sites/sitecontainers@2024-11-01' = { @@ -30,6 +32,7 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { properties: { serverFarmId: env_outputs_planid siteConfig: { + numberOfWorkers: 30 linuxFxVersion: 'SITECONTAINERS' acrUseManagedIdentityCreds: true acrUserManagedIdentityID: env_outputs_azure_container_registry_managed_identity_client_id @@ -54,6 +57,30 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { name: 'HTTPS_PORTS' value: project1_containerport } + { + name: 'OTEL_SERVICE_NAME' + value: 'project1' + } + { + name: 'OTEL_EXPORTER_OTLP_PROTOCOL' + value: 'grpc' + } + { + name: 'OTEL_EXPORTER_OTLP_ENDPOINT' + value: 'http://localhost:6001' + } + { + name: 'WEBSITE_ENABLE_ASPIRE_OTEL_SIDECAR' + value: 'true' + } + { + name: 'OTEL_COLLECTOR_URL' + value: env_outputs_dashboard_uri + } + { + name: 'OTEL_CLIENT_ID' + value: env_outputs_azure_container_registry_managed_identity_client_id + } ] healthCheckPath: '/health' } @@ -64,4 +91,4 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { '${env_outputs_azure_container_registry_managed_identity_id}': { } } } -} \ No newline at end of file +} diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.UnknownManifestExpressionProviderIsHandledWithAllocateParameter.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.UnknownManifestExpressionProviderIsHandledWithAllocateParameter.verified.bicep index 49f1e990488..f944e72fb16 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.UnknownManifestExpressionProviderIsHandledWithAllocateParameter.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.UnknownManifestExpressionProviderIsHandledWithAllocateParameter.verified.bicep @@ -1,4 +1,4 @@ -@description('The location for the resource(s) to be deployed.') +@description('The location for the resource(s) to be deployed.') param location string = resourceGroup().location param env_outputs_azure_container_registry_endpoint string @@ -11,6 +11,8 @@ param env_outputs_azure_container_registry_managed_identity_client_id string param api_containerimage string +param env_outputs_dashboard_uri string + param api_containerport string param customvalue string @@ -32,6 +34,7 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { properties: { serverFarmId: env_outputs_planid siteConfig: { + numberOfWorkers: 30 linuxFxVersion: 'SITECONTAINERS' acrUseManagedIdentityCreds: true acrUserManagedIdentityID: env_outputs_azure_container_registry_managed_identity_client_id @@ -60,6 +63,30 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { name: 'CUSTOM_VALUE' value: customvalue } + { + name: 'OTEL_SERVICE_NAME' + value: 'api' + } + { + name: 'OTEL_EXPORTER_OTLP_PROTOCOL' + value: 'grpc' + } + { + name: 'OTEL_EXPORTER_OTLP_ENDPOINT' + value: 'http://localhost:6001' + } + { + name: 'WEBSITE_ENABLE_ASPIRE_OTEL_SIDECAR' + value: 'true' + } + { + name: 'OTEL_COLLECTOR_URL' + value: env_outputs_dashboard_uri + } + { + name: 'OTEL_CLIENT_ID' + value: env_outputs_azure_container_registry_managed_identity_client_id + } ] } } @@ -69,4 +96,4 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { '${env_outputs_azure_container_registry_managed_identity_id}': { } } } -} \ No newline at end of file +} diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.UnknownManifestExpressionProviderIsHandledWithAllocateParameter.verified.json b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.UnknownManifestExpressionProviderIsHandledWithAllocateParameter.verified.json index 21afcf7ade7..190d384115d 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.UnknownManifestExpressionProviderIsHandledWithAllocateParameter.verified.json +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.UnknownManifestExpressionProviderIsHandledWithAllocateParameter.verified.json @@ -1,4 +1,4 @@ -{ +{ "type": "azure.bicep.v0", "path": "api.module.bicep", "params": { @@ -7,7 +7,8 @@ "env_outputs_azure_container_registry_managed_identity_id": "{env.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID}", "env_outputs_azure_container_registry_managed_identity_client_id": "{env.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_CLIENT_ID}", "api_containerimage": "{api.containerImage}", + "env_outputs_dashboard_uri": "{env.outputs.DASHBOARD_URI}", "api_containerport": "{api.containerPort}", "customvalue": "{customValue}" } -} \ No newline at end of file +} From 2baefd2c970f0bc0bafc097ad312ea73172f31c0 Mon Sep 17 00:00:00 2001 From: Shilpi Rachna Date: Fri, 26 Sep 2025 02:40:14 -0700 Subject: [PATCH 03/17] Update src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentExtensions.cs Co-authored-by: James Newton-King --- .../AzureAppServiceEnvironmentExtensions.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentExtensions.cs b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentExtensions.cs index 9c8ce7576a4..8497f3292d5 100644 --- a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentExtensions.cs +++ b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentExtensions.cs @@ -65,9 +65,7 @@ public static IResourceBuilder AddAzureAppSe infra.Add(identity); - var contributorIdentity = new UserAssignedIdentity(Infrastructure.NormalizeBicepIdentifier($"{prefix}-contributor-mi")) - { - }; + var contributorIdentity = new UserAssignedIdentity(Infrastructure.NormalizeBicepIdentifier($"{prefix}-contributor-mi")); infra.Add(contributorIdentity); From f21c564f89a704ff3dd9660a9ce64b6f077a06e8 Mon Sep 17 00:00:00 2001 From: Shilpi Rachna Date: Fri, 26 Sep 2025 04:46:26 -0700 Subject: [PATCH 04/17] Update src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentUtility.cs Co-authored-by: James Newton-King --- .../AzureAppServiceEnvironmentUtility.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentUtility.cs b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentUtility.cs index 62602e3e28d..13756da0bf0 100644 --- a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentUtility.cs +++ b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentUtility.cs @@ -61,6 +61,7 @@ public static WebSite AddDashboard(AzureResourceInfrastructure infra, // Security is handled by app service platform webSite.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "Dashboard__Frontend__AuthMode", Value = "Unsecured" }); webSite.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "Dashboard__Otlp__AuthMode", Value = "Unsecured" }); + webSite.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "Dashboard__Otlp__SuppressUnsecuredTelemetryMessage", Value = "true" }); webSite.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "Dashboard__ResourceServiceClient__AuthMode", Value = "Unsecured" }); // Dashboard ports webSite.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "WEBSITES_PORT", Value = "5000" }); From 622ba2db7c2b921eb3deb2fb440be6d2f986c463 Mon Sep 17 00:00:00 2001 From: Shilpi Rachna Date: Tue, 30 Sep 2025 04:09:24 -0700 Subject: [PATCH 05/17] Added option to exclude dashboard and moved contributor identity to dashboard utility --- .../AzureAppServiceEnvironmentExtensions.cs | 57 +++++---------- .../AzureAppServiceEnvironmentResource.cs | 6 ++ .../AzureAppServiceEnvironmentUtility.cs | 40 +++++++++- .../AzureAppServiceWebsiteContext.cs | 5 +- .../AzureAppServiceTests.cs | 56 ++++++++++++++ ...oardAddsEnvironmentResource.verified.bicep | 61 ++++++++++++++++ ...boardAddsEnvironmentResource.verified.json | 7 ++ ...EnvironmentWithoutDashboard.verified.bicep | 73 +++++++++++++++++++ ...oEnvironmentWithoutDashboard.verified.json | 13 ++++ ...mentAddsEnvironmentResource.verified.bicep | 10 +-- ...renceExistingAppServicePlan.verified.bicep | 10 +-- 11 files changed, 284 insertions(+), 54 deletions(-) create mode 100644 tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddAppServiceEnvironmentWithoutDashboardAddsEnvironmentResource.verified.bicep create mode 100644 tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddAppServiceEnvironmentWithoutDashboardAddsEnvironmentResource.verified.json create mode 100644 tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddAppServiceToEnvironmentWithoutDashboard.verified.bicep create mode 100644 tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddAppServiceToEnvironmentWithoutDashboard.verified.json diff --git a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentExtensions.cs b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentExtensions.cs index 9c8ce7576a4..6be91cde785 100644 --- a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentExtensions.cs +++ b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentExtensions.cs @@ -7,7 +7,6 @@ using Aspire.Hosting.Lifecycle; using Azure.Provisioning; using Azure.Provisioning.AppService; -using Azure.Provisioning.Authorization; using Azure.Provisioning.ContainerRegistry; using Azure.Provisioning.Expressions; using Azure.Provisioning.Roles; @@ -45,7 +44,7 @@ public static IResourceBuilder AddAzureAppSe var resource = new AzureAppServiceEnvironmentResource(name, static infra => { var prefix = infra.AspireResource.Name; - var resource = infra.AspireResource; + var resource = (AzureAppServiceEnvironmentResource)infra.AspireResource; // This tells azd to avoid creating infrastructure var userPrincipalId = new ProvisioningParameter(AzureBicepResource.KnownParameters.UserPrincipalId, typeof(string)) { Value = new BicepValue(string.Empty) }; @@ -65,12 +64,6 @@ public static IResourceBuilder AddAzureAppSe infra.Add(identity); - var contributorIdentity = new UserAssignedIdentity(Infrastructure.NormalizeBicepIdentifier($"{prefix}-contributor-mi")) - { - }; - - infra.Add(contributorIdentity); - ContainerRegistryService? containerRegistry = null; #pragma warning disable ASPIRECOMPUTE001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. if (resource.TryGetLastAnnotation(out var registryReferenceAnnotation) && registryReferenceAnnotation.Registry is AzureProvisioningResource registry) @@ -120,39 +113,11 @@ public static IResourceBuilder AddAzureAppSe Value = plan.Id }); - // Add Website Contributor role assignment - var rgRaId = BicepFunction.GetSubscriptionResourceId( - "Microsoft.Authorization/roleDefinitions", - "de139f84-1756-47ae-9be6-808fbbe84772"); - var rgRaName = BicepFunction.CreateGuid(BicepFunction.GetResourceGroup().Id, contributorIdentity.Id, rgRaId); - var rgRa = new RoleAssignment(Infrastructure.NormalizeBicepIdentifier($"{prefix}_ra")) - { - Name = rgRaName, - PrincipalType = RoleManagementPrincipalType.ServicePrincipal, - PrincipalId = contributorIdentity.PrincipalId, - RoleDefinitionId = rgRaId, - }; - - infra.Add(rgRa); - - // Add Reader role assignment - var rgRaId2 = BicepFunction.GetSubscriptionResourceId( - "Microsoft.Authorization/roleDefinitions", - "acdd72a7-3385-48ef-bd42-f606fba81ae7"); - var rgRaName2 = BicepFunction.CreateGuid(BicepFunction.GetResourceGroup().Id, contributorIdentity.Id, rgRaId2); - - var rgRa2 = new RoleAssignment(Infrastructure.NormalizeBicepIdentifier($"{prefix}_ra2")) + if (resource.EnableDashboard) { - Name = rgRaName2, - PrincipalType = RoleManagementPrincipalType.ServicePrincipal, - PrincipalId = contributorIdentity.PrincipalId, - RoleDefinitionId = rgRaId2 - }; - - infra.Add(rgRa2); - - // Add aspire dashboard website - var website = AzureAppServiceEnvironmentUtility.AddDashboard(infra, identity, contributorIdentity, plan.Id); + // Add aspire dashboard website + var website = AzureAppServiceEnvironmentUtility.AddDashboard(infra, identity, plan.Id); + } infra.Add(new ProvisioningOutput("AZURE_CONTAINER_REGISTRY_NAME", typeof(string)) { @@ -193,4 +158,16 @@ public static IResourceBuilder AddAzureAppSe return builder.AddResource(resource); } + + /// + /// Configures whether the Aspire dashboard should be included in the Azure App Service environment. + /// + /// The AzureAppServiceEnvironmentResource 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/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentResource.cs b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentResource.cs index 4347f99461e..611f8300fe0 100644 --- a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentResource.cs +++ b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentResource.cs @@ -27,6 +27,12 @@ public class AzureAppServiceEnvironmentResource(string name, Action new("AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID", this); internal BicepOutputReference ContainerRegistryClientId => new("AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_CLIENT_ID", this); + /// + /// 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 name of the App Service Plan. /// diff --git a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentUtility.cs b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentUtility.cs index 62602e3e28d..8c8d55e3559 100644 --- a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentUtility.cs +++ b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentUtility.cs @@ -4,6 +4,7 @@ using Azure.Core; using Azure.Provisioning; using Azure.Provisioning.AppService; +using Azure.Provisioning.Authorization; using Azure.Provisioning.Expressions; using Azure.Provisioning.Resources; using Azure.Provisioning.Roles; @@ -19,13 +20,48 @@ internal static class AzureAppServiceEnvironmentUtility public static WebSite AddDashboard(AzureResourceInfrastructure infra, UserAssignedIdentity otelIdentity, - UserAssignedIdentity contributorIdentity, BicepValue appServicePlanId) { var acrClientIdParameter = otelIdentity.ClientId; + var prefix = infra.AspireResource.Name; + var contributorIdentity = new UserAssignedIdentity(Infrastructure.NormalizeBicepIdentifier($"{prefix}-contributor-mi")); + var contributorMidParameter = contributorIdentity.Id; var contributorClientIdParameter = contributorIdentity.ClientId; + infra.Add(contributorIdentity); + + // Add Website Contributor role assignment + var rgRaId = BicepFunction.GetSubscriptionResourceId( + "Microsoft.Authorization/roleDefinitions", + "de139f84-1756-47ae-9be6-808fbbe84772"); + var rgRaName = BicepFunction.CreateGuid(BicepFunction.GetResourceGroup().Id, contributorIdentity.Id, rgRaId); + var rgRa = new RoleAssignment(Infrastructure.NormalizeBicepIdentifier($"{prefix}_ra")) + { + Name = rgRaName, + PrincipalType = RoleManagementPrincipalType.ServicePrincipal, + PrincipalId = contributorIdentity.PrincipalId, + RoleDefinitionId = rgRaId, + }; + + infra.Add(rgRa); + + // Add Reader role assignment + var rgRaId2 = BicepFunction.GetSubscriptionResourceId( + "Microsoft.Authorization/roleDefinitions", + "acdd72a7-3385-48ef-bd42-f606fba81ae7"); + var rgRaName2 = BicepFunction.CreateGuid(BicepFunction.GetResourceGroup().Id, contributorIdentity.Id, rgRaId2); + + var rgRa2 = new RoleAssignment(Infrastructure.NormalizeBicepIdentifier($"{prefix}_ra2")) + { + Name = rgRaName2, + PrincipalType = RoleManagementPrincipalType.ServicePrincipal, + PrincipalId = contributorIdentity.PrincipalId, + RoleDefinitionId = rgRaId2 + }; + + infra.Add(rgRa2); + var webSite = new WebSite("webapp") { // Use the host name as the name of the web app @@ -53,8 +89,6 @@ public static WebSite AddDashboard(AzureResourceInfrastructure infra, } }; - //var acrMid = BicepFunction.Interpolate($"{acrMidParameter}").Compile().ToString(); - //webSite.Identity.UserAssignedIdentities[acrMid] = new UserAssignedIdentityDetails(); var contributorMid = BicepFunction.Interpolate($"{contributorMidParameter}").Compile().ToString(); webSite.Identity.UserAssignedIdentities[contributorMid] = new UserAssignedIdentityDetails(); diff --git a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceWebsiteContext.cs b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceWebsiteContext.cs index 616fb7b3a79..520d8538cd7 100644 --- a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceWebsiteContext.cs +++ b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceWebsiteContext.cs @@ -327,7 +327,10 @@ static FunctionCallExpression Join(BicepExpression args, string delimeter) => } #pragma warning restore ASPIREPROBES001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. - AddDashboardSettings(webSite, acrClientIdParameter, dashboardUri); + if (environmentContext.Environment.EnableDashboard) + { + AddDashboardSettings(webSite, acrClientIdParameter, dashboardUri); + } infra.Add(webSite); diff --git a/tests/Aspire.Hosting.Azure.Tests/AzureAppServiceTests.cs b/tests/Aspire.Hosting.Azure.Tests/AzureAppServiceTests.cs index 822ca33db8a..fde5844e72d 100644 --- a/tests/Aspire.Hosting.Azure.Tests/AzureAppServiceTests.cs +++ b/tests/Aspire.Hosting.Azure.Tests/AzureAppServiceTests.cs @@ -394,6 +394,62 @@ public async Task ResourceWithProbes() await Verify(projectBicep, "bicep"); } + [Fact] + public async Task AddAppServiceEnvironmentWithoutDashboardAddsEnvironmentResource() + { + var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish); + + builder.AddAzureAppServiceEnvironment("env").WithDashboard(false); + + using var app = builder.Build(); + + await ExecuteBeforeStartHooksAsync(app, default); + + var model = app.Services.GetRequiredService(); + + var environment = Assert.Single(model.Resources.OfType()); + + var (manifest, bicep) = await GetManifestWithBicep(environment); + + await Verify(manifest.ToString(), "json") + .AppendContentAsFile(bicep, "bicep"); + } + + [Fact] + public async Task AddAppServiceToEnvironmentWithoutDashboard() + { + var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish); + + builder.AddAzureAppServiceEnvironment("env").WithDashboard(false); + + // Add 2 projects with endpoints + var project1 = builder.AddProject("project1", launchProfileName: null) + .WithHttpEndpoint() + .WithExternalHttpEndpoints(); + + var project2 = builder.AddProject("project2", launchProfileName: null) + .WithHttpEndpoint() + .WithExternalHttpEndpoints() + .WithReference(project1); + + using var app = builder.Build(); + + await ExecuteBeforeStartHooksAsync(app, default); + + var model = app.Services.GetRequiredService(); + + project2.Resource.TryGetLastAnnotation(out var target); + + var resource = target?.DeploymentTarget as AzureProvisioningResource; + + Assert.NotNull(resource); + + var (manifest, bicep) = await GetManifestWithBicep(resource); + + 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/AzureAppServiceTests.AddAppServiceEnvironmentWithoutDashboardAddsEnvironmentResource.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddAppServiceEnvironmentWithoutDashboardAddsEnvironmentResource.verified.bicep new file mode 100644 index 00000000000..45604b8d43e --- /dev/null +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddAppServiceEnvironmentWithoutDashboardAddsEnvironmentResource.verified.bicep @@ -0,0 +1,61 @@ +@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@2024-11-30' = { + name: take('env_mi-${uniqueString(resourceGroup().id)}', 128) + location: location + tags: tags +} + +resource env_acr 'Microsoft.ContainerRegistry/registries@2025-04-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_asplan 'Microsoft.Web/serverfarms@2024-11-01' = { + name: take('envasplan-${uniqueString(resourceGroup().id)}', 60) + location: location + properties: { + perSiteScaling: true + reserved: true + } + kind: 'Linux' + sku: { + name: 'P0V3' + tier: 'Premium' + } +} + +output name string = env_asplan.name + +output planId string = env_asplan.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_REGISTRY_MANAGED_IDENTITY_CLIENT_ID string = env_mi.properties.clientId + +output AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_NAME string = env_mi.name + +output DASHBOARD_URI string = 'https://${take('${toLower('dashboard')}-${uniqueString(resourceGroup().id)}', 60)}.azurewebsites.net' diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddAppServiceEnvironmentWithoutDashboardAddsEnvironmentResource.verified.json b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddAppServiceEnvironmentWithoutDashboardAddsEnvironmentResource.verified.json new file mode 100644 index 00000000000..0bc8a391d67 --- /dev/null +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddAppServiceEnvironmentWithoutDashboardAddsEnvironmentResource.verified.json @@ -0,0 +1,7 @@ +{ + "type": "azure.bicep.v0", + "path": "env.module.bicep", + "params": { + "userPrincipalId": "" + } +} diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddAppServiceToEnvironmentWithoutDashboard.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddAppServiceToEnvironmentWithoutDashboard.verified.bicep new file mode 100644 index 00000000000..367ed315cab --- /dev/null +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddAppServiceToEnvironmentWithoutDashboard.verified.bicep @@ -0,0 +1,73 @@ +@description('The location for the resource(s) to be deployed.') +param location string = resourceGroup().location + +param env_outputs_azure_container_registry_endpoint string + +param env_outputs_planid string + +param env_outputs_azure_container_registry_managed_identity_id string + +param env_outputs_azure_container_registry_managed_identity_client_id string + +param project2_containerimage string + +param env_outputs_dashboard_uri string + +param project2_containerport string + +resource mainContainer 'Microsoft.Web/sites/sitecontainers@2024-11-01' = { + name: 'main' + properties: { + authType: 'UserAssigned' + image: project2_containerimage + isMain: true + userManagedIdentityClientId: env_outputs_azure_container_registry_managed_identity_client_id + } + parent: webapp +} + +resource webapp 'Microsoft.Web/sites@2024-11-01' = { + name: take('${toLower('project2')}-${uniqueString(resourceGroup().id)}', 60) + location: location + properties: { + serverFarmId: env_outputs_planid + siteConfig: { + numberOfWorkers: 30 + linuxFxVersion: 'SITECONTAINERS' + acrUseManagedIdentityCreds: true + acrUserManagedIdentityID: env_outputs_azure_container_registry_managed_identity_client_id + appSettings: [ + { + name: 'OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EXCEPTION_LOG_ATTRIBUTES' + value: 'true' + } + { + name: 'OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EVENT_LOG_ATTRIBUTES' + value: 'true' + } + { + name: 'OTEL_DOTNET_EXPERIMENTAL_OTLP_RETRY' + value: 'in_memory' + } + { + name: 'ASPNETCORE_FORWARDEDHEADERS_ENABLED' + value: 'true' + } + { + name: 'HTTP_PORTS' + value: project2_containerport + } + { + name: 'services__project1__http__0' + value: 'http://${take('${toLower('project1')}-${uniqueString(resourceGroup().id)}', 60)}.azurewebsites.net' + } + ] + } + } + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${env_outputs_azure_container_registry_managed_identity_id}': { } + } + } +} diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddAppServiceToEnvironmentWithoutDashboard.verified.json b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddAppServiceToEnvironmentWithoutDashboard.verified.json new file mode 100644 index 00000000000..c4f63ce4f70 --- /dev/null +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddAppServiceToEnvironmentWithoutDashboard.verified.json @@ -0,0 +1,13 @@ +{ + "type": "azure.bicep.v0", + "path": "project2.module.bicep", + "params": { + "env_outputs_azure_container_registry_endpoint": "{env.outputs.AZURE_CONTAINER_REGISTRY_ENDPOINT}", + "env_outputs_planid": "{env.outputs.planId}", + "env_outputs_azure_container_registry_managed_identity_id": "{env.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID}", + "env_outputs_azure_container_registry_managed_identity_client_id": "{env.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_CLIENT_ID}", + "project2_containerimage": "{project2.containerImage}", + "env_outputs_dashboard_uri": "{env.outputs.DASHBOARD_URI}", + "project2_containerport": "{project2.containerPort}" + } +} diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsEnvironmentResource.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsEnvironmentResource.verified.bicep index edbe543526c..fafb159310e 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsEnvironmentResource.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsEnvironmentResource.verified.bicep @@ -11,11 +11,6 @@ resource env_mi 'Microsoft.ManagedIdentity/userAssignedIdentities@2024-11-30' = tags: tags } -resource env_contributor_mi 'Microsoft.ManagedIdentity/userAssignedIdentities@2024-11-30' = { - name: take('env_contributor_mi-${uniqueString(resourceGroup().id)}', 128) - location: location -} - resource env_acr 'Microsoft.ContainerRegistry/registries@2025-04-01' = { name: take('envacr${uniqueString(resourceGroup().id)}', 50) location: location @@ -49,6 +44,11 @@ resource env_asplan 'Microsoft.Web/serverfarms@2024-11-01' = { } } +resource env_contributor_mi 'Microsoft.ManagedIdentity/userAssignedIdentities@2024-11-30' = { + name: take('env_contributor_mi-${uniqueString(resourceGroup().id)}', 128) + location: location +} + resource env_ra 'Microsoft.Authorization/roleAssignments@2022-04-01' = { name: guid(resourceGroup().id, env_contributor_mi.id, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772')) properties: { diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceEnvironmentCanReferenceExistingAppServicePlan.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceEnvironmentCanReferenceExistingAppServicePlan.verified.bicep index edbe543526c..fafb159310e 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceEnvironmentCanReferenceExistingAppServicePlan.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceEnvironmentCanReferenceExistingAppServicePlan.verified.bicep @@ -11,11 +11,6 @@ resource env_mi 'Microsoft.ManagedIdentity/userAssignedIdentities@2024-11-30' = tags: tags } -resource env_contributor_mi 'Microsoft.ManagedIdentity/userAssignedIdentities@2024-11-30' = { - name: take('env_contributor_mi-${uniqueString(resourceGroup().id)}', 128) - location: location -} - resource env_acr 'Microsoft.ContainerRegistry/registries@2025-04-01' = { name: take('envacr${uniqueString(resourceGroup().id)}', 50) location: location @@ -49,6 +44,11 @@ resource env_asplan 'Microsoft.Web/serverfarms@2024-11-01' = { } } +resource env_contributor_mi 'Microsoft.ManagedIdentity/userAssignedIdentities@2024-11-30' = { + name: take('env_contributor_mi-${uniqueString(resourceGroup().id)}', 128) + location: location +} + resource env_ra 'Microsoft.Authorization/roleAssignments@2022-04-01' = { name: guid(resourceGroup().id, env_contributor_mi.id, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772')) properties: { From a669727af6f2b3b64acceacc97d87024137cb112 Mon Sep 17 00:00:00 2001 From: Shilpi Rachna Date: Tue, 30 Sep 2025 05:44:13 -0700 Subject: [PATCH 06/17] Fixed failing unit tests --- ...mentAddsEnvironmentResource.verified.bicep | 4 +++ ...renceExistingAppServicePlan.verified.bicep | 4 +++ ...ts_AzureAppService_Works#00.verified.bicep | 31 +++++++++++++++++-- 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsEnvironmentResource.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsEnvironmentResource.verified.bicep index fafb159310e..ab75723c7c0 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsEnvironmentResource.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsEnvironmentResource.verified.bicep @@ -86,6 +86,10 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { name: 'Dashboard__Otlp__AuthMode' value: 'Unsecured' } + { + name: 'Dashboard__Otlp__SuppressUnsecuredTelemetryMessage' + value: 'true' + } { name: 'Dashboard__ResourceServiceClient__AuthMode' value: 'Unsecured' diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceEnvironmentCanReferenceExistingAppServicePlan.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceEnvironmentCanReferenceExistingAppServicePlan.verified.bicep index fafb159310e..ab75723c7c0 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceEnvironmentCanReferenceExistingAppServicePlan.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceEnvironmentCanReferenceExistingAppServicePlan.verified.bicep @@ -86,6 +86,10 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { name: 'Dashboard__Otlp__AuthMode' value: 'Unsecured' } + { + name: 'Dashboard__Otlp__SuppressUnsecuredTelemetryMessage' + value: 'true' + } { name: 'Dashboard__ResourceServiceClient__AuthMode' value: 'Unsecured' diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureUserAssignedIdentityTests.WithAzureUserAssignedIdentity_WithRoleAssignments_AzureAppService_Works#00.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureUserAssignedIdentityTests.WithAzureUserAssignedIdentity_WithRoleAssignments_AzureAppService_Works#00.verified.bicep index f1e8fdd1b5a..2d670f58f78 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureUserAssignedIdentityTests.WithAzureUserAssignedIdentity_WithRoleAssignments_AzureAppService_Works#00.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureUserAssignedIdentityTests.WithAzureUserAssignedIdentity_WithRoleAssignments_AzureAppService_Works#00.verified.bicep @@ -1,4 +1,4 @@ -@description('The location for the resource(s) to be deployed.') +@description('The location for the resource(s) to be deployed.') param location string = resourceGroup().location param appservice_outputs_azure_container_registry_endpoint string @@ -11,6 +11,8 @@ param appservice_outputs_azure_container_registry_managed_identity_client_id str param myapp_containerimage string +param appservice_outputs_dashboard_uri string + param myidentity_outputs_id string param myidentity_outputs_clientid string @@ -33,6 +35,7 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { serverFarmId: appservice_outputs_planid keyVaultReferenceIdentity: myidentity_outputs_id siteConfig: { + numberOfWorkers: 30 linuxFxVersion: 'SITECONTAINERS' acrUseManagedIdentityCreds: true acrUserManagedIdentityID: appservice_outputs_azure_container_registry_managed_identity_client_id @@ -53,6 +56,30 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { name: 'AZURE_CLIENT_ID' value: myidentity_outputs_clientid } + { + name: 'OTEL_SERVICE_NAME' + value: 'myapp' + } + { + name: 'OTEL_EXPORTER_OTLP_PROTOCOL' + value: 'grpc' + } + { + name: 'OTEL_EXPORTER_OTLP_ENDPOINT' + value: 'http://localhost:6001' + } + { + name: 'WEBSITE_ENABLE_ASPIRE_OTEL_SIDECAR' + value: 'true' + } + { + name: 'OTEL_COLLECTOR_URL' + value: appservice_outputs_dashboard_uri + } + { + name: 'OTEL_CLIENT_ID' + value: appservice_outputs_azure_container_registry_managed_identity_client_id + } ] } } @@ -63,4 +90,4 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { '${myidentity_outputs_id}': { } } } -} \ No newline at end of file +} From a554e7724e0dce679b7856e8e32451c3771fef47 Mon Sep 17 00:00:00 2001 From: Shilpi Rachna Date: Fri, 3 Oct 2025 05:03:43 -0700 Subject: [PATCH 07/17] Made parameter dashboardUri conditional in web app bicep templates --- .../AzureAppServiceWebsiteContext.cs | 4 ++-- ...AppServiceToEnvironmentWithoutDashboard.verified.bicep | 2 -- ...dAppServiceToEnvironmentWithoutDashboard.verified.json | 1 - ...argetWithContainerAppToProjectResources.verified.bicep | 4 ++-- ...TargetWithContainerAppToProjectResources.verified.json | 4 ++-- ...ointReferencesAreResolvedAcrossProjects.verified.bicep | 4 ++-- ...pointReferencesAreResolvedAcrossProjects.verified.json | 4 ++-- ...pServiceTests.KeyvaultReferenceHandling.verified.bicep | 4 ++-- ...ppServiceTests.KeyvaultReferenceHandling.verified.json | 4 ++-- ...ipleAzureAppServiceEnvironmentsSupported.verified.json | 8 ++++---- ...AzureAppServiceTests.ResourceWithProbes.verified.bicep | 4 ++-- ...nProviderIsHandledWithAllocateParameter.verified.bicep | 4 ++-- ...onProviderIsHandledWithAllocateParameter.verified.json | 4 ++-- ...oleAssignments_AzureAppService_Works#00.verified.bicep | 4 ++-- 14 files changed, 26 insertions(+), 29 deletions(-) diff --git a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceWebsiteContext.cs b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceWebsiteContext.cs index d617687054d..24930cbdfe1 100644 --- a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceWebsiteContext.cs +++ b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceWebsiteContext.cs @@ -212,8 +212,7 @@ public void BuildWebSite(AzureResourceInfrastructure infra) var acrMidParameter = environmentContext.Environment.ContainerRegistryManagedIdentityId.AsProvisioningParameter(infra); var acrClientIdParameter = environmentContext.Environment.ContainerRegistryClientId.AsProvisioningParameter(infra); var containerImage = AllocateParameter(new ContainerImageReference(Resource)); - var dashboardUri = environmentContext.Environment.DashboardUriReference.AsProvisioningParameter(infra); - + var webSite = new WebSite("webapp") { // Use the host name as the name of the web app @@ -329,6 +328,7 @@ static FunctionCallExpression Join(BicepExpression args, string delimeter) => if (environmentContext.Environment.EnableDashboard) { + var dashboardUri = environmentContext.Environment.DashboardUriReference.AsProvisioningParameter(infra); AddDashboardSettings(webSite, acrClientIdParameter, dashboardUri); } diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddAppServiceToEnvironmentWithoutDashboard.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddAppServiceToEnvironmentWithoutDashboard.verified.bicep index 367ed315cab..61b352b2f82 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddAppServiceToEnvironmentWithoutDashboard.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddAppServiceToEnvironmentWithoutDashboard.verified.bicep @@ -11,8 +11,6 @@ param env_outputs_azure_container_registry_managed_identity_client_id string param project2_containerimage string -param env_outputs_dashboard_uri string - param project2_containerport string resource mainContainer 'Microsoft.Web/sites/sitecontainers@2024-11-01' = { diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddAppServiceToEnvironmentWithoutDashboard.verified.json b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddAppServiceToEnvironmentWithoutDashboard.verified.json index c4f63ce4f70..4e0562d0eff 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddAppServiceToEnvironmentWithoutDashboard.verified.json +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddAppServiceToEnvironmentWithoutDashboard.verified.json @@ -7,7 +7,6 @@ "env_outputs_azure_container_registry_managed_identity_id": "{env.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID}", "env_outputs_azure_container_registry_managed_identity_client_id": "{env.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_CLIENT_ID}", "project2_containerimage": "{project2.containerImage}", - "env_outputs_dashboard_uri": "{env.outputs.DASHBOARD_URI}", "project2_containerport": "{project2.containerPort}" } } diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsDeploymentTargetWithContainerAppToProjectResources.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsDeploymentTargetWithContainerAppToProjectResources.verified.bicep index 0748b06814d..673a481c6dd 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsDeploymentTargetWithContainerAppToProjectResources.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsDeploymentTargetWithContainerAppToProjectResources.verified.bicep @@ -11,10 +11,10 @@ param env_outputs_azure_container_registry_managed_identity_client_id string param api_containerimage string -param env_outputs_dashboard_uri string - param api_containerport string +param env_outputs_dashboard_uri string + resource mainContainer 'Microsoft.Web/sites/sitecontainers@2024-11-01' = { name: 'main' properties: { diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsDeploymentTargetWithContainerAppToProjectResources.verified.json b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsDeploymentTargetWithContainerAppToProjectResources.verified.json index 22e6a4cb8b1..5107f447540 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsDeploymentTargetWithContainerAppToProjectResources.verified.json +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsDeploymentTargetWithContainerAppToProjectResources.verified.json @@ -7,7 +7,7 @@ "env_outputs_azure_container_registry_managed_identity_id": "{env.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID}", "env_outputs_azure_container_registry_managed_identity_client_id": "{env.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_CLIENT_ID}", "api_containerimage": "{api.containerImage}", - "env_outputs_dashboard_uri": "{env.outputs.DASHBOARD_URI}", - "api_containerport": "{api.containerPort}" + "api_containerport": "{api.containerPort}", + "env_outputs_dashboard_uri": "{env.outputs.DASHBOARD_URI}" } } diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.EndpointReferencesAreResolvedAcrossProjects.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.EndpointReferencesAreResolvedAcrossProjects.verified.bicep index 7d7106ac072..7a4fea8a375 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.EndpointReferencesAreResolvedAcrossProjects.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.EndpointReferencesAreResolvedAcrossProjects.verified.bicep @@ -11,10 +11,10 @@ param env_outputs_azure_container_registry_managed_identity_client_id string param project2_containerimage string -param env_outputs_dashboard_uri string - param project2_containerport string +param env_outputs_dashboard_uri string + resource mainContainer 'Microsoft.Web/sites/sitecontainers@2024-11-01' = { name: 'main' properties: { diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.EndpointReferencesAreResolvedAcrossProjects.verified.json b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.EndpointReferencesAreResolvedAcrossProjects.verified.json index c4f63ce4f70..28c40fd1b73 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.EndpointReferencesAreResolvedAcrossProjects.verified.json +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.EndpointReferencesAreResolvedAcrossProjects.verified.json @@ -7,7 +7,7 @@ "env_outputs_azure_container_registry_managed_identity_id": "{env.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID}", "env_outputs_azure_container_registry_managed_identity_client_id": "{env.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_CLIENT_ID}", "project2_containerimage": "{project2.containerImage}", - "env_outputs_dashboard_uri": "{env.outputs.DASHBOARD_URI}", - "project2_containerport": "{project2.containerPort}" + "project2_containerport": "{project2.containerPort}", + "env_outputs_dashboard_uri": "{env.outputs.DASHBOARD_URI}" } } diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.KeyvaultReferenceHandling.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.KeyvaultReferenceHandling.verified.bicep index cb77709d867..79f4b1c642c 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.KeyvaultReferenceHandling.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.KeyvaultReferenceHandling.verified.bicep @@ -11,8 +11,6 @@ param env_outputs_azure_container_registry_managed_identity_client_id string param api_containerimage string -param env_outputs_dashboard_uri string - param mydb_kv_outputs_name string param kvName string @@ -23,6 +21,8 @@ param api_identity_outputs_id string param api_identity_outputs_clientid string +param env_outputs_dashboard_uri string + resource mainContainer 'Microsoft.Web/sites/sitecontainers@2024-11-01' = { name: 'main' properties: { diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.KeyvaultReferenceHandling.verified.json b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.KeyvaultReferenceHandling.verified.json index ac0ae225a11..b6bed8a024e 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.KeyvaultReferenceHandling.verified.json +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.KeyvaultReferenceHandling.verified.json @@ -7,11 +7,11 @@ "env_outputs_azure_container_registry_managed_identity_id": "{env.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID}", "env_outputs_azure_container_registry_managed_identity_client_id": "{env.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_CLIENT_ID}", "api_containerimage": "{api.containerImage}", - "env_outputs_dashboard_uri": "{env.outputs.DASHBOARD_URI}", "mydb_kv_outputs_name": "{mydb-kv.outputs.name}", "kvName": "{kvName.value}", "sharedRg": "{sharedRg.value}", "api_identity_outputs_id": "{api-identity.outputs.id}", - "api_identity_outputs_clientid": "{api-identity.outputs.clientId}" + "api_identity_outputs_clientid": "{api-identity.outputs.clientId}", + "env_outputs_dashboard_uri": "{env.outputs.DASHBOARD_URI}" } } diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.MultipleAzureAppServiceEnvironmentsSupported.verified.json b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.MultipleAzureAppServiceEnvironmentsSupported.verified.json index 894bf24503d..2febd42ae96 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.MultipleAzureAppServiceEnvironmentsSupported.verified.json +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.MultipleAzureAppServiceEnvironmentsSupported.verified.json @@ -26,8 +26,8 @@ "env1_outputs_azure_container_registry_managed_identity_id": "{env1.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID}", "env1_outputs_azure_container_registry_managed_identity_client_id": "{env1.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_CLIENT_ID}", "servicea_containerimage": "{ServiceA.containerImage}", - "env1_outputs_dashboard_uri": "{env1.outputs.DASHBOARD_URI}", - "servicea_containerport": "{ServiceA.containerPort}" + "servicea_containerport": "{ServiceA.containerPort}", + "env1_outputs_dashboard_uri": "{env1.outputs.DASHBOARD_URI}" } }, "env": { @@ -63,8 +63,8 @@ "env2_outputs_azure_container_registry_managed_identity_id": "{env2.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID}", "env2_outputs_azure_container_registry_managed_identity_client_id": "{env2.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_CLIENT_ID}", "serviceb_containerimage": "{ServiceB.containerImage}", - "env2_outputs_dashboard_uri": "{env2.outputs.DASHBOARD_URI}", - "serviceb_containerport": "{ServiceB.containerPort}" + "serviceb_containerport": "{ServiceB.containerPort}", + "env2_outputs_dashboard_uri": "{env2.outputs.DASHBOARD_URI}" } }, "env": { diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.ResourceWithProbes.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.ResourceWithProbes.verified.bicep index 7bd7ccb2f68..229dd11525e 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.ResourceWithProbes.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.ResourceWithProbes.verified.bicep @@ -11,10 +11,10 @@ param env_outputs_azure_container_registry_managed_identity_client_id string param project1_containerimage string -param env_outputs_dashboard_uri string - param project1_containerport string +param env_outputs_dashboard_uri string + resource mainContainer 'Microsoft.Web/sites/sitecontainers@2024-11-01' = { name: 'main' properties: { diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.UnknownManifestExpressionProviderIsHandledWithAllocateParameter.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.UnknownManifestExpressionProviderIsHandledWithAllocateParameter.verified.bicep index f944e72fb16..47baf889bdb 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.UnknownManifestExpressionProviderIsHandledWithAllocateParameter.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.UnknownManifestExpressionProviderIsHandledWithAllocateParameter.verified.bicep @@ -11,12 +11,12 @@ param env_outputs_azure_container_registry_managed_identity_client_id string param api_containerimage string -param env_outputs_dashboard_uri string - param api_containerport string param customvalue string +param env_outputs_dashboard_uri string + resource mainContainer 'Microsoft.Web/sites/sitecontainers@2024-11-01' = { name: 'main' properties: { diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.UnknownManifestExpressionProviderIsHandledWithAllocateParameter.verified.json b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.UnknownManifestExpressionProviderIsHandledWithAllocateParameter.verified.json index 190d384115d..13afb878faa 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.UnknownManifestExpressionProviderIsHandledWithAllocateParameter.verified.json +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.UnknownManifestExpressionProviderIsHandledWithAllocateParameter.verified.json @@ -7,8 +7,8 @@ "env_outputs_azure_container_registry_managed_identity_id": "{env.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID}", "env_outputs_azure_container_registry_managed_identity_client_id": "{env.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_CLIENT_ID}", "api_containerimage": "{api.containerImage}", - "env_outputs_dashboard_uri": "{env.outputs.DASHBOARD_URI}", "api_containerport": "{api.containerPort}", - "customvalue": "{customValue}" + "customvalue": "{customValue}", + "env_outputs_dashboard_uri": "{env.outputs.DASHBOARD_URI}" } } diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureUserAssignedIdentityTests.WithAzureUserAssignedIdentity_WithRoleAssignments_AzureAppService_Works#00.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureUserAssignedIdentityTests.WithAzureUserAssignedIdentity_WithRoleAssignments_AzureAppService_Works#00.verified.bicep index 2d670f58f78..6f1ea13f420 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureUserAssignedIdentityTests.WithAzureUserAssignedIdentity_WithRoleAssignments_AzureAppService_Works#00.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureUserAssignedIdentityTests.WithAzureUserAssignedIdentity_WithRoleAssignments_AzureAppService_Works#00.verified.bicep @@ -11,12 +11,12 @@ param appservice_outputs_azure_container_registry_managed_identity_client_id str param myapp_containerimage string -param appservice_outputs_dashboard_uri string - param myidentity_outputs_id string param myidentity_outputs_clientid string +param appservice_outputs_dashboard_uri string + resource mainContainer 'Microsoft.Web/sites/sitecontainers@2024-11-01' = { name: 'main' properties: { From d1eac7c4b4ac8d971e96c9c4b1923ac4457f5073 Mon Sep 17 00:00:00 2001 From: Shilpi Rachna Date: Fri, 3 Oct 2025 05:32:19 -0700 Subject: [PATCH 08/17] Publishing DashboardUri paramater only when dashboard is enabled --- .../AzureAppServiceEnvironmentExtensions.cs | 18 +++++++++--------- ...boardAddsEnvironmentResource.verified.bicep | 2 -- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentExtensions.cs b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentExtensions.cs index 6be91cde785..d61b8689ef3 100644 --- a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentExtensions.cs +++ b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentExtensions.cs @@ -113,12 +113,6 @@ public static IResourceBuilder AddAzureAppSe Value = plan.Id }); - if (resource.EnableDashboard) - { - // Add aspire dashboard website - var website = AzureAppServiceEnvironmentUtility.AddDashboard(infra, identity, plan.Id); - } - infra.Add(new ProvisioningOutput("AZURE_CONTAINER_REGISTRY_NAME", typeof(string)) { Value = containerRegistry.Name @@ -145,10 +139,16 @@ public static IResourceBuilder AddAzureAppSe Value = identity.Name }); - infra.Add(new ProvisioningOutput("DASHBOARD_URI", typeof(string)) + if (resource.EnableDashboard) { - Value = BicepFunction.Interpolate($"https://{AzureAppServiceEnvironmentUtility.DashboardHostName}.azurewebsites.net") - }); + // Add aspire dashboard website + var website = AzureAppServiceEnvironmentUtility.AddDashboard(infra, identity, plan.Id); + + infra.Add(new ProvisioningOutput("DASHBOARD_URI", typeof(string)) + { + Value = BicepFunction.Interpolate($"https://{AzureAppServiceEnvironmentUtility.DashboardHostName}.azurewebsites.net") + }); + } }); if (!builder.ExecutionContext.IsPublishMode) diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddAppServiceEnvironmentWithoutDashboardAddsEnvironmentResource.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddAppServiceEnvironmentWithoutDashboardAddsEnvironmentResource.verified.bicep index 45604b8d43e..eb4d1810e8b 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddAppServiceEnvironmentWithoutDashboardAddsEnvironmentResource.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddAppServiceEnvironmentWithoutDashboardAddsEnvironmentResource.verified.bicep @@ -57,5 +57,3 @@ output AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID string = env_mi.id output AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_CLIENT_ID string = env_mi.properties.clientId output AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_NAME string = env_mi.name - -output DASHBOARD_URI string = 'https://${take('${toLower('dashboard')}-${uniqueString(resourceGroup().id)}', 60)}.azurewebsites.net' From df38b1c96f98db0ab3a1d59885af0cb16dd34c37 Mon Sep 17 00:00:00 2001 From: Shilpi Rachna Date: Fri, 3 Oct 2025 10:57:33 -0700 Subject: [PATCH 09/17] Update src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentUtility.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../AzureAppServiceEnvironmentUtility.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentUtility.cs b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentUtility.cs index 64bd6d2fc5a..8979f556cb0 100644 --- a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentUtility.cs +++ b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentUtility.cs @@ -13,7 +13,7 @@ namespace Aspire.Hosting.Azure.AppService; internal static class AzureAppServiceEnvironmentUtility { - const string ResourceName = "dashboard"; + internal static readonly string ResourceName = "dashboard"; public static BicepValue DashboardHostName => BicepFunction.Take( BicepFunction.Interpolate($"{BicepFunction.ToLower(ResourceName)}-{BicepFunction.GetUniqueString(BicepFunction.GetResourceGroup().Id)}"), 60); From 26793ea1ca09972b3e0a471f92a47c8b879dcaec Mon Sep 17 00:00:00 2001 From: Shilpi Rachna Date: Fri, 3 Oct 2025 10:57:56 -0700 Subject: [PATCH 10/17] Update src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentUtility.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../AzureAppServiceEnvironmentUtility.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentUtility.cs b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentUtility.cs index 8979f556cb0..4a5e22d076e 100644 --- a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentUtility.cs +++ b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentUtility.cs @@ -76,7 +76,7 @@ public static WebSite AddDashboard(AzureResourceInfrastructure infra, UseManagedIdentityCreds = true, IsHttp20Enabled = true, Http20ProxyFlag = 1, - // Setting NumberOfWorkers to 1 to ensure dashboard runs of 1 instance + // Setting NumberOfWorkers to 1 to ensure dashboard runs on 1 instance NumberOfWorkers = 1, // IsAlwaysOn set to true ensures the app is always running IsAlwaysOn = true, From ae6fec293bab5fc4d9369fd40ea5fa7934c6f033 Mon Sep 17 00:00:00 2001 From: Shilpi Rachna Date: Fri, 3 Oct 2025 12:02:28 -0700 Subject: [PATCH 11/17] Handled PR feedback and added support to log dashboard Uri --- .../AzureAppServiceEnvironmentExtensions.cs | 7 +------ .../AzureAppServiceEnvironmentResource.cs | 2 +- .../AzureAppServiceEnvironmentUtility.cs | 20 +++++++++---------- .../AzureDeployingContext.cs | 6 ++++++ ...oardAddsEnvironmentResource.verified.bicep | 2 -- ...tainerAppToProjectResources.verified.bicep | 4 ++-- ...ntainerAppToProjectResources.verified.json | 2 +- ...mentAddsEnvironmentResource.verified.bicep | 4 +--- ...ServiceToContainerResources.verified.bicep | 4 ++-- ...pServiceToContainerResources.verified.json | 2 +- ...renceExistingAppServicePlan.verified.bicep | 4 +--- ...pportBaitAndSwitchResources.verified.bicep | 4 ++-- ...upportBaitAndSwitchResources.verified.json | 2 +- ...esAreResolvedAcrossProjects.verified.bicep | 4 ++-- ...cesAreResolvedAcrossProjects.verified.json | 2 +- ...s.KeyvaultReferenceHandling.verified.bicep | 4 ++-- ...ts.KeyvaultReferenceHandling.verified.json | 2 +- ...ServiceEnvironmentsSupported.verified.json | 4 ++-- ...iceTests.ResourceWithProbes.verified.bicep | 4 ++-- ...andledWithAllocateParameter.verified.bicep | 4 ++-- ...HandledWithAllocateParameter.verified.json | 2 +- ...ts_AzureAppService_Works#00.verified.bicep | 4 ++-- 22 files changed, 44 insertions(+), 49 deletions(-) diff --git a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentExtensions.cs b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentExtensions.cs index d61b8689ef3..c74626ade56 100644 --- a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentExtensions.cs +++ b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentExtensions.cs @@ -134,17 +134,12 @@ public static IResourceBuilder AddAzureAppSe Value = identity.ClientId }); - infra.Add(new ProvisioningOutput("AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_NAME", typeof(string)) - { - Value = identity.Name - }); - if (resource.EnableDashboard) { // Add aspire dashboard website var website = AzureAppServiceEnvironmentUtility.AddDashboard(infra, identity, plan.Id); - infra.Add(new ProvisioningOutput("DASHBOARD_URI", typeof(string)) + infra.Add(new ProvisioningOutput("AZURE_APP_SERVICE_DASHBOARD_URI", typeof(string)) { Value = BicepFunction.Interpolate($"https://{AzureAppServiceEnvironmentUtility.DashboardHostName}.azurewebsites.net") }); diff --git a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentResource.cs b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentResource.cs index 611f8300fe0..c3a16ab07e3 100644 --- a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentResource.cs +++ b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentResource.cs @@ -41,7 +41,7 @@ public class AzureAppServiceEnvironmentResource(string name, Action /// Gets the URI of the App Service Environment dashboard. /// - public BicepOutputReference DashboardUriReference => new("DASHBOARD_URI", this); + public BicepOutputReference DashboardUriReference => new("AZURE_APP_SERVICE_DASHBOARD_URI", this); ReferenceExpression IAzureContainerRegistry.ManagedIdentityId => ReferenceExpression.Create($"{ContainerRegistryManagedIdentityId}"); diff --git a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentUtility.cs b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentUtility.cs index 4a5e22d076e..5ba98989c5b 100644 --- a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentUtility.cs +++ b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentUtility.cs @@ -13,7 +13,7 @@ namespace Aspire.Hosting.Azure.AppService; internal static class AzureAppServiceEnvironmentUtility { - internal static readonly string ResourceName = "dashboard"; + internal const string ResourceName = "dashboard"; public static BicepValue DashboardHostName => BicepFunction.Take( BicepFunction.Interpolate($"{BicepFunction.ToLower(ResourceName)}-{BicepFunction.GetUniqueString(BicepFunction.GetResourceGroup().Id)}"), 60); @@ -22,13 +22,13 @@ public static WebSite AddDashboard(AzureResourceInfrastructure infra, UserAssignedIdentity otelIdentity, BicepValue appServicePlanId) { - var acrClientIdParameter = otelIdentity.ClientId; + // This ACR identity is used by the dashboard to authorize the telemetry data + // coming from the dotnet web apps. This identity is being assigned to every web app + // in the aspire project and can be safely reused for authorization in the dashboard. + var otelClientId = otelIdentity.ClientId; var prefix = infra.AspireResource.Name; var contributorIdentity = new UserAssignedIdentity(Infrastructure.NormalizeBicepIdentifier($"{prefix}-contributor-mi")); - - var contributorMidParameter = contributorIdentity.Id; - var contributorClientIdParameter = contributorIdentity.ClientId; - + infra.Add(contributorIdentity); // Add Website Contributor role assignment @@ -72,7 +72,7 @@ public static WebSite AddDashboard(AzureResourceInfrastructure infra, SiteConfig = new SiteConfigProperties() { LinuxFxVersion = "ASPIREDASHBOARD|1.0", - AcrUserManagedIdentityId = acrClientIdParameter, + AcrUserManagedIdentityId = otelClientId, UseManagedIdentityCreds = true, IsHttp20Enabled = true, Http20ProxyFlag = 1, @@ -89,7 +89,7 @@ public static WebSite AddDashboard(AzureResourceInfrastructure infra, } }; - var contributorMid = BicepFunction.Interpolate($"{contributorMidParameter}").Compile().ToString(); + var contributorMid = BicepFunction.Interpolate($"{contributorIdentity.Id}").Compile().ToString(); webSite.Identity.UserAssignedIdentities[contributorMid] = new UserAssignedIdentityDetails(); // Security is handled by app service platform @@ -103,8 +103,8 @@ public static WebSite AddDashboard(AzureResourceInfrastructure infra, // Enable SCM preloading to ensure dashboard is always available webSite.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "WEBSITE_START_SCM_WITH_PRELOAD", Value = "true" }); // Appsettings related to managed identity for auth - webSite.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "AZURE_CLIENT_ID", Value = contributorClientIdParameter }); - webSite.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "ALLOWED_MANAGED_IDENTITIES", Value = acrClientIdParameter }); + webSite.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "AZURE_CLIENT_ID", Value = contributorIdentity.ClientId }); + webSite.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "ALLOWED_MANAGED_IDENTITIES", Value = otelClientId }); infra.Add(webSite); return webSite; diff --git a/src/Aspire.Hosting.Azure/AzureDeployingContext.cs b/src/Aspire.Hosting.Azure/AzureDeployingContext.cs index 8a4b52c268c..f0322f674f2 100644 --- a/src/Aspire.Hosting.Azure/AzureDeployingContext.cs +++ b/src/Aspire.Hosting.Azure/AzureDeployingContext.cs @@ -482,6 +482,12 @@ private static string TryGetComputeResourceEndpoint(IResource computeResource, I { return $"https://aspire-dashboard.ext.{domainValue}"; } + // If the resource is a compute environment (app service), we can use its properties + // to get the dashboard URL. + if (environmentBicepResource.Outputs.TryGetValue($"AZURE_APP_SERVICE_DASHBOARD_URI", out var dashboardUri)) + { + return (string?)dashboardUri; + } } } diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddAppServiceEnvironmentWithoutDashboardAddsEnvironmentResource.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddAppServiceEnvironmentWithoutDashboardAddsEnvironmentResource.verified.bicep index eb4d1810e8b..86d0ea42ffd 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddAppServiceEnvironmentWithoutDashboardAddsEnvironmentResource.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddAppServiceEnvironmentWithoutDashboardAddsEnvironmentResource.verified.bicep @@ -55,5 +55,3 @@ output AZURE_CONTAINER_REGISTRY_ENDPOINT string = env_acr.properties.loginServer output AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID string = env_mi.id output AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_CLIENT_ID string = env_mi.properties.clientId - -output AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_NAME string = env_mi.name diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsDeploymentTargetWithContainerAppToProjectResources.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsDeploymentTargetWithContainerAppToProjectResources.verified.bicep index 673a481c6dd..354023a8a31 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsDeploymentTargetWithContainerAppToProjectResources.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsDeploymentTargetWithContainerAppToProjectResources.verified.bicep @@ -13,7 +13,7 @@ param api_containerimage string param api_containerport string -param env_outputs_dashboard_uri string +param env_outputs_azure_app_service_dashboard_uri string resource mainContainer 'Microsoft.Web/sites/sitecontainers@2024-11-01' = { name: 'main' @@ -75,7 +75,7 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { } { name: 'OTEL_COLLECTOR_URL' - value: env_outputs_dashboard_uri + value: env_outputs_azure_app_service_dashboard_uri } { name: 'OTEL_CLIENT_ID' diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsDeploymentTargetWithContainerAppToProjectResources.verified.json b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsDeploymentTargetWithContainerAppToProjectResources.verified.json index 5107f447540..7523f4c26ac 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsDeploymentTargetWithContainerAppToProjectResources.verified.json +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsDeploymentTargetWithContainerAppToProjectResources.verified.json @@ -8,6 +8,6 @@ "env_outputs_azure_container_registry_managed_identity_client_id": "{env.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_CLIENT_ID}", "api_containerimage": "{api.containerImage}", "api_containerport": "{api.containerPort}", - "env_outputs_dashboard_uri": "{env.outputs.DASHBOARD_URI}" + "env_outputs_azure_app_service_dashboard_uri": "{env.outputs.AZURE_APP_SERVICE_DASHBOARD_URI}" } } diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsEnvironmentResource.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsEnvironmentResource.verified.bicep index ab75723c7c0..9637cab232b 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsEnvironmentResource.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsEnvironmentResource.verified.bicep @@ -141,6 +141,4 @@ output AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID string = env_mi.id output AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_CLIENT_ID string = env_mi.properties.clientId -output AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_NAME string = env_mi.name - -output DASHBOARD_URI string = 'https://${take('${toLower('dashboard')}-${uniqueString(resourceGroup().id)}', 60)}.azurewebsites.net' +output AZURE_APP_SERVICE_DASHBOARD_URI string = 'https://${take('${toLower('dashboard')}-${uniqueString(resourceGroup().id)}', 60)}.azurewebsites.net' diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddDockerfileWithAppServiceInfrastructureAddsDeploymentTargetWithAppServiceToContainerResources.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddDockerfileWithAppServiceInfrastructureAddsDeploymentTargetWithAppServiceToContainerResources.verified.bicep index cf1c94ef78d..382c66431f2 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddDockerfileWithAppServiceInfrastructureAddsDeploymentTargetWithAppServiceToContainerResources.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddDockerfileWithAppServiceInfrastructureAddsDeploymentTargetWithAppServiceToContainerResources.verified.bicep @@ -11,7 +11,7 @@ param env_outputs_azure_container_registry_managed_identity_client_id string param api_containerimage string -param env_outputs_dashboard_uri string +param env_outputs_azure_app_service_dashboard_uri string resource mainContainer 'Microsoft.Web/sites/sitecontainers@2024-11-01' = { name: 'main' @@ -57,7 +57,7 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { } { name: 'OTEL_COLLECTOR_URL' - value: env_outputs_dashboard_uri + value: env_outputs_azure_app_service_dashboard_uri } { name: 'OTEL_CLIENT_ID' diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddDockerfileWithAppServiceInfrastructureAddsDeploymentTargetWithAppServiceToContainerResources.verified.json b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddDockerfileWithAppServiceInfrastructureAddsDeploymentTargetWithAppServiceToContainerResources.verified.json index 429d17cf3a1..f8ba7a2f06c 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddDockerfileWithAppServiceInfrastructureAddsDeploymentTargetWithAppServiceToContainerResources.verified.json +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddDockerfileWithAppServiceInfrastructureAddsDeploymentTargetWithAppServiceToContainerResources.verified.json @@ -7,6 +7,6 @@ "env_outputs_azure_container_registry_managed_identity_id": "{env.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID}", "env_outputs_azure_container_registry_managed_identity_client_id": "{env.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_CLIENT_ID}", "api_containerimage": "{api.containerImage}", - "env_outputs_dashboard_uri": "{env.outputs.DASHBOARD_URI}" + "env_outputs_azure_app_service_dashboard_uri": "{env.outputs.AZURE_APP_SERVICE_DASHBOARD_URI}" } } diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceEnvironmentCanReferenceExistingAppServicePlan.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceEnvironmentCanReferenceExistingAppServicePlan.verified.bicep index ab75723c7c0..9637cab232b 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceEnvironmentCanReferenceExistingAppServicePlan.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceEnvironmentCanReferenceExistingAppServicePlan.verified.bicep @@ -141,6 +141,4 @@ output AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID string = env_mi.id output AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_CLIENT_ID string = env_mi.properties.clientId -output AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_NAME string = env_mi.name - -output DASHBOARD_URI string = 'https://${take('${toLower('dashboard')}-${uniqueString(resourceGroup().id)}', 60)}.azurewebsites.net' +output AZURE_APP_SERVICE_DASHBOARD_URI string = 'https://${take('${toLower('dashboard')}-${uniqueString(resourceGroup().id)}', 60)}.azurewebsites.net' diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceSupportBaitAndSwitchResources.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceSupportBaitAndSwitchResources.verified.bicep index 6d914146902..3d318e7d3af 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceSupportBaitAndSwitchResources.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceSupportBaitAndSwitchResources.verified.bicep @@ -11,7 +11,7 @@ param env_outputs_azure_container_registry_managed_identity_client_id string param api_containerimage string -param env_outputs_dashboard_uri string +param env_outputs_azure_app_service_dashboard_uri string resource mainContainer 'Microsoft.Web/sites/sitecontainers@2024-11-01' = { name: 'main' @@ -73,7 +73,7 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { } { name: 'OTEL_COLLECTOR_URL' - value: env_outputs_dashboard_uri + value: env_outputs_azure_app_service_dashboard_uri } { name: 'OTEL_CLIENT_ID' diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceSupportBaitAndSwitchResources.verified.json b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceSupportBaitAndSwitchResources.verified.json index 429d17cf3a1..f8ba7a2f06c 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceSupportBaitAndSwitchResources.verified.json +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceSupportBaitAndSwitchResources.verified.json @@ -7,6 +7,6 @@ "env_outputs_azure_container_registry_managed_identity_id": "{env.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID}", "env_outputs_azure_container_registry_managed_identity_client_id": "{env.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_CLIENT_ID}", "api_containerimage": "{api.containerImage}", - "env_outputs_dashboard_uri": "{env.outputs.DASHBOARD_URI}" + "env_outputs_azure_app_service_dashboard_uri": "{env.outputs.AZURE_APP_SERVICE_DASHBOARD_URI}" } } diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.EndpointReferencesAreResolvedAcrossProjects.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.EndpointReferencesAreResolvedAcrossProjects.verified.bicep index 7a4fea8a375..938368410cf 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.EndpointReferencesAreResolvedAcrossProjects.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.EndpointReferencesAreResolvedAcrossProjects.verified.bicep @@ -13,7 +13,7 @@ param project2_containerimage string param project2_containerport string -param env_outputs_dashboard_uri string +param env_outputs_azure_app_service_dashboard_uri string resource mainContainer 'Microsoft.Web/sites/sitecontainers@2024-11-01' = { name: 'main' @@ -79,7 +79,7 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { } { name: 'OTEL_COLLECTOR_URL' - value: env_outputs_dashboard_uri + value: env_outputs_azure_app_service_dashboard_uri } { name: 'OTEL_CLIENT_ID' diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.EndpointReferencesAreResolvedAcrossProjects.verified.json b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.EndpointReferencesAreResolvedAcrossProjects.verified.json index 28c40fd1b73..eeda43c4e49 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.EndpointReferencesAreResolvedAcrossProjects.verified.json +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.EndpointReferencesAreResolvedAcrossProjects.verified.json @@ -8,6 +8,6 @@ "env_outputs_azure_container_registry_managed_identity_client_id": "{env.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_CLIENT_ID}", "project2_containerimage": "{project2.containerImage}", "project2_containerport": "{project2.containerPort}", - "env_outputs_dashboard_uri": "{env.outputs.DASHBOARD_URI}" + "env_outputs_azure_app_service_dashboard_uri": "{env.outputs.AZURE_APP_SERVICE_DASHBOARD_URI}" } } diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.KeyvaultReferenceHandling.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.KeyvaultReferenceHandling.verified.bicep index 79f4b1c642c..f014bf0dbff 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.KeyvaultReferenceHandling.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.KeyvaultReferenceHandling.verified.bicep @@ -21,7 +21,7 @@ param api_identity_outputs_id string param api_identity_outputs_clientid string -param env_outputs_dashboard_uri string +param env_outputs_azure_app_service_dashboard_uri string resource mainContainer 'Microsoft.Web/sites/sitecontainers@2024-11-01' = { name: 'main' @@ -107,7 +107,7 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { } { name: 'OTEL_COLLECTOR_URL' - value: env_outputs_dashboard_uri + value: env_outputs_azure_app_service_dashboard_uri } { name: 'OTEL_CLIENT_ID' diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.KeyvaultReferenceHandling.verified.json b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.KeyvaultReferenceHandling.verified.json index b6bed8a024e..1eb950914ee 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.KeyvaultReferenceHandling.verified.json +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.KeyvaultReferenceHandling.verified.json @@ -12,6 +12,6 @@ "sharedRg": "{sharedRg.value}", "api_identity_outputs_id": "{api-identity.outputs.id}", "api_identity_outputs_clientid": "{api-identity.outputs.clientId}", - "env_outputs_dashboard_uri": "{env.outputs.DASHBOARD_URI}" + "env_outputs_azure_app_service_dashboard_uri": "{env.outputs.AZURE_APP_SERVICE_DASHBOARD_URI}" } } diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.MultipleAzureAppServiceEnvironmentsSupported.verified.json b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.MultipleAzureAppServiceEnvironmentsSupported.verified.json index 2febd42ae96..7bbb411e09a 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.MultipleAzureAppServiceEnvironmentsSupported.verified.json +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.MultipleAzureAppServiceEnvironmentsSupported.verified.json @@ -27,7 +27,7 @@ "env1_outputs_azure_container_registry_managed_identity_client_id": "{env1.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_CLIENT_ID}", "servicea_containerimage": "{ServiceA.containerImage}", "servicea_containerport": "{ServiceA.containerPort}", - "env1_outputs_dashboard_uri": "{env1.outputs.DASHBOARD_URI}" + "env1_outputs_azure_app_service_dashboard_uri": "{env1.outputs.AZURE_APP_SERVICE_DASHBOARD_URI}" } }, "env": { @@ -64,7 +64,7 @@ "env2_outputs_azure_container_registry_managed_identity_client_id": "{env2.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_CLIENT_ID}", "serviceb_containerimage": "{ServiceB.containerImage}", "serviceb_containerport": "{ServiceB.containerPort}", - "env2_outputs_dashboard_uri": "{env2.outputs.DASHBOARD_URI}" + "env2_outputs_azure_app_service_dashboard_uri": "{env2.outputs.AZURE_APP_SERVICE_DASHBOARD_URI}" } }, "env": { diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.ResourceWithProbes.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.ResourceWithProbes.verified.bicep index 229dd11525e..2d919a4a305 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.ResourceWithProbes.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.ResourceWithProbes.verified.bicep @@ -13,7 +13,7 @@ param project1_containerimage string param project1_containerport string -param env_outputs_dashboard_uri string +param env_outputs_azure_app_service_dashboard_uri string resource mainContainer 'Microsoft.Web/sites/sitecontainers@2024-11-01' = { name: 'main' @@ -75,7 +75,7 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { } { name: 'OTEL_COLLECTOR_URL' - value: env_outputs_dashboard_uri + value: env_outputs_azure_app_service_dashboard_uri } { name: 'OTEL_CLIENT_ID' diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.UnknownManifestExpressionProviderIsHandledWithAllocateParameter.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.UnknownManifestExpressionProviderIsHandledWithAllocateParameter.verified.bicep index 47baf889bdb..a2ae628aec8 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.UnknownManifestExpressionProviderIsHandledWithAllocateParameter.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.UnknownManifestExpressionProviderIsHandledWithAllocateParameter.verified.bicep @@ -15,7 +15,7 @@ param api_containerport string param customvalue string -param env_outputs_dashboard_uri string +param env_outputs_azure_app_service_dashboard_uri string resource mainContainer 'Microsoft.Web/sites/sitecontainers@2024-11-01' = { name: 'main' @@ -81,7 +81,7 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { } { name: 'OTEL_COLLECTOR_URL' - value: env_outputs_dashboard_uri + value: env_outputs_azure_app_service_dashboard_uri } { name: 'OTEL_CLIENT_ID' diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.UnknownManifestExpressionProviderIsHandledWithAllocateParameter.verified.json b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.UnknownManifestExpressionProviderIsHandledWithAllocateParameter.verified.json index 13afb878faa..c669a75e741 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.UnknownManifestExpressionProviderIsHandledWithAllocateParameter.verified.json +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.UnknownManifestExpressionProviderIsHandledWithAllocateParameter.verified.json @@ -9,6 +9,6 @@ "api_containerimage": "{api.containerImage}", "api_containerport": "{api.containerPort}", "customvalue": "{customValue}", - "env_outputs_dashboard_uri": "{env.outputs.DASHBOARD_URI}" + "env_outputs_azure_app_service_dashboard_uri": "{env.outputs.AZURE_APP_SERVICE_DASHBOARD_URI}" } } diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureUserAssignedIdentityTests.WithAzureUserAssignedIdentity_WithRoleAssignments_AzureAppService_Works#00.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureUserAssignedIdentityTests.WithAzureUserAssignedIdentity_WithRoleAssignments_AzureAppService_Works#00.verified.bicep index 6f1ea13f420..4933204dfd8 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureUserAssignedIdentityTests.WithAzureUserAssignedIdentity_WithRoleAssignments_AzureAppService_Works#00.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureUserAssignedIdentityTests.WithAzureUserAssignedIdentity_WithRoleAssignments_AzureAppService_Works#00.verified.bicep @@ -15,7 +15,7 @@ param myidentity_outputs_id string param myidentity_outputs_clientid string -param appservice_outputs_dashboard_uri string +param appservice_outputs_azure_app_service_dashboard_uri string resource mainContainer 'Microsoft.Web/sites/sitecontainers@2024-11-01' = { name: 'main' @@ -74,7 +74,7 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { } { name: 'OTEL_COLLECTOR_URL' - value: appservice_outputs_dashboard_uri + value: appservice_outputs_azure_app_service_dashboard_uri } { name: 'OTEL_CLIENT_ID' From 884857c9154b75d91449742d230f1ef3a2cc8d0d Mon Sep 17 00:00:00 2001 From: Shilpi Rachna Date: Fri, 3 Oct 2025 12:25:32 -0700 Subject: [PATCH 12/17] Updated a comment --- .../AzureAppServiceWebsiteContext.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceWebsiteContext.cs b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceWebsiteContext.cs index 24930cbdfe1..62ad7b59a80 100644 --- a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceWebsiteContext.cs +++ b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceWebsiteContext.cs @@ -226,6 +226,9 @@ public void BuildWebSite(AzureResourceInfrastructure infra) UseManagedIdentityCreds = true, // Setting NumberOfWorkers to maximum allowed value for Premium SKU // https://learn.microsoft.com/en-us/azure/app-service/manage-scale-up + // This is required due to use of feature PerSiteScaling for the App Service plan + // We want the web apps to scale normally as defined for the app service plan + // so setting the maximum number of workers to the maximum allowed for Premium V2 SKU. NumberOfWorkers = 30, AppSettings = [] }, From da816ef29a09aa0d28b3bb147522818081e20326 Mon Sep 17 00:00:00 2001 From: David Fowler Date: Sat, 4 Oct 2025 13:27:46 -0700 Subject: [PATCH 13/17] Update src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentUtility.cs Co-authored-by: Eric Erhardt --- .../AzureAppServiceEnvironmentUtility.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentUtility.cs b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentUtility.cs index 5ba98989c5b..f68c6d0a1ad 100644 --- a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentUtility.cs +++ b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentUtility.cs @@ -62,7 +62,7 @@ public static WebSite AddDashboard(AzureResourceInfrastructure infra, infra.Add(rgRa2); - var webSite = new WebSite("webapp") + var webSite = new WebSite("dashboard") { // Use the host name as the name of the web app Name = DashboardHostName, From e43750ecd8393f69700cacde7e6976264734e8bf Mon Sep 17 00:00:00 2001 From: Shilpi Rachna Date: Mon, 6 Oct 2025 03:44:29 -0700 Subject: [PATCH 14/17] Handled scenario where there are multiple Aspire environments in a single resource group. - Website Contributor role is assigned for each web app in an environment instead of at resource group level - Added environment name to construct unique dashboard names - Add appsetting for ASPIRE_ENVIRONMENT_NAME to identify web apps and dashboard specific to an environment --- .../AzureAppServiceEnvironmentExtensions.cs | 2 +- .../AzureAppServiceEnvironmentResource.cs | 2 + .../AzureAppServiceEnvironmentUtility.cs | 60 +++++++------------ .../AzureAppServiceWebsiteContext.cs | 35 ++++++++++- ...EnvironmentWithoutDashboard.verified.bicep | 4 ++ ...tainerAppToProjectResources.verified.bicep | 18 ++++++ ...ntainerAppToProjectResources.verified.json | 4 +- ...mentAddsEnvironmentResource.verified.bicep | 30 ++++------ ...ServiceToContainerResources.verified.bicep | 18 ++++++ ...pServiceToContainerResources.verified.json | 4 +- ...renceExistingAppServicePlan.verified.bicep | 30 ++++------ ...pportBaitAndSwitchResources.verified.bicep | 18 ++++++ ...upportBaitAndSwitchResources.verified.json | 4 +- ...esAreResolvedAcrossProjects.verified.bicep | 18 ++++++ ...cesAreResolvedAcrossProjects.verified.json | 4 +- ...s.KeyvaultReferenceHandling.verified.bicep | 18 ++++++ ...ts.KeyvaultReferenceHandling.verified.json | 4 +- ...ServiceEnvironmentsSupported.verified.json | 8 ++- ...iceTests.ResourceWithProbes.verified.bicep | 18 ++++++ ...andledWithAllocateParameter.verified.bicep | 18 ++++++ ...HandledWithAllocateParameter.verified.json | 4 +- ...ts_AzureAppService_Works#00.verified.bicep | 18 ++++++ ...ithRoleAssignments_Works#00.verified.bicep | 4 +- 23 files changed, 252 insertions(+), 91 deletions(-) diff --git a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentExtensions.cs b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentExtensions.cs index c74626ade56..86ad4926aa8 100644 --- a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentExtensions.cs +++ b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentExtensions.cs @@ -141,7 +141,7 @@ public static IResourceBuilder AddAzureAppSe infra.Add(new ProvisioningOutput("AZURE_APP_SERVICE_DASHBOARD_URI", typeof(string)) { - Value = BicepFunction.Interpolate($"https://{AzureAppServiceEnvironmentUtility.DashboardHostName}.azurewebsites.net") + Value = BicepFunction.Interpolate($"https://{AzureAppServiceEnvironmentUtility.GetDashboardHostName(prefix)}.azurewebsites.net") }); } }); diff --git a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentResource.cs b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentResource.cs index c3a16ab07e3..6c07a16a15b 100644 --- a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentResource.cs +++ b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentResource.cs @@ -26,6 +26,8 @@ public class AzureAppServiceEnvironmentResource(string name, Action new("AZURE_CONTAINER_REGISTRY_NAME", this); internal BicepOutputReference ContainerRegistryManagedIdentityId => new("AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID", this); internal BicepOutputReference ContainerRegistryClientId => new("AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_CLIENT_ID", this); + internal BicepOutputReference WebsiteContributorManagedIdentityId => new("AZURE_WEBSITE_CONTRIBUTOR_MANAGED_IDENTITY_ID", this); + internal BicepOutputReference WebsiteContributorManagedIdentityPrincipalId => new("AZURE_WEBSITE_CONTRIBUTOR_MANAGED_IDENTITY_PRINCIPAL_ID", this); /// /// Gets or sets a value indicating whether the Aspire dashboard should be included in the container app environment. diff --git a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentUtility.cs b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentUtility.cs index 5ba98989c5b..7fc74551699 100644 --- a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentUtility.cs +++ b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentUtility.cs @@ -4,7 +4,6 @@ using Azure.Core; using Azure.Provisioning; using Azure.Provisioning.AppService; -using Azure.Provisioning.Authorization; using Azure.Provisioning.Expressions; using Azure.Provisioning.Resources; using Azure.Provisioning.Roles; @@ -13,10 +12,13 @@ namespace Aspire.Hosting.Azure.AppService; internal static class AzureAppServiceEnvironmentUtility { - internal const string ResourceName = "dashboard"; + internal const string ResourceName = "aspiredashboard"; - public static BicepValue DashboardHostName => BicepFunction.Take( - BicepFunction.Interpolate($"{BicepFunction.ToLower(ResourceName)}-{BicepFunction.GetUniqueString(BicepFunction.GetResourceGroup().Id)}"), 60); + public static BicepValue GetDashboardHostName(string aspireResourceName) + { + return BicepFunction.Take( + BicepFunction.Interpolate($"{BicepFunction.ToLower(aspireResourceName)}-{BicepFunction.ToLower(ResourceName)}-{BicepFunction.GetUniqueString(BicepFunction.GetResourceGroup().Id)}"), 60); + } public static WebSite AddDashboard(AzureResourceInfrastructure infra, UserAssignedIdentity otelIdentity, @@ -28,44 +30,13 @@ public static WebSite AddDashboard(AzureResourceInfrastructure infra, var otelClientId = otelIdentity.ClientId; var prefix = infra.AspireResource.Name; var contributorIdentity = new UserAssignedIdentity(Infrastructure.NormalizeBicepIdentifier($"{prefix}-contributor-mi")); - - infra.Add(contributorIdentity); - // Add Website Contributor role assignment - var rgRaId = BicepFunction.GetSubscriptionResourceId( - "Microsoft.Authorization/roleDefinitions", - "de139f84-1756-47ae-9be6-808fbbe84772"); - var rgRaName = BicepFunction.CreateGuid(BicepFunction.GetResourceGroup().Id, contributorIdentity.Id, rgRaId); - var rgRa = new RoleAssignment(Infrastructure.NormalizeBicepIdentifier($"{prefix}_ra")) - { - Name = rgRaName, - PrincipalType = RoleManagementPrincipalType.ServicePrincipal, - PrincipalId = contributorIdentity.PrincipalId, - RoleDefinitionId = rgRaId, - }; - - infra.Add(rgRa); - - // Add Reader role assignment - var rgRaId2 = BicepFunction.GetSubscriptionResourceId( - "Microsoft.Authorization/roleDefinitions", - "acdd72a7-3385-48ef-bd42-f606fba81ae7"); - var rgRaName2 = BicepFunction.CreateGuid(BicepFunction.GetResourceGroup().Id, contributorIdentity.Id, rgRaId2); - - var rgRa2 = new RoleAssignment(Infrastructure.NormalizeBicepIdentifier($"{prefix}_ra2")) - { - Name = rgRaName2, - PrincipalType = RoleManagementPrincipalType.ServicePrincipal, - PrincipalId = contributorIdentity.PrincipalId, - RoleDefinitionId = rgRaId2 - }; - - infra.Add(rgRa2); + infra.Add(contributorIdentity); var webSite = new WebSite("webapp") { // Use the host name as the name of the web app - Name = DashboardHostName, + Name = GetDashboardHostName(infra.AspireResource.Name), AppServicePlanId = appServicePlanId, // Aspire dashboards are created with a new kind aspiredashboard Kind = "app,linux,aspiredashboard", @@ -105,8 +76,23 @@ public static WebSite AddDashboard(AzureResourceInfrastructure infra, // Appsettings related to managed identity for auth webSite.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "AZURE_CLIENT_ID", Value = contributorIdentity.ClientId }); webSite.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "ALLOWED_MANAGED_IDENTITIES", Value = otelClientId }); + // Added appsetting to identify the resources in a specific aspire environment + webSite.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "ASPIRE_ENVIRONMENT_NAME", Value = infra.AspireResource.Name }); + infra.Add(webSite); + // Outputs needed by the app service environment + // This identity needs website contributor access on the websites for resource server to work + infra.Add(new ProvisioningOutput("AZURE_WEBSITE_CONTRIBUTOR_MANAGED_IDENTITY_ID", typeof(string)) + { + Value = contributorIdentity.Id + }); + + infra.Add(new ProvisioningOutput("AZURE_WEBSITE_CONTRIBUTOR_MANAGED_IDENTITY_PRINCIPAL_ID", typeof(string)) + { + Value = contributorIdentity.PrincipalId + }); + return webSite; } } diff --git a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceWebsiteContext.cs b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceWebsiteContext.cs index 62ad7b59a80..df4a9045049 100644 --- a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceWebsiteContext.cs +++ b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceWebsiteContext.cs @@ -7,6 +7,7 @@ using Aspire.Hosting.ApplicationModel; using Azure.Provisioning; using Azure.Provisioning.AppService; +using Azure.Provisioning.Authorization; using Azure.Provisioning.Expressions; using Azure.Provisioning.Resources; @@ -312,6 +313,9 @@ static FunctionCallExpression Join(BicepExpression args, string delimeter) => }); } + // Added appsetting to identify the resource in a specific aspire environment + webSite.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "ASPIRE_ENVIRONMENT_NAME", Value = environmentContext.Environment.Name }); + // Probes #pragma warning disable ASPIREPROBES001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. if (resource.TryGetAnnotationsOfType(out var probeAnnotations)) @@ -329,13 +333,17 @@ static FunctionCallExpression Join(BicepExpression args, string delimeter) => } #pragma warning restore ASPIREPROBES001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. + RoleAssignment? webSiteRa = null; if (environmentContext.Environment.EnableDashboard) { - var dashboardUri = environmentContext.Environment.DashboardUriReference.AsProvisioningParameter(infra); - AddDashboardSettings(webSite, acrClientIdParameter, dashboardUri); + webSiteRa = AddDashboardPermissionAndSettings(webSite, acrClientIdParameter); } infra.Add(webSite); + if (webSiteRa is not null) + { + infra.Add(webSiteRa); + } // Allow users to customize the web app here if (resource.TryGetAnnotationsOfType(out var customizeWebSiteAnnotations)) @@ -374,14 +382,35 @@ private ProvisioningParameter AllocateParameter(IManifestExpressionProvider para { return parameter.AsProvisioningParameter(Infra, isSecure: secretType == SecretType.Normal); } - private void AddDashboardSettings(WebSite webSite, ProvisioningParameter acrClientIdParameter, ProvisioningParameter dashboardUri) + + private RoleAssignment AddDashboardPermissionAndSettings(WebSite webSite, ProvisioningParameter acrClientIdParameter) { + var dashboardUri = environmentContext.Environment.DashboardUriReference.AsProvisioningParameter(Infra); + var contributorId = environmentContext.Environment.WebsiteContributorManagedIdentityId.AsProvisioningParameter(Infra); + var contributorPrincipalId = environmentContext.Environment.WebsiteContributorManagedIdentityPrincipalId.AsProvisioningParameter(Infra); + + // Add the appsettings specific to sending telemetry data to dashboard webSite.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "OTEL_SERVICE_NAME", Value = resource.Name }); webSite.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "OTEL_EXPORTER_OTLP_PROTOCOL", Value = "grpc" }); webSite.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "OTEL_EXPORTER_OTLP_ENDPOINT", Value = "http://localhost:6001" }); webSite.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "WEBSITE_ENABLE_ASPIRE_OTEL_SIDECAR", Value = "true" }); webSite.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "OTEL_COLLECTOR_URL", Value = dashboardUri }); webSite.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "OTEL_CLIENT_ID", Value = acrClientIdParameter }); + + // Add Website Contributor role assignment to dashboard's managed identity forthis webapp + var websiteRaId = BicepFunction.GetSubscriptionResourceId( + "Microsoft.Authorization/roleDefinitions", + "de139f84-1756-47ae-9be6-808fbbe84772"); + var websiteRaName = BicepFunction.CreateGuid(webSite.Id, contributorId, websiteRaId); + + return new RoleAssignment(Infrastructure.NormalizeBicepIdentifier($"{Infra.AspireResource.Name}_ra")) + { + Name = websiteRaName, + Scope = new IdentifierExpression(webSite.BicepIdentifier), + PrincipalType = RoleManagementPrincipalType.ServicePrincipal, + PrincipalId = contributorPrincipalId, + RoleDefinitionId = websiteRaId, + }; } enum SecretType diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddAppServiceToEnvironmentWithoutDashboard.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddAppServiceToEnvironmentWithoutDashboard.verified.bicep index 61b352b2f82..074638a4ba6 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddAppServiceToEnvironmentWithoutDashboard.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddAppServiceToEnvironmentWithoutDashboard.verified.bicep @@ -59,6 +59,10 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { name: 'services__project1__http__0' value: 'http://${take('${toLower('project1')}-${uniqueString(resourceGroup().id)}', 60)}.azurewebsites.net' } + { + name: 'ASPIRE_ENVIRONMENT_NAME' + value: 'env' + } ] } } diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsDeploymentTargetWithContainerAppToProjectResources.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsDeploymentTargetWithContainerAppToProjectResources.verified.bicep index 354023a8a31..9cf323ac43e 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsDeploymentTargetWithContainerAppToProjectResources.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsDeploymentTargetWithContainerAppToProjectResources.verified.bicep @@ -15,6 +15,10 @@ param api_containerport string param env_outputs_azure_app_service_dashboard_uri string +param env_outputs_azure_website_contributor_managed_identity_id string + +param env_outputs_azure_website_contributor_managed_identity_principal_id string + resource mainContainer 'Microsoft.Web/sites/sitecontainers@2024-11-01' = { name: 'main' properties: { @@ -57,6 +61,10 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { name: 'HTTP_PORTS' value: api_containerport } + { + name: 'ASPIRE_ENVIRONMENT_NAME' + value: 'env' + } { name: 'OTEL_SERVICE_NAME' value: 'api' @@ -92,3 +100,13 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { } } } + +resource api_ra 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(webapp.id, env_outputs_azure_website_contributor_managed_identity_id, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772')) + properties: { + principalId: env_outputs_azure_website_contributor_managed_identity_principal_id + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772') + principalType: 'ServicePrincipal' + } + scope: webapp +} diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsDeploymentTargetWithContainerAppToProjectResources.verified.json b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsDeploymentTargetWithContainerAppToProjectResources.verified.json index 7523f4c26ac..dbf9ec3989a 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsDeploymentTargetWithContainerAppToProjectResources.verified.json +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsDeploymentTargetWithContainerAppToProjectResources.verified.json @@ -8,6 +8,8 @@ "env_outputs_azure_container_registry_managed_identity_client_id": "{env.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_CLIENT_ID}", "api_containerimage": "{api.containerImage}", "api_containerport": "{api.containerPort}", - "env_outputs_azure_app_service_dashboard_uri": "{env.outputs.AZURE_APP_SERVICE_DASHBOARD_URI}" + "env_outputs_azure_app_service_dashboard_uri": "{env.outputs.AZURE_APP_SERVICE_DASHBOARD_URI}", + "env_outputs_azure_website_contributor_managed_identity_id": "{env.outputs.AZURE_WEBSITE_CONTRIBUTOR_MANAGED_IDENTITY_ID}", + "env_outputs_azure_website_contributor_managed_identity_principal_id": "{env.outputs.AZURE_WEBSITE_CONTRIBUTOR_MANAGED_IDENTITY_PRINCIPAL_ID}" } } diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsEnvironmentResource.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsEnvironmentResource.verified.bicep index 9637cab232b..ce653d8f830 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsEnvironmentResource.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsEnvironmentResource.verified.bicep @@ -49,26 +49,8 @@ resource env_contributor_mi 'Microsoft.ManagedIdentity/userAssignedIdentities@20 location: location } -resource env_ra 'Microsoft.Authorization/roleAssignments@2022-04-01' = { - name: guid(resourceGroup().id, env_contributor_mi.id, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772')) - properties: { - principalId: env_contributor_mi.properties.principalId - roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772') - principalType: 'ServicePrincipal' - } -} - -resource env_ra2 'Microsoft.Authorization/roleAssignments@2022-04-01' = { - name: guid(resourceGroup().id, env_contributor_mi.id, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')) - properties: { - principalId: env_contributor_mi.properties.principalId - roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - principalType: 'ServicePrincipal' - } -} - resource webapp 'Microsoft.Web/sites@2024-11-01' = { - name: take('${toLower('dashboard')}-${uniqueString(resourceGroup().id)}', 60) + name: take('${toLower('env')}-${toLower('aspiredashboard')}-${uniqueString(resourceGroup().id)}', 60) location: location properties: { serverFarmId: env_asplan.id @@ -114,6 +96,10 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { name: 'ALLOWED_MANAGED_IDENTITIES' value: env_mi.properties.clientId } + { + name: 'ASPIRE_ENVIRONMENT_NAME' + value: 'env' + } ] alwaysOn: true http20Enabled: true @@ -141,4 +127,8 @@ output AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID string = env_mi.id output AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_CLIENT_ID string = env_mi.properties.clientId -output AZURE_APP_SERVICE_DASHBOARD_URI string = 'https://${take('${toLower('dashboard')}-${uniqueString(resourceGroup().id)}', 60)}.azurewebsites.net' +output AZURE_WEBSITE_CONTRIBUTOR_MANAGED_IDENTITY_ID string = env_contributor_mi.id + +output AZURE_WEBSITE_CONTRIBUTOR_MANAGED_IDENTITY_PRINCIPAL_ID string = env_contributor_mi.properties.principalId + +output AZURE_APP_SERVICE_DASHBOARD_URI string = 'https://${take('${toLower('env')}-${toLower('aspiredashboard')}-${uniqueString(resourceGroup().id)}', 60)}.azurewebsites.net' diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddDockerfileWithAppServiceInfrastructureAddsDeploymentTargetWithAppServiceToContainerResources.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddDockerfileWithAppServiceInfrastructureAddsDeploymentTargetWithAppServiceToContainerResources.verified.bicep index 382c66431f2..37dfe78b3d5 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddDockerfileWithAppServiceInfrastructureAddsDeploymentTargetWithAppServiceToContainerResources.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddDockerfileWithAppServiceInfrastructureAddsDeploymentTargetWithAppServiceToContainerResources.verified.bicep @@ -13,6 +13,10 @@ param api_containerimage string param env_outputs_azure_app_service_dashboard_uri string +param env_outputs_azure_website_contributor_managed_identity_id string + +param env_outputs_azure_website_contributor_managed_identity_principal_id string + resource mainContainer 'Microsoft.Web/sites/sitecontainers@2024-11-01' = { name: 'main' properties: { @@ -39,6 +43,10 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { name: 'PORT' value: '85' } + { + name: 'ASPIRE_ENVIRONMENT_NAME' + value: 'env' + } { name: 'OTEL_SERVICE_NAME' value: 'api' @@ -73,3 +81,13 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { } } } + +resource api_ra 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(webapp.id, env_outputs_azure_website_contributor_managed_identity_id, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772')) + properties: { + principalId: env_outputs_azure_website_contributor_managed_identity_principal_id + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772') + principalType: 'ServicePrincipal' + } + scope: webapp +} diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddDockerfileWithAppServiceInfrastructureAddsDeploymentTargetWithAppServiceToContainerResources.verified.json b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddDockerfileWithAppServiceInfrastructureAddsDeploymentTargetWithAppServiceToContainerResources.verified.json index f8ba7a2f06c..069979ae62c 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddDockerfileWithAppServiceInfrastructureAddsDeploymentTargetWithAppServiceToContainerResources.verified.json +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddDockerfileWithAppServiceInfrastructureAddsDeploymentTargetWithAppServiceToContainerResources.verified.json @@ -7,6 +7,8 @@ "env_outputs_azure_container_registry_managed_identity_id": "{env.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID}", "env_outputs_azure_container_registry_managed_identity_client_id": "{env.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_CLIENT_ID}", "api_containerimage": "{api.containerImage}", - "env_outputs_azure_app_service_dashboard_uri": "{env.outputs.AZURE_APP_SERVICE_DASHBOARD_URI}" + "env_outputs_azure_app_service_dashboard_uri": "{env.outputs.AZURE_APP_SERVICE_DASHBOARD_URI}", + "env_outputs_azure_website_contributor_managed_identity_id": "{env.outputs.AZURE_WEBSITE_CONTRIBUTOR_MANAGED_IDENTITY_ID}", + "env_outputs_azure_website_contributor_managed_identity_principal_id": "{env.outputs.AZURE_WEBSITE_CONTRIBUTOR_MANAGED_IDENTITY_PRINCIPAL_ID}" } } diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceEnvironmentCanReferenceExistingAppServicePlan.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceEnvironmentCanReferenceExistingAppServicePlan.verified.bicep index 9637cab232b..ce653d8f830 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceEnvironmentCanReferenceExistingAppServicePlan.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceEnvironmentCanReferenceExistingAppServicePlan.verified.bicep @@ -49,26 +49,8 @@ resource env_contributor_mi 'Microsoft.ManagedIdentity/userAssignedIdentities@20 location: location } -resource env_ra 'Microsoft.Authorization/roleAssignments@2022-04-01' = { - name: guid(resourceGroup().id, env_contributor_mi.id, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772')) - properties: { - principalId: env_contributor_mi.properties.principalId - roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772') - principalType: 'ServicePrincipal' - } -} - -resource env_ra2 'Microsoft.Authorization/roleAssignments@2022-04-01' = { - name: guid(resourceGroup().id, env_contributor_mi.id, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')) - properties: { - principalId: env_contributor_mi.properties.principalId - roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - principalType: 'ServicePrincipal' - } -} - resource webapp 'Microsoft.Web/sites@2024-11-01' = { - name: take('${toLower('dashboard')}-${uniqueString(resourceGroup().id)}', 60) + name: take('${toLower('env')}-${toLower('aspiredashboard')}-${uniqueString(resourceGroup().id)}', 60) location: location properties: { serverFarmId: env_asplan.id @@ -114,6 +96,10 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { name: 'ALLOWED_MANAGED_IDENTITIES' value: env_mi.properties.clientId } + { + name: 'ASPIRE_ENVIRONMENT_NAME' + value: 'env' + } ] alwaysOn: true http20Enabled: true @@ -141,4 +127,8 @@ output AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID string = env_mi.id output AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_CLIENT_ID string = env_mi.properties.clientId -output AZURE_APP_SERVICE_DASHBOARD_URI string = 'https://${take('${toLower('dashboard')}-${uniqueString(resourceGroup().id)}', 60)}.azurewebsites.net' +output AZURE_WEBSITE_CONTRIBUTOR_MANAGED_IDENTITY_ID string = env_contributor_mi.id + +output AZURE_WEBSITE_CONTRIBUTOR_MANAGED_IDENTITY_PRINCIPAL_ID string = env_contributor_mi.properties.principalId + +output AZURE_APP_SERVICE_DASHBOARD_URI string = 'https://${take('${toLower('env')}-${toLower('aspiredashboard')}-${uniqueString(resourceGroup().id)}', 60)}.azurewebsites.net' diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceSupportBaitAndSwitchResources.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceSupportBaitAndSwitchResources.verified.bicep index 3d318e7d3af..a92750caf6a 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceSupportBaitAndSwitchResources.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceSupportBaitAndSwitchResources.verified.bicep @@ -13,6 +13,10 @@ param api_containerimage string param env_outputs_azure_app_service_dashboard_uri string +param env_outputs_azure_website_contributor_managed_identity_id string + +param env_outputs_azure_website_contributor_managed_identity_principal_id string + resource mainContainer 'Microsoft.Web/sites/sitecontainers@2024-11-01' = { name: 'main' properties: { @@ -55,6 +59,10 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { name: 'PORT' value: '80' } + { + name: 'ASPIRE_ENVIRONMENT_NAME' + value: 'env' + } { name: 'OTEL_SERVICE_NAME' value: 'api' @@ -89,3 +97,13 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { } } } + +resource api_ra 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(webapp.id, env_outputs_azure_website_contributor_managed_identity_id, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772')) + properties: { + principalId: env_outputs_azure_website_contributor_managed_identity_principal_id + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772') + principalType: 'ServicePrincipal' + } + scope: webapp +} diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceSupportBaitAndSwitchResources.verified.json b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceSupportBaitAndSwitchResources.verified.json index f8ba7a2f06c..069979ae62c 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceSupportBaitAndSwitchResources.verified.json +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceSupportBaitAndSwitchResources.verified.json @@ -7,6 +7,8 @@ "env_outputs_azure_container_registry_managed_identity_id": "{env.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID}", "env_outputs_azure_container_registry_managed_identity_client_id": "{env.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_CLIENT_ID}", "api_containerimage": "{api.containerImage}", - "env_outputs_azure_app_service_dashboard_uri": "{env.outputs.AZURE_APP_SERVICE_DASHBOARD_URI}" + "env_outputs_azure_app_service_dashboard_uri": "{env.outputs.AZURE_APP_SERVICE_DASHBOARD_URI}", + "env_outputs_azure_website_contributor_managed_identity_id": "{env.outputs.AZURE_WEBSITE_CONTRIBUTOR_MANAGED_IDENTITY_ID}", + "env_outputs_azure_website_contributor_managed_identity_principal_id": "{env.outputs.AZURE_WEBSITE_CONTRIBUTOR_MANAGED_IDENTITY_PRINCIPAL_ID}" } } diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.EndpointReferencesAreResolvedAcrossProjects.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.EndpointReferencesAreResolvedAcrossProjects.verified.bicep index 938368410cf..bd0059330d7 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.EndpointReferencesAreResolvedAcrossProjects.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.EndpointReferencesAreResolvedAcrossProjects.verified.bicep @@ -15,6 +15,10 @@ param project2_containerport string param env_outputs_azure_app_service_dashboard_uri string +param env_outputs_azure_website_contributor_managed_identity_id string + +param env_outputs_azure_website_contributor_managed_identity_principal_id string + resource mainContainer 'Microsoft.Web/sites/sitecontainers@2024-11-01' = { name: 'main' properties: { @@ -61,6 +65,10 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { name: 'services__project1__http__0' value: 'http://${take('${toLower('project1')}-${uniqueString(resourceGroup().id)}', 60)}.azurewebsites.net' } + { + name: 'ASPIRE_ENVIRONMENT_NAME' + value: 'env' + } { name: 'OTEL_SERVICE_NAME' value: 'project2' @@ -95,3 +103,13 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { } } } + +resource project2_ra 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(webapp.id, env_outputs_azure_website_contributor_managed_identity_id, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772')) + properties: { + principalId: env_outputs_azure_website_contributor_managed_identity_principal_id + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772') + principalType: 'ServicePrincipal' + } + scope: webapp +} diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.EndpointReferencesAreResolvedAcrossProjects.verified.json b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.EndpointReferencesAreResolvedAcrossProjects.verified.json index eeda43c4e49..f850deb03d4 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.EndpointReferencesAreResolvedAcrossProjects.verified.json +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.EndpointReferencesAreResolvedAcrossProjects.verified.json @@ -8,6 +8,8 @@ "env_outputs_azure_container_registry_managed_identity_client_id": "{env.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_CLIENT_ID}", "project2_containerimage": "{project2.containerImage}", "project2_containerport": "{project2.containerPort}", - "env_outputs_azure_app_service_dashboard_uri": "{env.outputs.AZURE_APP_SERVICE_DASHBOARD_URI}" + "env_outputs_azure_app_service_dashboard_uri": "{env.outputs.AZURE_APP_SERVICE_DASHBOARD_URI}", + "env_outputs_azure_website_contributor_managed_identity_id": "{env.outputs.AZURE_WEBSITE_CONTRIBUTOR_MANAGED_IDENTITY_ID}", + "env_outputs_azure_website_contributor_managed_identity_principal_id": "{env.outputs.AZURE_WEBSITE_CONTRIBUTOR_MANAGED_IDENTITY_PRINCIPAL_ID}" } } diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.KeyvaultReferenceHandling.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.KeyvaultReferenceHandling.verified.bicep index f014bf0dbff..0a1b2cb0e13 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.KeyvaultReferenceHandling.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.KeyvaultReferenceHandling.verified.bicep @@ -23,6 +23,10 @@ param api_identity_outputs_clientid string param env_outputs_azure_app_service_dashboard_uri string +param env_outputs_azure_website_contributor_managed_identity_id string + +param env_outputs_azure_website_contributor_managed_identity_principal_id string + resource mainContainer 'Microsoft.Web/sites/sitecontainers@2024-11-01' = { name: 'main' properties: { @@ -89,6 +93,10 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { name: 'AZURE_CLIENT_ID' value: api_identity_outputs_clientid } + { + name: 'ASPIRE_ENVIRONMENT_NAME' + value: 'env' + } { name: 'OTEL_SERVICE_NAME' value: 'api' @@ -124,3 +132,13 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { } } } + +resource api_ra 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(webapp.id, env_outputs_azure_website_contributor_managed_identity_id, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772')) + properties: { + principalId: env_outputs_azure_website_contributor_managed_identity_principal_id + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772') + principalType: 'ServicePrincipal' + } + scope: webapp +} diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.KeyvaultReferenceHandling.verified.json b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.KeyvaultReferenceHandling.verified.json index 1eb950914ee..bc30b2ead00 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.KeyvaultReferenceHandling.verified.json +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.KeyvaultReferenceHandling.verified.json @@ -12,6 +12,8 @@ "sharedRg": "{sharedRg.value}", "api_identity_outputs_id": "{api-identity.outputs.id}", "api_identity_outputs_clientid": "{api-identity.outputs.clientId}", - "env_outputs_azure_app_service_dashboard_uri": "{env.outputs.AZURE_APP_SERVICE_DASHBOARD_URI}" + "env_outputs_azure_app_service_dashboard_uri": "{env.outputs.AZURE_APP_SERVICE_DASHBOARD_URI}", + "env_outputs_azure_website_contributor_managed_identity_id": "{env.outputs.AZURE_WEBSITE_CONTRIBUTOR_MANAGED_IDENTITY_ID}", + "env_outputs_azure_website_contributor_managed_identity_principal_id": "{env.outputs.AZURE_WEBSITE_CONTRIBUTOR_MANAGED_IDENTITY_PRINCIPAL_ID}" } } diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.MultipleAzureAppServiceEnvironmentsSupported.verified.json b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.MultipleAzureAppServiceEnvironmentsSupported.verified.json index 7bbb411e09a..576fa8a046d 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.MultipleAzureAppServiceEnvironmentsSupported.verified.json +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.MultipleAzureAppServiceEnvironmentsSupported.verified.json @@ -27,7 +27,9 @@ "env1_outputs_azure_container_registry_managed_identity_client_id": "{env1.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_CLIENT_ID}", "servicea_containerimage": "{ServiceA.containerImage}", "servicea_containerport": "{ServiceA.containerPort}", - "env1_outputs_azure_app_service_dashboard_uri": "{env1.outputs.AZURE_APP_SERVICE_DASHBOARD_URI}" + "env1_outputs_azure_app_service_dashboard_uri": "{env1.outputs.AZURE_APP_SERVICE_DASHBOARD_URI}", + "env1_outputs_azure_website_contributor_managed_identity_id": "{env1.outputs.AZURE_WEBSITE_CONTRIBUTOR_MANAGED_IDENTITY_ID}", + "env1_outputs_azure_website_contributor_managed_identity_principal_id": "{env1.outputs.AZURE_WEBSITE_CONTRIBUTOR_MANAGED_IDENTITY_PRINCIPAL_ID}" } }, "env": { @@ -64,7 +66,9 @@ "env2_outputs_azure_container_registry_managed_identity_client_id": "{env2.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_CLIENT_ID}", "serviceb_containerimage": "{ServiceB.containerImage}", "serviceb_containerport": "{ServiceB.containerPort}", - "env2_outputs_azure_app_service_dashboard_uri": "{env2.outputs.AZURE_APP_SERVICE_DASHBOARD_URI}" + "env2_outputs_azure_app_service_dashboard_uri": "{env2.outputs.AZURE_APP_SERVICE_DASHBOARD_URI}", + "env2_outputs_azure_website_contributor_managed_identity_id": "{env2.outputs.AZURE_WEBSITE_CONTRIBUTOR_MANAGED_IDENTITY_ID}", + "env2_outputs_azure_website_contributor_managed_identity_principal_id": "{env2.outputs.AZURE_WEBSITE_CONTRIBUTOR_MANAGED_IDENTITY_PRINCIPAL_ID}" } }, "env": { diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.ResourceWithProbes.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.ResourceWithProbes.verified.bicep index 2d919a4a305..65ccb730c1a 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.ResourceWithProbes.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.ResourceWithProbes.verified.bicep @@ -15,6 +15,10 @@ param project1_containerport string param env_outputs_azure_app_service_dashboard_uri string +param env_outputs_azure_website_contributor_managed_identity_id string + +param env_outputs_azure_website_contributor_managed_identity_principal_id string + resource mainContainer 'Microsoft.Web/sites/sitecontainers@2024-11-01' = { name: 'main' properties: { @@ -57,6 +61,10 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { name: 'HTTPS_PORTS' value: project1_containerport } + { + name: 'ASPIRE_ENVIRONMENT_NAME' + value: 'env' + } { name: 'OTEL_SERVICE_NAME' value: 'project1' @@ -92,3 +100,13 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { } } } + +resource project1_ra 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(webapp.id, env_outputs_azure_website_contributor_managed_identity_id, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772')) + properties: { + principalId: env_outputs_azure_website_contributor_managed_identity_principal_id + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772') + principalType: 'ServicePrincipal' + } + scope: webapp +} diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.UnknownManifestExpressionProviderIsHandledWithAllocateParameter.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.UnknownManifestExpressionProviderIsHandledWithAllocateParameter.verified.bicep index a2ae628aec8..e00a9f36113 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.UnknownManifestExpressionProviderIsHandledWithAllocateParameter.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.UnknownManifestExpressionProviderIsHandledWithAllocateParameter.verified.bicep @@ -17,6 +17,10 @@ param customvalue string param env_outputs_azure_app_service_dashboard_uri string +param env_outputs_azure_website_contributor_managed_identity_id string + +param env_outputs_azure_website_contributor_managed_identity_principal_id string + resource mainContainer 'Microsoft.Web/sites/sitecontainers@2024-11-01' = { name: 'main' properties: { @@ -63,6 +67,10 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { name: 'CUSTOM_VALUE' value: customvalue } + { + name: 'ASPIRE_ENVIRONMENT_NAME' + value: 'env' + } { name: 'OTEL_SERVICE_NAME' value: 'api' @@ -97,3 +105,13 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { } } } + +resource api_ra 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(webapp.id, env_outputs_azure_website_contributor_managed_identity_id, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772')) + properties: { + principalId: env_outputs_azure_website_contributor_managed_identity_principal_id + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772') + principalType: 'ServicePrincipal' + } + scope: webapp +} \ No newline at end of file diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.UnknownManifestExpressionProviderIsHandledWithAllocateParameter.verified.json b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.UnknownManifestExpressionProviderIsHandledWithAllocateParameter.verified.json index c669a75e741..c22be722ee9 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.UnknownManifestExpressionProviderIsHandledWithAllocateParameter.verified.json +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.UnknownManifestExpressionProviderIsHandledWithAllocateParameter.verified.json @@ -9,6 +9,8 @@ "api_containerimage": "{api.containerImage}", "api_containerport": "{api.containerPort}", "customvalue": "{customValue}", - "env_outputs_azure_app_service_dashboard_uri": "{env.outputs.AZURE_APP_SERVICE_DASHBOARD_URI}" + "env_outputs_azure_app_service_dashboard_uri": "{env.outputs.AZURE_APP_SERVICE_DASHBOARD_URI}", + "env_outputs_azure_website_contributor_managed_identity_id": "{env.outputs.AZURE_WEBSITE_CONTRIBUTOR_MANAGED_IDENTITY_ID}", + "env_outputs_azure_website_contributor_managed_identity_principal_id": "{env.outputs.AZURE_WEBSITE_CONTRIBUTOR_MANAGED_IDENTITY_PRINCIPAL_ID}" } } diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureUserAssignedIdentityTests.WithAzureUserAssignedIdentity_WithRoleAssignments_AzureAppService_Works#00.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureUserAssignedIdentityTests.WithAzureUserAssignedIdentity_WithRoleAssignments_AzureAppService_Works#00.verified.bicep index 4933204dfd8..f9d75ca447f 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureUserAssignedIdentityTests.WithAzureUserAssignedIdentity_WithRoleAssignments_AzureAppService_Works#00.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureUserAssignedIdentityTests.WithAzureUserAssignedIdentity_WithRoleAssignments_AzureAppService_Works#00.verified.bicep @@ -17,6 +17,10 @@ param myidentity_outputs_clientid string param appservice_outputs_azure_app_service_dashboard_uri string +param appservice_outputs_azure_website_contributor_managed_identity_id string + +param appservice_outputs_azure_website_contributor_managed_identity_principal_id string + resource mainContainer 'Microsoft.Web/sites/sitecontainers@2024-11-01' = { name: 'main' properties: { @@ -56,6 +60,10 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { name: 'AZURE_CLIENT_ID' value: myidentity_outputs_clientid } + { + name: 'ASPIRE_ENVIRONMENT_NAME' + value: 'appservice' + } { name: 'OTEL_SERVICE_NAME' value: 'myapp' @@ -91,3 +99,13 @@ resource webapp 'Microsoft.Web/sites@2024-11-01' = { } } } + +resource myapp_ra 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(webapp.id, appservice_outputs_azure_website_contributor_managed_identity_id, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772')) + properties: { + principalId: appservice_outputs_azure_website_contributor_managed_identity_principal_id + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772') + principalType: 'ServicePrincipal' + } + scope: webapp +} \ No newline at end of file diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureUserAssignedIdentityTests.WithAzureUserAssignedIdentity_WithRoleAssignments_Works#00.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureUserAssignedIdentityTests.WithAzureUserAssignedIdentity_WithRoleAssignments_Works#00.verified.bicep index 59c4ebc5d78..7add56a6345 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureUserAssignedIdentityTests.WithAzureUserAssignedIdentity_WithRoleAssignments_Works#00.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureUserAssignedIdentityTests.WithAzureUserAssignedIdentity_WithRoleAssignments_Works#00.verified.bicep @@ -1,4 +1,4 @@ -@description('The location for the resource(s) to be deployed.') +@description('The location for the resource(s) to be deployed.') param location string = resourceGroup().location param cae_outputs_azure_container_apps_environment_default_domain string @@ -71,4 +71,4 @@ resource myapp 'Microsoft.App/containerApps@2025-02-02-preview' = { '${cae_outputs_azure_container_registry_managed_identity_id}': { } } } -} \ No newline at end of file +} From 9d3200b6de648520669f72e8637261da926a2184 Mon Sep 17 00:00:00 2001 From: Shilpi Rachna Date: Mon, 6 Oct 2025 04:29:00 -0700 Subject: [PATCH 15/17] Added Reader role assignment to managed identity for dashboard --- .../AzureAppServiceEnvironmentUtility.cs | 17 +++++++++++++++++ ...onmentAddsEnvironmentResource.verified.bicep | 11 ++++++++++- ...ferenceExistingAppServicePlan.verified.bicep | 9 +++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentUtility.cs b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentUtility.cs index 7fc74551699..6b99e139473 100644 --- a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentUtility.cs +++ b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentUtility.cs @@ -4,6 +4,7 @@ using Azure.Core; using Azure.Provisioning; using Azure.Provisioning.AppService; +using Azure.Provisioning.Authorization; using Azure.Provisioning.Expressions; using Azure.Provisioning.Resources; using Azure.Provisioning.Roles; @@ -33,6 +34,22 @@ public static WebSite AddDashboard(AzureResourceInfrastructure infra, infra.Add(contributorIdentity); + // Add Reader role assignment + var rgReaderRaId = BicepFunction.GetSubscriptionResourceId( + "Microsoft.Authorization/roleDefinitions", + "acdd72a7-3385-48ef-bd42-f606fba81ae7"); + var rgReaderRaName = BicepFunction.CreateGuid(BicepFunction.GetResourceGroup().Id, contributorIdentity.Id, rgReaderRaId); + + var rgReaderRa = new RoleAssignment(Infrastructure.NormalizeBicepIdentifier($"{prefix}_reader_ra")) + { + Name = rgReaderRaName, + PrincipalType = RoleManagementPrincipalType.ServicePrincipal, + PrincipalId = contributorIdentity.PrincipalId, + RoleDefinitionId = rgReaderRaId + }; + + infra.Add(rgReaderRa); + var webSite = new WebSite("webapp") { // Use the host name as the name of the web app diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsEnvironmentResource.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsEnvironmentResource.verified.bicep index ce653d8f830..ad39fe1a847 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsEnvironmentResource.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsEnvironmentResource.verified.bicep @@ -49,6 +49,15 @@ resource env_contributor_mi 'Microsoft.ManagedIdentity/userAssignedIdentities@20 location: location } +resource env_reader_ra 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(resourceGroup().id, env_contributor_mi.id, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')) + properties: { + principalId: env_contributor_mi.properties.principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') + principalType: 'ServicePrincipal' + } +} + resource webapp 'Microsoft.Web/sites@2024-11-01' = { name: take('${toLower('env')}-${toLower('aspiredashboard')}-${uniqueString(resourceGroup().id)}', 60) location: location @@ -131,4 +140,4 @@ output AZURE_WEBSITE_CONTRIBUTOR_MANAGED_IDENTITY_ID string = env_contributor_mi output AZURE_WEBSITE_CONTRIBUTOR_MANAGED_IDENTITY_PRINCIPAL_ID string = env_contributor_mi.properties.principalId -output AZURE_APP_SERVICE_DASHBOARD_URI string = 'https://${take('${toLower('env')}-${toLower('aspiredashboard')}-${uniqueString(resourceGroup().id)}', 60)}.azurewebsites.net' +output AZURE_APP_SERVICE_DASHBOARD_URI string = 'https://${take('${toLower('env')}-${toLower('aspiredashboard')}-${uniqueString(resourceGroup().id)}', 60)}.azurewebsites.net' \ No newline at end of file diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceEnvironmentCanReferenceExistingAppServicePlan.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceEnvironmentCanReferenceExistingAppServicePlan.verified.bicep index ce653d8f830..a39e85c0c68 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceEnvironmentCanReferenceExistingAppServicePlan.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceEnvironmentCanReferenceExistingAppServicePlan.verified.bicep @@ -49,6 +49,15 @@ resource env_contributor_mi 'Microsoft.ManagedIdentity/userAssignedIdentities@20 location: location } +resource env_reader_ra 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(resourceGroup().id, env_contributor_mi.id, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')) + properties: { + principalId: env_contributor_mi.properties.principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') + principalType: 'ServicePrincipal' + } +} + resource webapp 'Microsoft.Web/sites@2024-11-01' = { name: take('${toLower('env')}-${toLower('aspiredashboard')}-${uniqueString(resourceGroup().id)}', 60) location: location From 378a9d72848367029af7dca9f76ee75f35355487 Mon Sep 17 00:00:00 2001 From: Shilpi Rachna Date: Mon, 6 Oct 2025 11:22:57 -0700 Subject: [PATCH 16/17] Update src/Aspire.Hosting.Azure.AppService/AzureAppServiceWebsiteContext.cs Co-authored-by: Eric Erhardt --- .../AzureAppServiceWebsiteContext.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceWebsiteContext.cs b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceWebsiteContext.cs index df4a9045049..9d888edf364 100644 --- a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceWebsiteContext.cs +++ b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceWebsiteContext.cs @@ -397,7 +397,7 @@ private RoleAssignment AddDashboardPermissionAndSettings(WebSite webSite, Provis webSite.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "OTEL_COLLECTOR_URL", Value = dashboardUri }); webSite.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "OTEL_CLIENT_ID", Value = acrClientIdParameter }); - // Add Website Contributor role assignment to dashboard's managed identity forthis webapp + // Add Website Contributor role assignment to dashboard's managed identity for this webapp var websiteRaId = BicepFunction.GetSubscriptionResourceId( "Microsoft.Authorization/roleDefinitions", "de139f84-1756-47ae-9be6-808fbbe84772"); From c39fac69a08e9cf912c37f6c1df8793e5701d842 Mon Sep 17 00:00:00 2001 From: Shilpi Rachna Date: Mon, 6 Oct 2025 11:31:14 -0700 Subject: [PATCH 17/17] Taking a nit change --- .../AzureAppServiceEnvironmentUtility.cs | 40 +++++++++---------- ...mentAddsEnvironmentResource.verified.bicep | 4 +- ...renceExistingAppServicePlan.verified.bicep | 4 +- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentUtility.cs b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentUtility.cs index 6b99e139473..4e7eddfe673 100644 --- a/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentUtility.cs +++ b/src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentUtility.cs @@ -35,22 +35,22 @@ public static WebSite AddDashboard(AzureResourceInfrastructure infra, infra.Add(contributorIdentity); // Add Reader role assignment - var rgReaderRaId = BicepFunction.GetSubscriptionResourceId( + var rgRaId = BicepFunction.GetSubscriptionResourceId( "Microsoft.Authorization/roleDefinitions", "acdd72a7-3385-48ef-bd42-f606fba81ae7"); - var rgReaderRaName = BicepFunction.CreateGuid(BicepFunction.GetResourceGroup().Id, contributorIdentity.Id, rgReaderRaId); + var rgRaName = BicepFunction.CreateGuid(BicepFunction.GetResourceGroup().Id, contributorIdentity.Id, rgRaId); - var rgReaderRa = new RoleAssignment(Infrastructure.NormalizeBicepIdentifier($"{prefix}_reader_ra")) + var rgRa = new RoleAssignment(Infrastructure.NormalizeBicepIdentifier($"{prefix}_ra")) { - Name = rgReaderRaName, + Name = rgRaName, PrincipalType = RoleManagementPrincipalType.ServicePrincipal, PrincipalId = contributorIdentity.PrincipalId, - RoleDefinitionId = rgReaderRaId + RoleDefinitionId = rgRaId }; - infra.Add(rgReaderRa); + infra.Add(rgRa); - var webSite = new WebSite("webapp") + var dashboard = new WebSite("dashboard") { // Use the host name as the name of the web app Name = GetDashboardHostName(infra.AspireResource.Name), @@ -78,25 +78,25 @@ public static WebSite AddDashboard(AzureResourceInfrastructure infra, }; var contributorMid = BicepFunction.Interpolate($"{contributorIdentity.Id}").Compile().ToString(); - webSite.Identity.UserAssignedIdentities[contributorMid] = new UserAssignedIdentityDetails(); + dashboard.Identity.UserAssignedIdentities[contributorMid] = new UserAssignedIdentityDetails(); // Security is handled by app service platform - webSite.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "Dashboard__Frontend__AuthMode", Value = "Unsecured" }); - webSite.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "Dashboard__Otlp__AuthMode", Value = "Unsecured" }); - webSite.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "Dashboard__Otlp__SuppressUnsecuredTelemetryMessage", Value = "true" }); - webSite.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "Dashboard__ResourceServiceClient__AuthMode", Value = "Unsecured" }); + dashboard.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "Dashboard__Frontend__AuthMode", Value = "Unsecured" }); + dashboard.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "Dashboard__Otlp__AuthMode", Value = "Unsecured" }); + dashboard.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "Dashboard__Otlp__SuppressUnsecuredTelemetryMessage", Value = "true" }); + dashboard.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "Dashboard__ResourceServiceClient__AuthMode", Value = "Unsecured" }); // Dashboard ports - webSite.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "WEBSITES_PORT", Value = "5000" }); - webSite.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "HTTP20_ONLY_PORT", Value = "4317" }); + dashboard.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "WEBSITES_PORT", Value = "5000" }); + dashboard.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "HTTP20_ONLY_PORT", Value = "4317" }); // Enable SCM preloading to ensure dashboard is always available - webSite.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "WEBSITE_START_SCM_WITH_PRELOAD", Value = "true" }); + dashboard.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "WEBSITE_START_SCM_WITH_PRELOAD", Value = "true" }); // Appsettings related to managed identity for auth - webSite.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "AZURE_CLIENT_ID", Value = contributorIdentity.ClientId }); - webSite.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "ALLOWED_MANAGED_IDENTITIES", Value = otelClientId }); + dashboard.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "AZURE_CLIENT_ID", Value = contributorIdentity.ClientId }); + dashboard.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "ALLOWED_MANAGED_IDENTITIES", Value = otelClientId }); // Added appsetting to identify the resources in a specific aspire environment - webSite.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "ASPIRE_ENVIRONMENT_NAME", Value = infra.AspireResource.Name }); + dashboard.SiteConfig.AppSettings.Add(new AppServiceNameValuePair { Name = "ASPIRE_ENVIRONMENT_NAME", Value = infra.AspireResource.Name }); - infra.Add(webSite); + infra.Add(dashboard); // Outputs needed by the app service environment // This identity needs website contributor access on the websites for resource server to work @@ -110,6 +110,6 @@ public static WebSite AddDashboard(AzureResourceInfrastructure infra, Value = contributorIdentity.PrincipalId }); - return webSite; + return dashboard; } } diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsEnvironmentResource.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsEnvironmentResource.verified.bicep index ad39fe1a847..b102149cb31 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsEnvironmentResource.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddContainerAppEnvironmentAddsEnvironmentResource.verified.bicep @@ -49,7 +49,7 @@ resource env_contributor_mi 'Microsoft.ManagedIdentity/userAssignedIdentities@20 location: location } -resource env_reader_ra 'Microsoft.Authorization/roleAssignments@2022-04-01' = { +resource env_ra 'Microsoft.Authorization/roleAssignments@2022-04-01' = { name: guid(resourceGroup().id, env_contributor_mi.id, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')) properties: { principalId: env_contributor_mi.properties.principalId @@ -58,7 +58,7 @@ resource env_reader_ra 'Microsoft.Authorization/roleAssignments@2022-04-01' = { } } -resource webapp 'Microsoft.Web/sites@2024-11-01' = { +resource dashboard 'Microsoft.Web/sites@2024-11-01' = { name: take('${toLower('env')}-${toLower('aspiredashboard')}-${uniqueString(resourceGroup().id)}', 60) location: location properties: { diff --git a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceEnvironmentCanReferenceExistingAppServicePlan.verified.bicep b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceEnvironmentCanReferenceExistingAppServicePlan.verified.bicep index a39e85c0c68..f90c40fe14f 100644 --- a/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceEnvironmentCanReferenceExistingAppServicePlan.verified.bicep +++ b/tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AzureAppServiceEnvironmentCanReferenceExistingAppServicePlan.verified.bicep @@ -49,7 +49,7 @@ resource env_contributor_mi 'Microsoft.ManagedIdentity/userAssignedIdentities@20 location: location } -resource env_reader_ra 'Microsoft.Authorization/roleAssignments@2022-04-01' = { +resource env_ra 'Microsoft.Authorization/roleAssignments@2022-04-01' = { name: guid(resourceGroup().id, env_contributor_mi.id, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')) properties: { principalId: env_contributor_mi.properties.principalId @@ -58,7 +58,7 @@ resource env_reader_ra 'Microsoft.Authorization/roleAssignments@2022-04-01' = { } } -resource webapp 'Microsoft.Web/sites@2024-11-01' = { +resource dashboard 'Microsoft.Web/sites@2024-11-01' = { name: take('${toLower('env')}-${toLower('aspiredashboard')}-${uniqueString(resourceGroup().id)}', 60) location: location properties: {