You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Provision Microsoft Entra registrations using Azure.Provisioning - #20937
Allow C# AppHosts to create Microsoft Entra application registrations and service principals through Aspire's Azure provisioning infrastructure. AddEntraIdApplication(...).AsExistingApplication(...) references an existing registration while retaining the normal Azure provisioning setup. The new top-level AddExistingEntraApplication(...) provides a configuration-only reference without enabling Azure provisioning or requiring Graph access. Both existing-reference APIs accept literal IDs or parameter resources. Builds on merged #14668.
This is the registration-creation foundation, not the complete web-to-API authentication workflow. Automatic redirect URI discovery, credential creation/rotation, API scopes/permissions/consent, and federated identity trust provisioning remain follow-ups. The integration is currently C#-only. No separate issue is closed by this PR.
Created registrations supply client and home-tenant IDs through existing Bicep output references. No implicit readiness/health wait or Azure controller failure/retry refactor is introduced. Caller-added waits remain intact.
Registration settings are emitted directly from resource properties. WithIdTokenIssuance() enables implicit/hybrid ID-token issuance when needed; it is disabled by default and unnecessary for authorization-code-only flows. Callback URIs must be supplied explicitly.
The generated Entra module has only the tenantResourceSuffix deployment parameter. It has no location or owner-principal parameter and no explicit owner assignments. Microsoft Entra's default ownership behavior applies. This avoids requiring an application/RBAC principalId when native aspire deploy provisions the registration. Explicit owner configuration is tracked separately in #21030.
Provisioning and publishing omit implicit location arguments for generated modules that do not declare location; other modules retain existing behavior. Entra ARM deployments remain resource-group scoped. Explicit subscription/tenant deployment support is not included.
Naming, provisioning, and cleanup
The Azure provisioning dialog and Change Azure context offer an optional tenant-resource suffix for participating provisioned resources. It is persisted, restored, and clearable; Azure:TenantResourceSuffix supplies its initial value.
Empty suffix uses the resource name; dev-a produces entra-web-dev-a. Logical Aspire names and consumer configuration prefixes remain unchanged. Directory identity is determined by tenant/name/suffix, not the resource group.
Published templates expose the suffix with an empty default rather than embedding a developer's local setting. Explicit module values take precedence. Direct deployment binds it from Azure configuration. Changing it invalidates cached outputs, targets another registration, and leaves prior objects intact.
Generate Graph application/service-principal resources and outputs through Azure.Provisioning 1.6.0. Experimental BicepExtensions declarations use SDK escaping and pin br:mcr.microsoft.com/bicep/extensions/microsoftgraph/v1.0:1.0.0. Bicep 0.36.1+ is required; normal compilation restores the extension. Native SDK support is requested in Azure/azure-sdk-for-net#63848.
Experimental TenantScopedResourceAnnotation identifies tenant-wide non-ARM objects and carries cleanup identification outputs. It is not ARM deployment-scope metadata.
Retain Entra reprovision/cancel/local-state commands while excluding ARM inspection/location/deletion and drift probes.
Allow environment resource-group deletion in mixed models. Preserve directory outputs/state/links and report remaining names/types/object IDs/tenant IDs; reset ordinary ARM state. Reports use stored outputs, not live Graph verification; missing IDs remain unknown.
Existing references do not mutate registrations. Creation requires Azure deployment and appropriate Graph permissions. No API permissions, credentials, or tenant consent are created automatically. WithFicMsi requires a user-assigned managed identity client ID and emits configuration only, not identity assignment or federation trust.
The playground supports existing/create-new modes selected in AppHost code, dashboard-driven provisioning setup, and a manifest launch profile. Its existing-only mode uses AddExistingEntraApplication without enabling Azure provisioning. Generated local artifacts are not committed.
Generic manifest scalar/deferred fixes, publisher typed/deferred fixes, automatic parameter-type inference, and EvaluatedParameters were reverted as outside this feature's scope. The attempted readiness/controller refactor was also reverted. Shared parameter changes are limited to the agreed suffix handling and omission of undeclared location arguments. The configuration-only factory does not change Azure core registration or pipeline behavior.
Latest existing-reference validation — c41e812914
Validation used a task-owned temporary output directory; $output is its full path.
The regression tests execute an existing-only deploy pipeline for both literal and parameter factory overloads, verify that Azure authentication/context steps are not selected, and fail if Azure credentials are requested. They also cover preservation of the fluent API's Azure setup, application pipeline customizations, and mixed Azure/created-Entra models in both addition orders. Azure core files are unchanged by this factory addition.
The earlier native deployment E2E run at 57f9767b8e passed with 1 executed, 1 passed, and 0 skipped. It covered owner-omitted application/service-principal creation, the output-backed consumer environment and live HTTPS response, stable IDs after a redirect-URI update, and separate directory/resource-group cleanup. The CI identity's retrieved Graph application grants contained Application.ReadWrite.OwnedBy, not Application.ReadWrite.All. This live run predates the new top-level factory; no new live deployment at c41e812914 is claimed.
Earlier source validation — 410e03a55e
Validation used a task-owned temporary output directory to avoid interfering with developer processes. $output is the directory created with Directory.CreateTempSubdirectory(...); it was removed afterward.
az bicep build --file <temporary-main.bicep> --outfile <temporary-main.json> --only-show-errors
Passed for owner-free parent/Graph/consumer artifacts; compiled Graph parameter shape contains only tenantResourceSuffix
git diff --check and git diff --cached --check
Passed before publication
Separate publisher/manifest snapshots cover owner/principal removal. Basic Bicep generation is mode-independent and uses a single fact rather than duplicate run/publish snapshots.
At preceding head b8914b3478, the following command also passed 233 affected Azure tests: dotnet test --project .\tests\Aspire.Hosting.Azure.Tests\Aspire.Hosting.Azure.Tests.csproj --no-restore --no-launch-profile -p:BuildInParallel=false "-p:OutDir=$($output.FullName)\" -- --filter-class "*.AzureBicepProvisionerTests" --filter-class "*.AzureProvisioningResourceTests" --filter-class "*.AzureEnvironmentResourceTests" --filter-class "*.AzureInfrastructureExtensionsTests" --filter-class "*.AzureEnvironmentResourceExtensionsTests" --filter-class "*.ProvisioningContextProviderTests" --filter-not-trait "quarantined=true" --filter-not-trait "outerloop=true". Shared Azure provisioning code is unchanged by the owner-removal commit.
Existing transitive NuGet audit advisories are emitted. No owner-removal-head native AOT/full-solution build or native aspire deploy live success is claimed. The locally patched Release DLL successfully published the retained test AppHost without an Entra principal parameter. Current GitHub checks must be read from the PR; local validation does not imply CI is green. Formal API/security review remains outstanding.
Earlier live and artifact evidence
Matching b8914b3478 dogfood artifacts were verified through real Graph creation/settings/ownership, a healthy .NET consumer and dashboard, cached restart without redeployment, stable-ID callback updates, unmodified published parent/nested deployment with a real output-backed consumer, and suffix isolation followed by restoration of the original identity. User-requested test apps and cloud resources were retained privately for inspection.
Those live scenarios explicitly assigned owners and do not establish owner-omitted creation/update behavior or successful native aspire deploy. The later user-reported native deploy principal-resolution failure motivated removing default owner assignment and its parameter.
Earlier artifact testing at 8a8164c5 also covered eight fresh projects and ten approved scenarios; those task-owned resources were cleaned up. Neither historical run substitutes for testing subsequent revisions. Live destructive cleanup reporting and completed interactive browser authentication remain unverified at the current revision. Private tenant/resource/log evidence is not uploaded.
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?
Full managed solution build completed with zero warnings/errors. All 207 Entra tests and 98 affected Azure tests passed. Real AppHost provisioning, stable-ID updates/cache reuse, existing-only and safe failure paths, published nested deployment/output propagation, and task-owned cleanup were verified.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 68b00e03-418b-4839-88a1-d0ebfc318f60
Keep native Boolean and integer values in manifests, include callback-returned dependencies, and evaluate module callbacks once before mapping their references and serializing their values. Reject unsupported values instead of silently emitting empty strings. Add focused regressions and document the deferred parameter contract.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 68b00e03-418b-4839-88a1-d0ebfc318f60
This adds real deployment behavior through the Microsoft Graph Bicep extension, but the committed coverage only validates generated Bicep/manifests; there is no deployment E2E test under tests/Aspire.Deployment.EndToEnd.Tests. A repeatable test should deploy the application and service principal, verify the outputs and stable update behavior, and clean up both directory objects. The manual validation in the PR description will not catch future extension/schema/auth regressions in CI.
Preserve the fluent existing-registration API and add a top-level factory that does not enable Azure provisioning.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 68b00e03-418b-4839-88a1-d0ebfc318f60
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Allow C# AppHosts to create Microsoft Entra application registrations and service principals through Aspire's Azure provisioning infrastructure.
AddEntraIdApplication(...).AsExistingApplication(...)references an existing registration while retaining the normal Azure provisioning setup. The new top-levelAddExistingEntraApplication(...)provides a configuration-only reference without enabling Azure provisioning or requiring Graph access. Both existing-reference APIs accept literal IDs or parameter resources. Builds on merged #14668.This is the registration-creation foundation, not the complete web-to-API authentication workflow. Automatic redirect URI discovery, credential creation/rotation, API scopes/permissions/consent, and federated identity trust provisioning remain follow-ups. The integration is currently C#-only. No separate issue is closed by this PR.
Usage and behavior
Created registrations supply client and home-tenant IDs through existing Bicep output references. No implicit readiness/health wait or Azure controller failure/retry refactor is introduced. Caller-added waits remain intact.
Registration settings are emitted directly from resource properties.
WithIdTokenIssuance()enables implicit/hybrid ID-token issuance when needed; it is disabled by default and unnecessary for authorization-code-only flows. Callback URIs must be supplied explicitly.The generated Entra module has only the
tenantResourceSuffixdeployment parameter. It has nolocationor owner-principal parameter and no explicit owner assignments. Microsoft Entra's default ownership behavior applies. This avoids requiring an application/RBACprincipalIdwhen nativeaspire deployprovisions the registration. Explicit owner configuration is tracked separately in #21030.Provisioning and publishing omit implicit location arguments for generated modules that do not declare location; other modules retain existing behavior. Entra ARM deployments remain resource-group scoped. Explicit subscription/tenant deployment support is not included.
Naming, provisioning, and cleanup
Azure:TenantResourceSuffixsupplies its initial value.dev-aproducesentra-web-dev-a. Logical Aspire names and consumer configuration prefixes remain unchanged. Directory identity is determined by tenant/name/suffix, not the resource group.BicepExtensionsdeclarations use SDK escaping and pinbr:mcr.microsoft.com/bicep/extensions/microsoftgraph/v1.0:1.0.0. Bicep 0.36.1+ is required; normal compilation restores the extension. Native SDK support is requested in Azure/azure-sdk-for-net#63848.TenantScopedResourceAnnotationidentifies tenant-wide non-ARM objects and carries cleanup identification outputs. It is not ARM deployment-scope metadata.WithFicMsirequires a user-assigned managed identity client ID and emits configuration only, not identity assignment or federation trust.The playground supports existing/create-new modes selected in AppHost code, dashboard-driven provisioning setup, and a manifest launch profile. Its existing-only mode uses
AddExistingEntraApplicationwithout enabling Azure provisioning. Generated local artifacts are not committed.Generic manifest scalar/deferred fixes, publisher typed/deferred fixes, automatic parameter-type inference, and
EvaluatedParameterswere reverted as outside this feature's scope. The attempted readiness/controller refactor was also reverted. Shared parameter changes are limited to the agreed suffix handling and omission of undeclared location arguments. The configuration-only factory does not change Azure core registration or pipeline behavior.Latest existing-reference validation —
c41e812914Validation used a task-owned temporary output directory;
$outputis its full path.dotnet test --project .\tests\Aspire.Hosting.Azure.EntraId.Tests\Aspire.Hosting.Azure.EntraId.Tests.csproj --no-restore --no-launch-profile -p:BuildInParallel=false "-p:OutDir=$output\" --verbosity quiet -- --filter-not-trait "quarantined=true" --filter-not-trait "outerloop=true"dotnet build .\playground\EntraId\EntraId.AppHost\EntraId.AppHost.csproj --no-restore -p:SkipNativeBuild=true -p:BuildInParallel=false "-p:OutDir=$output\playground\" --verbosity quietThe regression tests execute an existing-only
deploypipeline for both literal and parameter factory overloads, verify that Azure authentication/context steps are not selected, and fail if Azure credentials are requested. They also cover preservation of the fluent API's Azure setup, application pipeline customizations, and mixed Azure/created-Entra models in both addition orders. Azure core files are unchanged by this factory addition.The earlier native deployment E2E run at
57f9767b8epassed with 1 executed, 1 passed, and 0 skipped. It covered owner-omitted application/service-principal creation, the output-backed consumer environment and live HTTPS response, stable IDs after a redirect-URI update, and separate directory/resource-group cleanup. The CI identity's retrieved Graph application grants containedApplication.ReadWrite.OwnedBy, notApplication.ReadWrite.All. This live run predates the new top-level factory; no new live deployment atc41e812914is claimed.Earlier source validation —
410e03a55eValidation used a task-owned temporary output directory to avoid interfering with developer processes.
$outputis the directory created withDirectory.CreateTempSubdirectory(...); it was removed afterward.dotnet test --project .\tests\Aspire.Hosting.Azure.EntraId.Tests\Aspire.Hosting.Azure.EntraId.Tests.csproj --no-restore --no-launch-profile -p:BuildInParallel=false "-p:OutDir=$($output.FullName)\" -- --filter-not-trait "quarantined=true" --filter-not-trait "outerloop=true"dotnet build .\src\Aspire.Hosting.Azure.EntraId\Aspire.Hosting.Azure.EntraId.csproj -c Release --no-restore -p:SkipNativeBuild=true -p:BuildInParallel=false -m:1 --verbosity minimalaz bicep build --file <temporary-main.bicep> --outfile <temporary-main.json> --only-show-errorstenantResourceSuffixgit diff --checkandgit diff --cached --checkSeparate publisher/manifest snapshots cover owner/principal removal. Basic Bicep generation is mode-independent and uses a single fact rather than duplicate run/publish snapshots.
At preceding head
b8914b3478, the following command also passed 233 affected Azure tests:dotnet test --project .\tests\Aspire.Hosting.Azure.Tests\Aspire.Hosting.Azure.Tests.csproj --no-restore --no-launch-profile -p:BuildInParallel=false "-p:OutDir=$($output.FullName)\" -- --filter-class "*.AzureBicepProvisionerTests" --filter-class "*.AzureProvisioningResourceTests" --filter-class "*.AzureEnvironmentResourceTests" --filter-class "*.AzureInfrastructureExtensionsTests" --filter-class "*.AzureEnvironmentResourceExtensionsTests" --filter-class "*.ProvisioningContextProviderTests" --filter-not-trait "quarantined=true" --filter-not-trait "outerloop=true". Shared Azure provisioning code is unchanged by the owner-removal commit.Existing transitive NuGet audit advisories are emitted. No owner-removal-head native AOT/full-solution build or native
aspire deploylive success is claimed. The locally patched Release DLL successfully published the retained test AppHost without an Entra principal parameter. Current GitHub checks must be read from the PR; local validation does not imply CI is green. Formal API/security review remains outstanding.Earlier live and artifact evidence
Matching
b8914b3478dogfood artifacts were verified through real Graph creation/settings/ownership, a healthy .NET consumer and dashboard, cached restart without redeployment, stable-ID callback updates, unmodified published parent/nested deployment with a real output-backed consumer, and suffix isolation followed by restoration of the original identity. User-requested test apps and cloud resources were retained privately for inspection.Those live scenarios explicitly assigned owners and do not establish owner-omitted creation/update behavior or successful native
aspire deploy. The later user-reported native deploy principal-resolution failure motivated removing default owner assignment and its parameter.Earlier artifact testing at
8a8164c5also covered eight fresh projects and ten approved scenarios; those task-owned resources were cleaned up. Neither historical run substitutes for testing subsequent revisions. Live destructive cleanup reporting and completed interactive browser authentication remain unverified at the current revision. Private tenant/resource/log evidence is not uploaded.Checklist
<remarks />and<code />elements on your triple slash comments?