Skip to content
Draft
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
3 changes: 2 additions & 1 deletion docs/azureai/ai-integrations-compatibility-matrix.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,8 @@ The [Aspire.Hosting.GitHub.Models](https://www.nuget.org/packages/Aspire.Hosting
```csharp
var builder = DistributedApplication.CreateBuilder(args);

var chat = builder.AddGitHubModel("chat", "openai/gpt-4o-mini");
var model = GitHubModel.OpenAI.OpenAIGpt4oMini;
var chat = builder.AddGitHubModel("chat", model);

builder.AddProject<Projects.ExampleProject>()
.WithReference(chat);
Expand Down
18 changes: 12 additions & 6 deletions docs/azureai/azureai-foundry-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ var builder = DistributedApplication.CreateBuilder(args);

var foundry = builder.AddAzureAIFoundry("foundry");

var chat = foundry.AddDeployment("chat", "Phi-4", "1", "Microsoft");
var model = AIFoundryModel.Microsoft.Phi4;
var chat = foundry.AddDeployment("chat", model);

builder.AddProject<Projects.ExampleProject>()
.WithReference(chat)
Expand All @@ -74,17 +75,18 @@ builder.AddProject<Projects.ExampleProject>()
The preceding code:

- Adds an Azure AI Foundry resource named `foundry`.
- Adds an Azure AI Foundry deployment resource named `chat` with a model name of `Phi-4`. The model name must correspond to an [available model](/azure/ai-foundry/foundry-models/concepts/models) in the Azure AI Foundry service.
- Adds an Azure AI Foundry deployment resource named `chat` using the <xref:Aspire.Hosting.Azure.AIFoundryModel> constant for Phi-4. The model must correspond to an [available model](/azure/ai-foundry/foundry-models/concepts/models) in the Azure AI Foundry service.

> [!NOTE]
> The `format` parameter of the `AddDeployment(...)` method can be found in the Azure AI Foundry portal in the details page of the model, right after the `Quick facts` text.
> [!TIP]
> Use the strongly-typed <xref:Aspire.Hosting.Azure.AIFoundryModel> constants to avoid typos and ensure you're using valid model identifiers. These constants are grouped by publisher (for example, `AIFoundryModel.Microsoft.Phi4`, `AIFoundryModel.OpenAI.Gpt4o`).

### Configure deployment properties

You can customize deployment properties using the <xref:Aspire.Hosting.AzureAIFoundryExtensions.WithProperties*> method:

```csharp
var chat = foundry.AddDeployment("chat", "Phi-4", "1", "Microsoft")
var model = AIFoundryModel.Microsoft.Phi4;
var chat = foundry.AddDeployment("chat", model)
.WithProperties(deployment =>
{
deployment.SkuName = "Standard";
Expand Down Expand Up @@ -130,7 +132,8 @@ var builder = DistributedApplication.CreateBuilder(args);
var foundry = builder.AddAzureAIFoundry("foundry")
.RunAsFoundryLocal();

var chat = foundry.AddDeployment("chat", "phi-3.5-mini", "1", "Microsoft");
var model = AIFoundryModel.Local.Phi35Mini;
var chat = foundry.AddDeployment("chat", model);

builder.AddProject<Projects.ExampleProject>()
.WithReference(chat)
Expand All @@ -143,6 +146,9 @@ When the AppHost starts up, the local foundry service is also started. This requ

The <xref:Aspire.Hosting.AzureAIFoundryExtensions.RunAsFoundryLocal*> method configures the resource to run as an emulator. It downloads and loads the specified models locally. The method provides health checks for the local service and automatically manages the Foundry Local lifecycle.

> [!TIP]
> Use the strongly-typed `AIFoundryModel.Local` constants for local development models. These constants are specifically optimized for Foundry Local (for example, `AIFoundryModel.Local.Phi4Mini`, `AIFoundryModel.Local.DeepseekR17b`).

### Assign roles to resources

You can assign specific roles to resources that need to access the Azure AI Foundry service. Use the <xref:Aspire.Hosting.AzureAIFoundryExtensions.WithRoleAssignments*> method:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
---
title: "Breaking change - DefaultAzureCredential defaults to ManagedIdentityCredential on ACA and App Service"
description: "Learn about the breaking change in Aspire 13.0 where DefaultAzureCredential behavior is changed to only use ManagedIdentityCredential when deploying to Azure Container Apps and Azure App Service."
ms.date: 10/17/2025
ai-usage: ai-assisted
ms.custom: https://github.com/dotnet/docs-aspire/issues/5154
---

# DefaultAzureCredential defaults to ManagedIdentityCredential on ACA and App Service

In Aspire 13.0, when deploying to Azure Container Apps and Azure App Service, the default behavior of `DefaultAzureCredential` has been changed to only use `ManagedIdentityCredential`. This is accomplished by setting the `AZURE_TOKEN_CREDENTIALS` environment variable to ensure deterministic credential resolution.

## Version introduced

Aspire 13.0

## Previous behavior

Previously, `DefaultAzureCredential` used the full chain of identity providers by default. This meant that `EnvironmentCredential` and `WorkloadIdentityCredential` would be attempted before `ManagedIdentityCredential` when authenticating to Azure resources.

```csharp
// No explicit environment variable was set
// DefaultAzureCredential would try credentials in this order:
// 1. EnvironmentCredential
// 2. WorkloadIdentityCredential
// 3. ManagedIdentityCredential
// ... and others
```

## New behavior

Now `DefaultAzureCredential` only uses `ManagedIdentityCredential` when deploying to Azure Container Apps and Azure App Service. This is achieved by setting the `AZURE_TOKEN_CREDENTIALS` environment variable automatically.

```csharp
// The AZURE_TOKEN_CREDENTIALS environment variable is automatically set
// DefaultAzureCredential now only uses ManagedIdentityCredential
```

This change forces `DefaultAzureCredential` to behave in a deterministic manner and optimizes the underlying `ManagedIdentityCredential` for resilience.

## Type of breaking change

This is a [behavioral change](../categories.md#behavioral-change).

## Reason for change

This change enforces Azure SDK best practices for production environments. Using deterministic credentials improves both security and reliability by:

- Ensuring predictable authentication behavior
- Reducing potential authentication failures from trying multiple credential types
- Optimizing credential acquisition performance
- Following the principle of least privilege by using managed identities

For more information, see:

- [Use deterministic credentials in production environments](https://learn.microsoft.com/dotnet/azure/sdk/authentication/best-practices?tabs=aspdotnet#use-deterministic-credentials-in-production-environments)
- [GitHub PR dotnet/aspire#11832](https://github.com/dotnet/aspire/pull/11832)
- [Azure SDK resilience improvements](https://github.com/Azure/azure-sdk-for-net/pull/52545)

## Recommended action

If you were relying on `EnvironmentCredential` or `WorkloadIdentityCredential` in your application, you can choose one of the following options to revert to the old behavior or adapt your code:

### Option 1: Use specific credential types explicitly

Don't use `DefaultAzureCredential` in your application. Instead, explicitly use `EnvironmentCredential` or `WorkloadIdentityCredential` in production code.

### Option 2: Remove the environment variable in your deployment

Implement a `PublishAsAzureContainerApp` callback and remove the environment variable from the bicep:

```csharp
builder.AddProject<Projects.Frontend>("frontend")
.PublishAsAzureContainerApp((infra, app) =>
{
// Remove the AZURE_TOKEN_CREDENTIALS env var to restore previous behavior
var containerAppContainer = app.Template.Containers[0].Value!;
var azureTokenCredentialEnv = containerAppContainer.Env
.Single(v => v.Value!.Name.Value == "AZURE_TOKEN_CREDENTIALS");
containerAppContainer.Env.Remove(azureTokenCredentialEnv);
});
```

For Azure App Service, use a similar approach with `PublishAsAzureAppService`:

```csharp
builder.AddProject<Projects.Frontend>("frontend")
.PublishAsAzureAppService((infra, app) =>
{
// Remove the AZURE_TOKEN_CREDENTIALS env var to restore previous behavior
var settings = app.Properties.SiteConfig.Value!.AppSettings!;
var azureTokenCredentialSetting = settings
.Single(s => s.Value!.Name.Value == "AZURE_TOKEN_CREDENTIALS");
settings.Remove(azureTokenCredentialSetting);
});
```

## Affected APIs

- <xref:Aspire.Hosting.Azure.AzureContainerAppExtensions.AddAzureContainerAppEnvironment*?displayProperty=fullName>
- <xref:Aspire.Hosting.Azure.AzureAppServiceEnvironmentExtensions.AddAzureAppServiceEnvironment*?displayProperty=fullName>
22 changes: 22 additions & 0 deletions docs/compatibility/13.0/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
title: Breaking changes in Aspire 13.0
titleSuffix: ""
description: Navigate to the breaking changes in Aspire 13.0.
ms.date: 10/20/2025
---

# Breaking changes in Aspire 13.0

If you're migrating an app to Aspire 13.0, the breaking changes listed here might affect you.

[!INCLUDE [binary-source-behavioral](../includes/binary-source-behavioral.md)]

> [!NOTE]
> This article is a work in progress. It's not a complete list of breaking changes in Aspire 13.0.

## Breaking changes

| Title | Type of change | Introduced version |
|--|--|--|
| [Activity reporter and pipeline context renamed](pipeline-activity-reporter-renamed.md) | Binary incompatible, source incompatible | 13.0 |
| [DefaultAzureCredential defaults to ManagedIdentityCredential on ACA and App Service](defaultazurecredential-managedidentity-default.md) | Behavioral change | 13.0 |
142 changes: 142 additions & 0 deletions docs/compatibility/13.0/pipeline-activity-reporter-renamed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
---
title: "Breaking change - Activity reporter and pipeline context renamed"
description: "Learn about the breaking change in Aspire 13.0 where publishing activity reporter APIs and context types were renamed to reflect pipeline architecture."
ms.date: 10/20/2025
ai-usage: ai-assisted
ms.custom: https://github.com/dotnet/aspire/pull/12137
---

# Activity reporter and pipeline context renamed

In Aspire 13.0, the publishing activity reporter APIs and context types have been renamed to better reflect the pipeline architecture. The publishing step creation process has been simplified, with steps now automatically created during pipeline execution rather than requiring explicit creation within step actions.

## Version introduced

Aspire 13.0 Preview 1

## Previous behavior

The previous API used publishing-specific names and required explicit step creation within pipeline actions:

```csharp
builder.Pipeline.AddStep("assign-storage-role", async (context) =>
{
var roleAssignmentStep = await context.ActivityReporter
.CreateStepAsync($"assign-storage-role", context.CancellationToken);

await using (roleAssignmentStep.ConfigureAwait(false))
{
var assignRoleTask = await roleAssignmentStep
.CreateTaskAsync($"Granting file share access...", context.CancellationToken);

await using (assignRoleTask.ConfigureAwait(false))
{
// ... task work
}
}
});
```

The types used were:

- `DeployingContext` - Provided context for pipeline execution
- `IPublishingActivityReporter` - Interface for reporting activities
- `PublishingActivityReporter` - Implementation of the activity reporter
- `NullPublishingActivityReporter` - Null object pattern implementation
- `IPublishingStep` - Interface for publishing steps
- `IPublishingTask` - Interface for publishing tasks

## New behavior

The API now uses pipeline-specific names and automatically creates steps during pipeline execution:

```csharp
builder.Pipeline.AddStep("assign-storage-role", async (stepContext) =>
{
var assignRoleTask = await stepContext.ReportingStep
.CreateTaskAsync($"Granting file share access...", stepContext.CancellationToken);

await using (assignRoleTask.ConfigureAwait(false))
{
// ... task work
}
});
```

The types have been renamed as follows:

- `DeployingContext` → `PipelineContext` - Shared context across all pipeline steps
- `IPublishingActivityReporter` → `IPipelineActivityReporter` - Interface for reporting pipeline activities
- `PublishingActivityReporter` → `PipelineActivityReporter` - Implementation of the pipeline activity reporter
- `NullPublishingActivityReporter` → `NullPipelineActivityReporter` - Null object pattern implementation
- `IPublishingStep` → `IReportingStep` - Interface for reporting steps
- `IPublishingTask` → `IReportingTask` - Interface for reporting tasks

Additionally, a new `PipelineStepContext` type has been introduced that combines the shared `PipelineContext` with a step-specific `IReportingStep`, allowing each step to track its own tasks and completion state independently.

## Type of breaking change

This change can affect [binary compatibility](../categories.md#binary-compatibility) and [source compatibility](../categories.md#source-compatibility).

## Reason for change

The previous three-level hierarchy (`ActivityReporter → Step → Task`) was unnecessarily complex. The new architecture simplifies this by automatically creating steps during pipeline execution and integrating step management directly into the pipeline. This provides a cleaner separation between the shared pipeline context and step-specific execution context, making the API more intuitive and reducing boilerplate code.

## Recommended action

Update your code to use the new type names and simplified step creation pattern:

1. Replace `DeployingContext` with `PipelineContext` for shared pipeline context or `PipelineStepContext` for step-specific context.
1. Replace `IPublishingActivityReporter` with `IPipelineActivityReporter`.
1. Replace `PublishingActivityReporter` with `PipelineActivityReporter`.
1. Replace `NullPublishingActivityReporter` with `NullPipelineActivityReporter`.
1. Replace `IPublishingStep` with `IReportingStep`.
1. Replace `IPublishingTask` with `IReportingTask`.
1. Update pipeline step actions to accept `PipelineStepContext` instead of `DeployingContext`.
1. Remove explicit step creation calls within pipeline actions and use the automatically created `context.ReportingStep` instead.

Migration example:

```csharp
// Before
builder.Pipeline.AddStep("my-step", async (context) =>
{
var step = await context.ActivityReporter
.CreateStepAsync("my-step", context.CancellationToken);

await using (step.ConfigureAwait(false))
{
var task = await step.CreateTaskAsync("Doing work...", context.CancellationToken);
await using (task.ConfigureAwait(false))
{
// Do work
await task.CompleteAsync("Done", CompletionState.Completed, context.CancellationToken);
}
}
});

// After
builder.Pipeline.AddStep("my-step", async (stepContext) =>
{
var task = await stepContext.ReportingStep
.CreateTaskAsync("Doing work...", stepContext.CancellationToken);

await using (task.ConfigureAwait(false))
{
// Do work
await task.CompleteAsync("Done", CompletionState.Completed, stepContext.CancellationToken);
}
});
```

## Affected APIs

- <xref:Aspire.Hosting.ApplicationModel.DeployingContext?displayProperty=fullName>
- <xref:Aspire.Hosting.Publishing.IPublishingActivityReporter?displayProperty=fullName>
- `Aspire.Hosting.Publishing.PublishingActivityReporter`
- <xref:Aspire.Hosting.Publishing.NullPublishingActivityReporter?displayProperty=fullName>
- <xref:Aspire.Hosting.Publishing.IPublishingStep?displayProperty=fullName>
- <xref:Aspire.Hosting.Publishing.IPublishingTask?displayProperty=fullName>
- `Aspire.Hosting.Publishing.PublishingStep`
- `Aspire.Hosting.Publishing.PublishingTask`
- <xref:Aspire.Hosting.Publishing.PublishingExtensions?displayProperty=fullName>
14 changes: 13 additions & 1 deletion docs/compatibility/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,21 @@ items:
href: ../get-started/aspire-overview.md
- name: Breaking changes
href: breaking-changes.md
- name: Aspire 9.5
- name: Aspire 13.0
expanded: true
items:
- name: Overview
href: 13.0/index.md
- name: Breaking changes in 13.0
expanded: true
items:
- name: Activity reporter and pipeline context renamed
href: 13.0/pipeline-activity-reporter-renamed.md
- name: DefaultAzureCredential defaults to ManagedIdentityCredential on ACA and App Service
href: 13.0/defaultazurecredential-managedidentity-default.md
- name: Aspire 9.5
expanded: false
items:
- name: Overview
href: 9.5/index.md
- name: Breaking changes in 9.5
Expand Down
4 changes: 2 additions & 2 deletions docs/deployment/azd/aca-deployment-azd-in-depth.md
Original file line number Diff line number Diff line change
Expand Up @@ -370,9 +370,9 @@ properties:
value: "true"
- name: OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EXCEPTION_LOG_ATTRIBUTES
value: "true"
- name: services__apiservice__0
- name: APISERVICE_HTTP
value: http://apiservice.internal.{{ .Env.AZURE_CONTAINER_APPS_ENVIRONMENT_DEFAULT_DOMAIN }}
- name: services__apiservice__1
- name: APISERVICE_HTTPS
value: https://apiservice.internal.{{ .Env.AZURE_CONTAINER_APPS_ENVIRONMENT_DEFAULT_DOMAIN }}
tags:
azd-service-name: webfrontend
Expand Down
8 changes: 4 additions & 4 deletions docs/deployment/manifest-format.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,8 @@ Publishing the manifest from the default starter template for Aspire produces th
"OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EXCEPTION_LOG_ATTRIBUTES": "true",
"OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EVENT_LOG_ATTRIBUTES": "true",
"ConnectionStrings__cache": "{cache.connectionString}",
"services__apiservice__0": "{apiservice.bindings.http.url}",
"services__apiservice__1": "{apiservice.bindings.https.url}"
"APISERVICE_HTTP": "{apiservice.bindings.http.url}",
"APISERVICE_HTTPS": "{apiservice.bindings.https.url}"
},
"bindings": {
"http": {
Expand Down Expand Up @@ -142,8 +142,8 @@ This dependency is known because the environment variables for the _webfrontend_
"env": {
// ... other environment variables omitted for clarity
"ConnectionStrings__cache": "{cache.connectionString}",
"services__apiservice__0": "{apiservice.bindings.http.url}",
"services__apiservice__1": "{apiservice.bindings.https.url}"
"APISERVICE_HTTP": "{apiservice.bindings.http.url}",
"APISERVICE_HTTPS": "{apiservice.bindings.https.url}"
},
```

Expand Down
2 changes: 1 addition & 1 deletion docs/extensibility/dev-tunnels-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ The preceding code:
When another resource references a dev tunnel, environment variables are injected using the [Aspire service discovery](../service-discovery/overview.md) configuration format. Use the `WithReference` overloads that accept the `IResourceBuilder<DevTunnelResource>` parameter to reference a dev tunnel. This injects environment variables like:

```env
services__web__https__0=https://myweb-1234.westeurope.devtunnels.ms/
WEB_HTTPS=https://myweb-1234.westeurope.devtunnels.ms/
```

This lets downstream resources use the tunneled address exactly like any other Aspire service discovery entry.
Expand Down
Loading
Loading