Skip to content

Add App Service delegated subnet support - #18738

Merged
David Fowler (davidfowl) merged 20 commits into
mainfrom
davifowl-microsoft-verify-deployment-discussion
Jul 13, 2026
Merged

Add App Service delegated subnet support#18738
David Fowler (davidfowl) merged 20 commits into
mainfrom
davifowl-microsoft-verify-deployment-discussion

Conversation

@davidfowl

@davidfowl David Fowler (davidfowl) commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Description

Adds regional virtual network integration for Azure App Service environments, addressing the App Service configuration gap reported in discussion #18734. AppHost authors can now apply an Azure delegated subnet to an App Service environment, and Aspire configures workload sites, deployment slots, and the default dashboard consistently.

User-facing usage

#pragma warning disable ASPIREAZURE003 // Azure Virtual Network APIs are experimental.
var vnet = builder.AddAzureVirtualNetwork("vnet");
var subnet = vnet.AddSubnet("app-service-subnet", "10.0.0.0/24");

var appServiceEnvironment = builder.AddAzureAppServiceEnvironment("env")
    .WithDelegatedSubnet(subnet);
#pragma warning restore ASPIREAZURE003

The subnet is delegated to Microsoft.Web/serverFarms. This is regional VNet integration for outbound traffic only: it does not make site or dashboard ingress private and does not enable Route All.

Implementation details

Slot-enabled deployments preserve the production Microsoft.Web/sites resource with @onlyIfNotExists(). The change emits the required Microsoft.Web/sites/networkConfig child named virtualNetwork so an existing production site can be upgraded safely without bypassing that guard. A narrow custom Azure.Provisioning resource is used until the upstream library can generate the required fixed child resource name.

Validation

  • dotnet test --project tests/Aspire.Hosting.Azure.Tests/Aspire.Hosting.Azure.Tests.csproj --no-launch-profile -- --filter-class "*.AzureAppServiceTests" --filter-class "*.AzureVirtualNetworkExtensionsTests" --filter-not-trait "quarantined=true" --filter-not-trait "outerloop=true" (84 passed)
  • dotnet test --project tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests.csproj --no-launch-profile -- --filter-not-trait "quarantined=true" --filter-not-trait "outerloop=true" (99 passed)
  • The App Service TypeScript fixture restored the locally built App Service and Network SDK packages and type-checked the generated .withDelegatedSubnet(subnet) call.
  • Live Azure E2E ran the slot-enabled initial deployment, upgraded the same resource group with a delegated subnet, verified public staging-slot and dashboard endpoints, and confirmed the expected subnet on the production site, staging slot, and dashboard.

N/A - prompted by discussion #18734.

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
      • If yes, did you have an API Review for it?
        • Yes
        • No
      • Did you add <remarks /> and <code /> elements on your triple slash comments?
        • Yes
        • No
    • No
  • Does the change make any security assumptions or guarantees?
    • Yes
      • If yes, have you done a threat model and had a security review?
        • Yes
        • No
    • No

Configure App Service sites, slots, and the dashboard for regional VNet integration.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 25c353af-7555-4b46-ac6f-8e78f3cfeea1
Copilot AI review requested due to automatic review settings July 11, 2026 15:22
@github-actions

Copy link
Copy Markdown
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/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 18738

Or

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

@github-actions github-actions Bot added the area-integrations Issues pertaining to Aspire Integrations packages label Jul 11, 2026
@github-actions

This comment has been minimized.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds delegated-subnet support to Azure App Service deployment environments, including upgrade-safe VNet integration.

Changes:

  • Propagates delegated subnets to websites, slots, and the dashboard.
  • Emits an upgrade-safe networkConfig child resource.
  • Adds documentation, snapshots, unit tests, and Azure E2E coverage.
Show a summary per file
File Description
tests/...CanOverrideEnvironmentDelegatedSubnet.verified.bicep Verifies per-site subnet overrides.
tests/...AddAppServiceWithDelegatedSubnet.verified.bicep Captures complete generated infrastructure.
AzureVirtualNetworkExtensionsTests.cs Tests App Service delegation annotations.
AzureAppServiceTests.cs Tests Bicep generation and overrides.
Aspire.Deployment.EndToEnd.Tests.csproj Extends deployment-test timeouts.
AppServiceReactDeploymentTests.cs Tests live VNet upgrade behavior.
AzureVirtualNetworkExtensions.cs Documents App Service delegation.
README.md Documents regional VNet integration.
AzureAppServiceWebsiteContext.cs Applies subnet configuration to sites and slots.
AzureAppServiceEnvironmentResource.cs Adds delegated-subnet capability.
AzureAppServiceEnvironmentExtensions.cs Configures the dashboard subnet.
AspireSiteContainer.cs Adds the named networkConfig resource workaround.

Review details

  • Files reviewed: 12/12 changed files
  • Comments generated: 3
  • Review effort level: Medium

Comment thread src/Aspire.Hosting.Azure.AppService/README.md
@davidfowl
David Fowler (davidfowl) marked this pull request as draft July 11, 2026 16:23
@davidfowl

Copy link
Copy Markdown
Contributor Author

PR Testing Report

PR Information

Artifact Version Verification

  • Expected commit: 0c50ce69a8bff26e5214f0b86f87c7dff708218e
  • Installed PR CLI: 13.5.0-pr.18738.g0c50ce69
  • Status: Verified. The installed dogfood CLI includes the current PR head's short SHA.

Scenarios Executed

Fresh AppHost from the PR artifact

Status: Passed

  1. Installed the PR dogfood CLI in an isolated Linux container.
  2. Created a fresh C# empty AppHost from the PR template hive.
  3. Added Aspire.Hosting.Azure.AppService and Aspire.Hosting.Azure.Network, both at 13.5.0-pr.18738.g0c50ce69.
  4. Built an AppHost that configures an Azure virtual network, delegated subnet, App Service environment, dashboard, staging slot, and external ASP.NET Core web project.

Generated Azure artifacts

Status: Passed

aspire publish completed successfully and generated deployment artifacts without an Azure deployment. Structural assertions confirmed:

  • The subnet delegation is Microsoft.Web/serverFarms.
  • The production web site and staging slot each reference the delegated subnet.
  • The App Service dashboard references the same delegated subnet.
  • The slot-enabled production site retains @onlyIfNotExists().
  • The production site also emits Microsoft.Web/sites/networkConfig named virtualNetwork, with subnetResourceId set to the delegated subnet. This is the upgrade-safe path for an existing production site.

Experimental API diagnostic and recovery

Status: Passed

  • The same configuration without #pragma warning disable ASPIREAZURE003 failed the build as expected and reported ASPIREAZURE003.
  • Adding the documented narrowly scoped disable/restore pair restored a clean build.

Local AppHost lifecycle

Status: Passed

Started the fresh AppHost with the PR CLI in isolated mode, waited for the web resource to reach up, retrieved its resource state, and stopped the AppHost cleanly.

Summary

Scenario Status
PR artifact identity Passed
Fresh AppHost scaffold and integration add Passed
Delegated-subnet Bicep generation Passed
Slot and dashboard subnet propagation Passed
Upgrade-safe networkConfig resource Passed
ASPIREAZURE003 negative case and recovery Passed
AppHost lifecycle smoke test Passed

Overall Result

PR artifact verified.

This report contains no Azure account, tenant, subscription, resource-group, hostname, credential, token, local-path, or raw-log information.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 11, 2026 19:18
@github-actions

This comment has been minimized.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review details

  • Files reviewed: 14/14 changed files
  • Comments generated: 2
  • Review effort level: Medium

Comment thread tests/Aspire.Deployment.EndToEnd.Tests/AppServiceReactDeploymentTests.cs Outdated
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 11, 2026 19:35
@github-actions

This comment has been minimized.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review details

  • Files reviewed: 14/14 changed files
  • Comments generated: 0 new
  • Review effort level: Low

Copilot AI review requested due to automatic review settings July 11, 2026 19:43

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review details

  • Files reviewed: 14/14 changed files
  • Comments generated: 2
  • Review effort level: Medium

Comment thread src/Aspire.Hosting.Azure.Network/AzureVirtualNetworkExtensions.cs
@davidfowl
David Fowler (davidfowl) marked this pull request as ready for review July 11, 2026 19:49
@github-actions

This comment has been minimized.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@github-actions

This comment has been minimized.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review details

  • Files reviewed: 16/16 changed files
  • Comments generated: 0 new
  • Review effort level: Medium

…nner

The two-phase App Service VNet test runs two consecutive 'aspire deploy'
commands in one terminal session. WaitForPipelineSuccessAsync matches the
'pipeline succeeded' banner anywhere on the visible screen, so the second
deploy's wait matched the first deploy's leftover banner immediately and
returned without waiting for the redeploy. The subsequent success-prompt
wait then had to absorb the entire second deployment and timed out.

Clear the terminal before the second deploy, mirroring the existing pattern
in CliTelemetryTests, so the pipeline wait only observes the redeploy's own
completion banner.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@mitchdenny

Copy link
Copy Markdown
Member

Pushed a fix for the AppServiceReactDeploymentTests deployment-test failure (commit 332f696).

What failed: In the /deployment-test run against the earlier HEAD, AppServiceStoragePrivateEndpointDeploymentTests passed cleanly (~8m40s), but DeployReactTemplateToAzureAppServiceWithDelegatedSubnet timed out at its final step (WaitForSuccessPromptAsync, 2m). The deploy itself was healthy — the terminal snapshot showed provision-server-website still actively deploying when the wait expired.

Root cause (test sync, not a product bug): The two-phase test runs two consecutive aspire deploy commands in the same terminal. WaitForPipelineSuccessAsync matches the pipeline succeeded banner anywhere on the visible screen. After the first deploy that banner is still on screen, so the second deploy's WaitForPipelineSuccessAsync matched the stale banner and returned in ~3ms instead of waiting for the redeploy. That pushed the entire second deployment onto the following WaitForSuccessPromptAsync(2m), which can't cover a full App Service redeploy — hence the timeout. The Hex1b step trace confirms it: step [53] (first deploy pipeline wait) took 2:20, but step [60] (second deploy pipeline wait) returned in 3ms, then step [61] timed out at 2:00.

Fix: Clear the terminal before the second deploy (mirroring the existing clear pattern in CliTelemetryTests) so WaitForPipelineSuccessAsync only observes the redeploy's own completion banner. The storage test isn't affected because it deploys only once.

Worth re-running /deployment-test on the new HEAD to confirm both App Service tests are green together.

@mitchdenny

Copy link
Copy Markdown
Member

/deployment-test

@github-actions

Copy link
Copy Markdown
Contributor

🚀 Deployment tests starting on PR #18738...

This will deploy to real Azure infrastructure. Results will be posted here when complete.

View workflow run

@github-actions

This comment has been minimized.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review details

  • Files reviewed: 16/16 changed files
  • Comments generated: 1
  • Review effort level: Medium

Comment thread src/Aspire.Hosting.Azure.Network/AzureVirtualNetworkExtensions.cs Outdated
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@github-actions

This comment has been minimized.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review details

  • Files reviewed: 16/16 changed files
  • Comments generated: 2
  • Review effort level: Medium

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review details

  • Files reviewed: 16/16 changed files
  • Comments generated: 1
  • Review effort level: Low

@github-actions

This comment has been minimized.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown
Contributor

Tests selector (audit mode)

The full test matrix and all jobs still run in audit mode. The tests and jobs below are what selective CI would run under enforcement.

5 / 100 test projects · 4 jobs, from 15 changed files.

Selected test projects (5 / 100)

Aspire.Deployment.EndToEnd.Tests, Aspire.Hosting.Azure.Kubernetes.Tests, Aspire.Hosting.Azure.Tests, Aspire.Hosting.CodeGeneration.TypeScript.Tests, Aspire.Playground.Tests

Selected jobs (4)

deployment-e2e, extension-e2e, polyglot, typescript-api-compat


How these were chosen — grouped by what changed

🔧 src/Aspire.Hosting.Azure.AppService/AspireSiteContainer.cs (changed source)
1 directly: Aspire.Hosting.Azure.Tests
1 via the project graph: Aspire.Hosting.CodeGeneration.TypeScript.Tests

🔧 src/Aspire.Hosting.Azure.Network/AzureVirtualNetworkExtensions.cs (changed source)
1 directly: Aspire.Hosting.Azure.Tests
1 via the project graph: Aspire.Playground.Tests (3 hops)

🧪 tests/Aspire.Hosting.Azure.Tests/AzureAppServiceTests.cs (changed test)
1 directly: Aspire.Hosting.Azure.Tests
1 via the project graph: Aspire.Hosting.Azure.Kubernetes.Tests

🔧 src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentExtensions.cs (changed source)
1 directly: Aspire.Hosting.Azure.Tests

🔧 src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentResource.cs (changed source)
1 directly: Aspire.Hosting.Azure.Tests

🔧 src/Aspire.Hosting.Azure.AppService/AzureAppServiceWebsiteContext.cs (changed source)
1 directly: Aspire.Hosting.Azure.Tests

🧪 tests/Aspire.Deployment.EndToEnd.Tests/AppServiceReactDeploymentTests.cs (changed test)
1 directly: Aspire.Deployment.EndToEnd.Tests

🧪 tests/Aspire.Deployment.EndToEnd.Tests/AppServiceStoragePrivateEndpointDeploymentTests.cs (changed test)
1 directly: Aspire.Deployment.EndToEnd.Tests

🧪 tests/Aspire.Deployment.EndToEnd.Tests/Aspire.Deployment.EndToEnd.Tests.csproj (changed test)
1 directly: Aspire.Deployment.EndToEnd.Tests

🧪 tests/Aspire.Hosting.Azure.Tests/AzureVirtualNetworkExtensionsTests.cs (changed test)
1 directly: Aspire.Hosting.Azure.Tests

🧪 tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddAppServiceWithDelegatedSubnet.verified.bicep (changed test)
1 directly: Aspire.Hosting.Azure.Tests

🧪 tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.AddAppServiceWithDelegatedSubnetWithoutDeploymentSlot.verified.bicep (changed test)
1 directly: Aspire.Hosting.Azure.Tests

🧪 tests/Aspire.Hosting.Azure.Tests/Snapshots/AzureAppServiceTests.PublishAsAzureAppServiceWebsite_CanOverrideEnvironmentDelegatedSubnet.verified.bicep (changed test)
1 directly: Aspire.Hosting.Azure.Tests

Job reasons

Job Triggered by
deployment-e2e tests/Aspire.Deployment.EndToEnd.Tests/AppServiceReactDeploymentTests.cs, tests/Aspire.Deployment.EndToEnd.Tests/AppServiceStoragePrivateEndpointDeploymentTests.cs, tests/Aspire.Deployment.EndToEnd.Tests/Aspire.Deployment.EndToEnd.Tests.csproj
• affected project Aspire.Hosting.Azure.AppService
extension-e2e src/Aspire.Hosting.Azure.AppService/AspireSiteContainer.cs, src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentExtensions.cs, src/Aspire.Hosting.Azure.AppService/AzureAppServiceEnvironmentResource.cs, src/Aspire.Hosting.Azure.AppService/AzureAppServiceWebsiteContext.cs, src/Aspire.Hosting.Azure.Network/AzureVirtualNetworkExtensions.cs
• affected project Aspire.Hosting.Azure.AppService
polyglot tests/PolyglotAppHosts/Aspire.Hosting.Azure.AppService/TypeScript/apphost.mts, tests/PolyglotAppHosts/Aspire.Hosting.Azure.AppService/TypeScript/aspire.config.json
typescript-api-compat affected project Aspire.Hosting.Azure.AppService

Selection computed for commit a1615bd.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review details

  • Files reviewed: 16/16 changed files
  • Comments generated: 0 new
  • Review effort level: Medium

@mitchdenny Mitch Denny (mitchdenny) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Approving after a full review pass.

Reviewed the shared Aspire.Hosting.Azure.Network change closely since it's a common library: WithDelegatedSubnet<T> is generalized (not App-Service-specific) and hardened with idempotency + conflict guards (one subnet per resource, one delegation service per subnet) using ResourceAnnotationMutationBehavior.Replace, plus case-insensitive delegation comparison. No App Service leakage into the shared API.

App Service side: environment implements IAzureDelegatedSubnetResource (Microsoft.Web/serverFarms), and the subnet is applied to the dashboard site, each website, and its slot. The @onlyIfNotExists() + Microsoft.Web/sites/networkConfig (virtualNetwork) interplay is the highest-risk area but is correctly reasoned, well-commented, and verified on the production-upgrade path. The AspireSiteNetworkConfig BCP035 workaround is properly link-tracked (Azure/azure-sdk-for-net#54629).

README correctly documents that regional VNet integration is outbound-only. Both App Service deployment E2E tests (React two-phase upgrade + storage private-endpoint connectivity) passed together against real Azure on current HEAD.

Two follow-ups are already captured: the throw-on-re-delegation API-design consideration (author confirmed intentional) and the pre-existing App Service site-name tail-truncation weakness (#18750, not caused by this PR).

@aspire-repo-bot

This comment has been minimized.

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ CI Failure Analysis: Possible Flaky Test(s)

The CI build failed due to test failure(s) that appear unrelated to the PR changes. These may be flaky tests.

Suspected flaky test(s):

  • Aspire.Hosting.Docker.Tests.DockerComposeTests.DeployWithDashboard_PrintsDashboardAndServiceEndpoints in job Tests / Hosting.Docker / Hosting.Docker (ubuntu-latest)
    • Error: Assert.Contains() Failure: Filter not matched in collection
      Collection: []
    • Stack Trace (first frames):
      at Aspire.Hosting.Docker.Tests.DockerComposeTests.DeployWithDashboard_PrintsDashboardAndServiceEndpoints() in /home/runner/work/aspire/aspire/tests/Aspire.Hosting.Docker.Tests/DockerComposeTests.cs:line 460
         at Aspire.Hosting.Docker.Tests.DockerComposeTests.DeployWithDashboard_PrintsDashboardAndServiceEndpoints() in /home/runner/work/aspire/aspire/tests/Aspire.Hosting.Docker.Tests/DockerComposeTests.cs:line 500
      
    • Why likely flaky: The test failed because Docker could not bind port 0.0.0.0:55216 (address already in use on CI runner). This is an environmental port conflict — the test is in Aspire.Hosting.Docker.Tests which is unrelated to the PR's Azure App Service delegated subnet changes. Matches prior cause 'docker-compose-port-already-in-use'.

Suggested actions:

  • Re-run the failed CI jobs to confirm if the failure is intermittent
  • If the test continues to fail, consider quarantining it using /quarantine-test <test name> <issue URL>
  • Search existing issues to see if this test is already known to be flaky

You can re-run the failed jobs from the workflow run page.

@aspire-repo-bot

Copy link
Copy Markdown
Contributor

📝 Documentation has been drafted in microsoft/aspire.dev#1441 targeting release/13.5.

Drafted a docs PR updating azure-app-service-host.mdx (new "Regional virtual network integration" section with WithDelegatedSubnet C#/TypeScript examples) and azure-virtual-network.mdx (new "Delegate subnets to compute environments" subsection covering both ACA and App Service). Triggered signals: integration_readme_changed, pr_body_has_user_facing_section, pr_body_has_cli_flag_mention.

Note

This draft PR needs human review before merging.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-integrations Issues pertaining to Aspire Integrations packages

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants