Skip to content

Fix transitive Azure role assignments through WaitFor dependencies#14473

Merged
eerhardt merged 2 commits intorelease/13.2from
copilot/fix-transitive-role-assignment
Feb 12, 2026
Merged

Fix transitive Azure role assignments through WaitFor dependencies#14473
eerhardt merged 2 commits intorelease/13.2from
copilot/fix-transitive-role-assignment

Conversation

Copy link
Contributor

Copilot AI commented Feb 12, 2026

Description

CollectDependenciesFromValue in ResourceExtensions.cs was calling CollectAnnotationDependencies on resources discovered through env var/argument value traversal. This leaked WaitAnnotation (and parent/redirect) dependencies from referenced resources into the caller's dependency set — even in DirectOnly mode used by AzureResourcePreparer.GetAzureReferences.

Result: webfrontend → server (WaitFor cache) produced a spurious webfrontend-roles-cache.bicep.

var cache = builder.AddAzureManagedRedis("cache").RunAsContainer();

var server = builder.AddProject<Projects.Server>("server")
    .WithReference(cache).WaitFor(cache);

// webfrontend incorrectly got a role assignment to cache
var webfrontend = builder.AddProject<Projects.Server>("webfrontend")
    .WithReference(server).WaitFor(server);

Fix: Remove CollectAnnotationDependencies calls from CollectDependenciesFromValue. Annotation-based dependencies are already collected:

  • For the subject resource, via GatherDirectDependenciesAsyncCollectAnnotationDependencies
  • For transitive deps in Recursive mode, via the recursive loop in GetResourceDependenciesAsync

Checklist

  • Is this feature complete?
    • Yes. Ready to ship.
    • No. Follow-up changes expected.
  • Are you including unit tests for the changes and scenario tests if relevant?
    • Yes
    • No
  • Did you add public API?
    • Yes
    • No
  • Does the change make any security assumptions or guarantees?
    • Yes
    • No
  • Does the change require an update in our Aspire docs?
    • Yes
    • No

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • aka.ms
    • Triggering command: /usr/local/bin/bicep /usr/local/bin/bicep build /tmp/aspire-bicepOKDnzw/env-acr.module.bicep --stdout (dns block)
    • Triggering command: /usr/local/bin/bicep /usr/local/bin/bicep build /tmp/aspire-bicepfw1kQr/teststorage.module.bicep --stdout (dns block)
    • Triggering command: /usr/local/bin/bicep /usr/local/bin/bicep build /tmp/aspire-bicepDgmTIe/env.module.bicep --stdout (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Original prompt

This section details on the original issue you should resolve

<issue_title>Azure resources are getting found transtively through a backend if the backend has a</issue_title>
<issue_description>When I have an apphost with a webfrontend and a backend, with the backend having a WaitFor to an azure service, I'm getting a role assignment for the webfrontend to the azure service, even though there is no direct dependency.

var builder = DistributedApplication.CreateBuilder(args);

builder.AddAzureContainerAppEnvironment("env");

var cache = builder.AddAzureManagedRedis("cache")
    .RunAsContainer();

var server = builder.AddProject<Projects.ReproTransitive_Server>("server")
    .WithHttpHealthCheck("/health")
    .WithExternalHttpEndpoints()
    .WithReference(cache).WaitFor(cache);

var webfrontend = builder.AddProject<Projects.ReproTransitive_Server>("webfrontend")
    .WithReference(server)
    .WaitFor(server);

builder.Build().Run();

aspire publish that and I see:

Image

I shouldn't be getting a webfrontend-roles-cache.bicep since the webfrontend doesn't reference the cache.

I think the problem is in

private static void CollectDependenciesFromValue(object? value, HashSet<IResource> dependencies, HashSet<IResource> newDependencies, HashSet<object> visitedValues)
{
if (value is null || !visitedValues.Add(value))
{
return;
}
// Direct resource references
if (value is IResource resource)
{
if (dependencies.Add(resource))
{
newDependencies.Add(resource);
}
CollectAnnotationDependencies(resource, dependencies, newDependencies);
}
// Resource builder wrapping a resource
if (value is IResourceBuilder<IResource> resourceBuilder)
{
if (dependencies.Add(resourceBuilder.Resource))
{
newDependencies.Add(resourceBuilder.Resource);
}
CollectAnnotationDependencies(resourceBuilder.Resource, dependencies, newDependencies);
value = resourceBuilder.Resource;
}
// Recurse through IValueWithReferences
if (value is IValueWithReferences valueWithReferences)
{
foreach (var reference in valueWithReferences.References)
{
CollectDependenciesFromValue(reference, dependencies, newDependencies, visitedValues);
}
}

This shouldn't be collecting annotation dependencies on a resource when the ResourceDependencyDiscoveryMode is DirectOnly.
</issue_description>

<agent_instructions>Please add a new test that ensures this scenario works correctly. The server should have a roles assignment, but the webfrontend shouldn't.</agent_instructions>

Comments on the Issue (you are @copilot in this section)


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Remove CollectAnnotationDependencies calls from CollectDependenciesFromValue
to prevent WaitFor/parent/connection-string-redirect annotations from
referenced resources being included as direct dependencies of the caller.

Add tests verifying:
- DirectOnly mode excludes WaitFor deps from referenced resources
- WaitFor doesn't create transitive role assignments in Azure publish

Co-authored-by: eerhardt <8291187+eerhardt@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix transitive role assignment to Azure resources Fix transitive Azure role assignments through WaitFor dependencies Feb 12, 2026
Copilot AI requested a review from eerhardt February 12, 2026 20:11
@eerhardt eerhardt marked this pull request as ready for review February 12, 2026 20:40
@eerhardt eerhardt requested a review from mitchdenny as a code owner February 12, 2026 20:40
Copilot AI review requested due to automatic review settings February 12, 2026 20:40
@github-actions
Copy link
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 14473

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 14473"

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes a bug where Azure role assignments were incorrectly created for transitive dependencies through WaitFor relationships. When webfrontend referenced server (which had a WaitFor dependency on cache), the webfrontend incorrectly received role assignments to the cache resource.

Changes:

  • Removed incorrect CollectAnnotationDependencies calls from CollectDependenciesFromValue in ResourceExtensions.cs that were leaking transitive WaitFor/Parent/Redirect dependencies into DirectOnly dependency discovery mode
  • Added unit test verifying that DirectOnly mode excludes WaitFor dependencies from referenced resources
  • Added integration test confirming Azure role assignments are not created for transitive WaitFor dependencies

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
src/Aspire.Hosting/ApplicationModel/ResourceExtensions.cs Removed two CollectAnnotationDependencies calls from CollectDependenciesFromValue that incorrectly collected annotation-based dependencies (WaitFor, Parent, Redirect) from resources discovered through value traversal
tests/Aspire.Hosting.Tests/ResourceDependencyTests.cs Added test DirectOnlyDoesNotIncludeWaitForDependenciesFromReferencedResources verifying that DirectOnly mode correctly excludes transitive WaitFor dependencies
tests/Aspire.Hosting.Azure.Tests/RoleAssignmentTests.cs Added test WaitForDoesNotCreateTransitiveRoleAssignments verifying the exact bug scenario: server with cache dependency should get role assignment, but webfrontend referencing server should not

@karolz-ms
Copy link
Member

/backport to main

@github-actions
Copy link
Contributor

@eerhardt eerhardt merged commit da9cf85 into release/13.2 Feb 12, 2026
347 of 348 checks passed
@eerhardt eerhardt deleted the copilot/fix-transitive-role-assignment branch February 12, 2026 23:17
@dotnet-policy-service dotnet-policy-service bot added this to the 13.2 milestone Feb 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants