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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 22 additions & 2 deletions src/Aspire.Hosting.Azure/AzureResourcePreparer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public async Task BeforeStartAsync(DistributedApplicationModel appModel, Cancell
}

var options = provisioningOptions.Value;
if (!options.SupportsTargetedRoleAssignments)
if (!EnvironmentSupportsTargetedRoleAssignments(options))
{
// If the app infrastructure does not support targeted role assignments, then we need to ensure that
// there are no role assignment annotations in the app model because they won't be honored otherwise.
Expand Down Expand Up @@ -79,6 +79,13 @@ public async Task BeforeStartAsync(DistributedApplicationModel appModel, Cancell
return azureResources;
}

private bool EnvironmentSupportsTargetedRoleAssignments(AzureProvisioningOptions options)
{
// run mode always supports targeted role assignments
// publish mode only supports targeted role assignments if the environment supports it
return executionContext.IsRunMode || options.SupportsTargetedRoleAssignments;
}

private static void EnsureNoRoleAssignmentAnnotations(DistributedApplicationModel appModel)
{
foreach (var resource in appModel.Resources)
Expand All @@ -94,7 +101,7 @@ private async Task BuildRoleAssignmentAnnotations(DistributedApplicationModel ap
{
var globalRoleAssignments = new Dictionary<AzureProvisioningResource, HashSet<RoleDefinition>>();

if (!options.SupportsTargetedRoleAssignments)
if (!EnvironmentSupportsTargetedRoleAssignments(options))
{
// when the app infrastructure doesn't support targeted role assignments, just copy all the default role assignments to applied role assignments
foreach (var resource in azureResources.Select(r => r.AzureResource).OfType<AzureProvisioningResource>())
Expand Down Expand Up @@ -183,6 +190,19 @@ private async Task BuildRoleAssignmentAnnotations(DistributedApplicationModel ap
}
}
}

if (executionContext.IsRunMode)
{
// in RunMode, any Azure resources that are not referenced by a compute resource should have their default role assignments applied
foreach (var azureResource in azureResources.Select(r => r.AzureResource).OfType<AzureProvisioningResource>())
{
if (!globalRoleAssignments.TryGetValue(azureResource, out _) &&
azureResource.TryGetLastAnnotation<DefaultRoleAssignmentsAnnotation>(out var defaultRoleAssignments))
{
AppendGlobalRoleAssignments(globalRoleAssignments, azureResource, defaultRoleAssignments.Roles);
}
}
}
}

if (globalRoleAssignments.Count > 0)
Expand Down
20 changes: 15 additions & 5 deletions tests/Aspire.Hosting.Azure.Tests/AzureResourcePreparerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ namespace Aspire.Hosting.Azure.Tests;

public class AzureResourcePreparerTests(ITestOutputHelper output)
{
[Fact]
public void ThrowsExceptionsIfRoleAssignmentUnsupported()
[Theory]
[InlineData(DistributedApplicationOperation.Publish)]
[InlineData(DistributedApplicationOperation.Run)]
public async Task ThrowsExceptionsIfRoleAssignmentUnsupported(DistributedApplicationOperation operation)
{
using var builder = TestDistributedApplicationBuilder.Create();
using var builder = TestDistributedApplicationBuilder.Create(operation);

var storage = builder.AddAzureStorage("storage");

Expand All @@ -25,8 +27,16 @@ public void ThrowsExceptionsIfRoleAssignmentUnsupported()

var app = builder.Build();

var ex = Assert.Throws<InvalidOperationException>(app.Start);
Assert.Contains("role assignments", ex.Message);
if (operation == DistributedApplicationOperation.Publish)
{
var ex = Assert.Throws<InvalidOperationException>(app.Start);
Assert.Contains("role assignments", ex.Message);
}
else
{
await app.StartAsync();
// no exception is thrown in Run mode
}
}

[Theory]
Expand Down
Loading