diff --git a/docs/azureai/ai-integrations-compatibility-matrix.md b/docs/azureai/ai-integrations-compatibility-matrix.md index a60465e90c..ba3f8077e2 100644 --- a/docs/azureai/ai-integrations-compatibility-matrix.md +++ b/docs/azureai/ai-integrations-compatibility-matrix.md @@ -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() .WithReference(chat); diff --git a/docs/azureai/azureai-foundry-integration.md b/docs/azureai/azureai-foundry-integration.md index 615273f124..3cb1e154a6 100644 --- a/docs/azureai/azureai-foundry-integration.md +++ b/docs/azureai/azureai-foundry-integration.md @@ -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() .WithReference(chat) @@ -74,17 +75,18 @@ builder.AddProject() 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 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 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 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"; @@ -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() .WithReference(chat) @@ -143,6 +146,9 @@ When the AppHost starts up, the local foundry service is also started. This requ The 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 method: diff --git a/docs/compatibility/13.0/defaultazurecredential-managedidentity-default.md b/docs/compatibility/13.0/defaultazurecredential-managedidentity-default.md new file mode 100644 index 0000000000..53500b6d25 --- /dev/null +++ b/docs/compatibility/13.0/defaultazurecredential-managedidentity-default.md @@ -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("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("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 + +- +- diff --git a/docs/compatibility/13.0/index.md b/docs/compatibility/13.0/index.md new file mode 100644 index 0000000000..8decc31494 --- /dev/null +++ b/docs/compatibility/13.0/index.md @@ -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 | diff --git a/docs/compatibility/13.0/pipeline-activity-reporter-renamed.md b/docs/compatibility/13.0/pipeline-activity-reporter-renamed.md new file mode 100644 index 0000000000..106783147f --- /dev/null +++ b/docs/compatibility/13.0/pipeline-activity-reporter-renamed.md @@ -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 + +- +- +- `Aspire.Hosting.Publishing.PublishingActivityReporter` +- +- +- +- `Aspire.Hosting.Publishing.PublishingStep` +- `Aspire.Hosting.Publishing.PublishingTask` +- diff --git a/docs/compatibility/toc.yml b/docs/compatibility/toc.yml index b0a1e43590..9a3735621f 100644 --- a/docs/compatibility/toc.yml +++ b/docs/compatibility/toc.yml @@ -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 diff --git a/docs/deployment/azd/aca-deployment-azd-in-depth.md b/docs/deployment/azd/aca-deployment-azd-in-depth.md index 777a532241..442e9d66fb 100644 --- a/docs/deployment/azd/aca-deployment-azd-in-depth.md +++ b/docs/deployment/azd/aca-deployment-azd-in-depth.md @@ -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 diff --git a/docs/deployment/manifest-format.md b/docs/deployment/manifest-format.md index 6fc40d16fc..05874ef839 100644 --- a/docs/deployment/manifest-format.md +++ b/docs/deployment/manifest-format.md @@ -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": { @@ -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}" }, ``` diff --git a/docs/extensibility/dev-tunnels-integration.md b/docs/extensibility/dev-tunnels-integration.md index 76ba0fbd2d..6697499bcd 100644 --- a/docs/extensibility/dev-tunnels-integration.md +++ b/docs/extensibility/dev-tunnels-integration.md @@ -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` 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. diff --git a/docs/extensibility/interaction-service.md b/docs/extensibility/interaction-service.md index 20d39e344f..e3e07f1e61 100644 --- a/docs/extensibility/interaction-service.md +++ b/docs/extensibility/interaction-service.md @@ -87,7 +87,7 @@ These approaches help you create interactive, user-friendly experiences for loca > }); > ``` > -> For CLI specific contexts, the interaction service is retrieved from either the `PublishingContext` or `DeploymentContext` depending on the operation being performed. +> For CLI specific contexts, the interaction service is retrieved from either the `PublishingContext` or `PipelineStepContext` depending on the operation being performed. ## Display messages diff --git a/docs/fundamentals/app-host-overview.md b/docs/fundamentals/app-host-overview.md index 660e46470f..4aa743743d 100644 --- a/docs/fundamentals/app-host-overview.md +++ b/docs/fundamentals/app-host-overview.md @@ -46,28 +46,45 @@ When you call - - + Exe - net9.0 + net10.0 - - - - ``` +> [!NOTE] +> The alternative approach of explicitly listing the SDK and package reference still works and isn't a requirement to change existing projects: +> +> ```xml +> +> +> +> +> +> Exe +> net10.0 +> +> +> +> +> +> +> +> +> +> +> ``` + The following code describes an AppHost `Program` with two project references and a Redis cache: ```csharp @@ -113,11 +130,16 @@ Aspire projects are made up of a set of resources. The primary base resource typ | Method | Resource type | Description | |--|--|--| | | | A .NET project, for example, an ASP.NET Core web app. | +| `AddCSharpApp` | `CSharpAppResource` | A C# project or file-based app, for example, a _*.cs_ file, _*.csproj_ file, or project directory. | | | | A container image, such as a Docker image. | | | | An executable file, such as a [Node.js app](../get-started/build-aspire-apps-with-nodejs.md). | | | | A parameter resource that can be used to [express external parameters](external-parameters.md). | -Project resources represent .NET projects that are part of the app model. When you add a project reference to the AppHost project, the Aspire SDK generates a type in the `Projects` namespace for each referenced project. For more information, see [Aspire SDK: Project references](dotnet-aspire-sdk.md#project-references). + + +Project resources represent .NET projects that are part of the app model. When you add a project reference to the AppHost project, the Aspire SDK generates a type in the `Projects` namespace for each referenced project. For more information, see [Aspire SDK: Project references](dotnet-aspire-sdk.md#project-references). Alternatively, you can add C# projects or file-based apps without a project reference using the `AddCSharpApp` method. To add a project to the app model, use the method: @@ -140,6 +162,25 @@ var apiservice = builder.AddProject("apiservice") The preceding code adds three replicas of the "apiservice" project resource to the app model. For more information, see [Aspire dashboard: Resource replicas](dashboard/explore.md#resource-replicas). +C# app resources represent C# projects or file-based apps that are part of the app model. Unlike , which requires a project reference, the `AddCSharpApp` method can add C# projects or file-based apps using a path to a _*.cs_ file, _*.csproj_ file, or project directory. This is useful for adding file-based apps introduced in .NET 10 or for including projects without adding a project reference to the AppHost. + +To add a C# app to the app model, use the `AddCSharpApp` method: + +```csharp +var builder = DistributedApplication.CreateBuilder(args); + +// Adds a file-based C# app "inventoryservice" from a .cs file. +var inventoryService = builder.AddCSharpApp("inventoryservice", @"..\InventoryService.cs"); + +// Adds a C# project "catalogservice" from a project directory. +var catalogService = builder.AddCSharpApp("catalogservice", @"..\CatalogService"); +``` + +The `AddCSharpApp` method supports the same configuration options as , including replicas, environment variables, and resource dependencies. + +> [!NOTE] +> The `AddCSharpApp` method is marked as experimental and requires .NET 10 SDK for file-based C# app support. For more information on file-based apps, see the [What's new in Aspire 9.5](../whats-new/dotnet-aspire-9.5.md#file-based-apphost-support-preview) documentation. + ## Reference resources A reference represents a dependency between resources. For example, you can probably imagine a scenario where a web frontend depends on a Redis cache. Consider the following example AppHost `Program` C# code: @@ -176,7 +217,7 @@ Project-to-project references are handled differently than resources that have w | Method | Environment variable | |--|--| | `WithReference(cache)` | `ConnectionStrings__cache="localhost:62354"` | -| `WithReference(apiservice)` | `services__apiservice__http__0="http://localhost:5455"`
`services__apiservice__https__0="https://localhost:7356"` | +| `WithReference(apiservice)` | `APISERVICE_HTTP="http://localhost:5455"`
`APISERVICE_HTTPS="https://localhost:7356"` | Adding a reference to the "apiservice" project results in service discovery environment variables being added to the frontend. This is because typically, project-to-project communication occurs over HTTP/gRPC. For more information, see [Aspire service discovery](../service-discovery/overview.md). @@ -202,7 +243,7 @@ var apiservice = builder.AddProject("apiservice") | Method | Environment variable | |---------------------------|-------------------------------------------------------| -| `WithReference(endpoint)` | `services__myapp__endpoint__0=https://localhost:9043` | +| `WithReference(endpoint)` | `MYAPP_ENDPOINT=https://localhost:9043` | The `port` parameter is the port that the container is listening on. For more information on container ports, see [Container ports](networking-overview.md#container-ports). For more information on service discovery, see [Aspire service discovery](../service-discovery/overview.md). @@ -210,20 +251,20 @@ The `port` parameter is the port that the container is listening on. For more in In the preceding section, the method is used to express dependencies between resources. When service endpoints result in environment variables being injected into the dependent resource, the format might not be obvious. This section provides details on this format. -When one resource depends on another resource, the AppHost injects environment variables into the dependent resource. These environment variables configure the dependent resource to connect to the resource it depends on. The format of the environment variables is specific to Aspire and expresses service endpoints in a way that is compatible with [Service Discovery](../service-discovery/overview.md). +When one resource depends on another resource, the AppHost injects environment variables into the dependent resource. These environment variables configure the dependent resource to connect to the resource it depends on. The format of the environment variables is specific to Aspire and expresses service endpoints in a way that is compatible with [Service Discovery](../service-discovery/overview.md) and polyglot scenarios. -Service endpoint environment variable names are prefixed with `services__` (double underscore), then the service name, the endpoint name, and finally the index. The index supports multiple endpoints for a single service, starting with `0` for the first endpoint and incrementing for each endpoint. +Service endpoint environment variable names follow the pattern `{RESOURCENAME}_{ENDPOINTNAME}`, where both the resource name and endpoint name are uppercased. This format is language-agnostic and works well with non-.NET technologies. Consider the following environment variable examples: ```Environment -services__apiservice__http__0 +APISERVICE_HTTP ``` -The preceding environment variable expresses the first HTTP endpoint for the `apiservice` service. The value of the environment variable is the URL of the service endpoint. A named endpoint might be expressed as follows: +The preceding environment variable expresses the HTTP endpoint for the `apiservice` service. The value of the environment variable is the URL of the service endpoint. A named endpoint might be expressed as follows: ```Environment -services__apiservice__myendpoint__0 +APISERVICE_MYENDPOINT ``` In the preceding example, the `apiservice` service has a named endpoint called `myendpoint`. The value of the environment variable is the URL of the service endpoint. diff --git a/docs/fundamentals/custom-deployments.md b/docs/fundamentals/custom-deployments.md index 68f784cc9b..bbc4c5e362 100644 --- a/docs/fundamentals/custom-deployments.md +++ b/docs/fundamentals/custom-deployments.md @@ -14,7 +14,7 @@ Aspire provides powerful APIs for building container images from your resources During publishing and deployment, the container image builder is available to create images for resources that need them. Aspire uses this builder when a resource requires a container image, such as when publishing with Docker Compose. The process involves two main components: - : The service that turns resource definitions into runnable container images. -- : The API that provides structured progress reporting during long-running operations. +- `IPipelineActivityReporter`: The API that provides structured progress reporting during long-running operations. These APIs give you fine-grained control over the image building process and provide real-time feedback to users during lengthy build operations. @@ -53,9 +53,9 @@ The class provides strong The builder performs container runtime health checks (Docker/Podman) only when at least one resource requires a Dockerfile build. This change eliminates false-positive errors in projects that publish directly from .NET assemblies. If the container runtime is required but unhealthy, the builder throws an explicit `InvalidOperationException` to surface the problem early. -## Publishing activity reporter API +## Pipeline activity reporter API -The `PublishingActivityProgressReporter` API enables structured progress reporting during [`aspire publish`](../cli-reference/aspire-publish.md) and [`aspire deploy`](../cli-reference/aspire-deploy.md) commands. This reduces uncertainty during long-running operations and surfaces failures early. +The `PipelineActivityReporter` API enables structured progress reporting during [`aspire publish`](../cli-reference/aspire-publish.md) and [`aspire deploy`](../cli-reference/aspire-deploy.md) commands. This reduces uncertainty during long-running operations and surfaces failures early. ### API overview and behavior @@ -63,7 +63,7 @@ The progress reporter uses a hierarchical model with guaranteed ordering and thr | Concept | Description | CLI Rendering | Behavior | |---------|-------------|---------------|----------| -| **Step** | Top-level phase, such as "Build images" or "Deploy workloads". | Step message with status glyph and elapsed time. | Forms a strict tree structure; nested steps are unsupported. | +| **Step** | Top-level phase, such as "Build images" or "Deploy workloads". | Step message with status glyph and elapsed time. | Forms a strict tree structure; nested steps are unsupported. Steps are created automatically during pipeline execution. | | **Task** | Discrete unit of work nested under a step. | Task message with indentation. | Belongs to a single step; supports parallel creation with deterministic completion ordering. | | **Completion state** | Final status: `Completed`, `Warning`, or `Error`. | ✅ (Completed), ⚠️ (Warning), ❌ (Error) | Each step/task transitions exactly once to a final state. | @@ -71,9 +71,9 @@ The progress reporter uses a hierarchical model with guaranteed ordering and thr The reporter API provides structured access to progress reporting with the following characteristics: -- **Acquisition**: Retrieved from `PublishingContext.ActivityReporter` or `DeployingContext.ActivityReporter`. -- **Step creation**: `CreateStepAsync(title, ct)` returns an `IPublishingActivityStep`. -- **Task creation**: `IPublishingActivityStep.CreateTaskAsync(title, ct)` returns an `IPublishingActivityTask`. +- **Acquisition**: Retrieved from `PublishingContext.ActivityReporter` or through the `PipelineStepContext.ReportingStep` property in pipeline steps. +- **Step creation**: Steps are now created automatically during pipeline execution. The `CreateStepAsync(title, ct)` method returns an `IReportingStep`. +- **Task creation**: `IReportingStep.CreateTaskAsync(title, ct)` returns an `IReportingTask`. - **State transitions**: `SucceedAsync`, `WarnAsync`, `FailAsync` methods accept a summary message. - **Completion**: `CompletePublishAsync(message, state, isDeploy, ct)` marks the entire operation. - **Ordering**: Creation and completion events preserve call order; updates are serialized. @@ -130,7 +130,7 @@ The preceding code: - Implements a publishing pipeline that builds container images and generates deployment manifests. - Uses the `IResourceContainerImageBuilder` API to build container images. -- Reports progress and completion status using the `PublishingActivityProgressReporter` API. +- Reports progress and completion status using the `PipelineActivityReporter` API. Your publishing callback might use `IResourceContainerImageBuilder` to build container images, while your deployment callback might use the built images and push them to a registry or deployment target. @@ -143,7 +143,7 @@ Like the publishing callback, the deploying callback is registered using the `De The preceding code: - Simulates deploying workloads to a Kubernetes cluster. -- Uses the `PublishingActivityProgressReporter` API to create and manage deployment steps and tasks. +- Uses the `PipelineActivityReporter` API to create and manage deployment steps and tasks. - Reports progress and marks each deployment phase as completed. - Completes the deployment operation with a final status update. - Handles cancellation through the provided `CancellationToken`. diff --git a/docs/fundamentals/dashboard/configuration.md b/docs/fundamentals/dashboard/configuration.md index 9f2235bb70..c5cec73ccb 100644 --- a/docs/fundamentals/dashboard/configuration.md +++ b/docs/fundamentals/dashboard/configuration.md @@ -1,7 +1,7 @@ --- title: Aspire dashboard configuration description: Aspire dashboard configuration options -ms.date: 04/15/2025 +ms.date: 10/10/2025 ms.topic: reference --- @@ -91,6 +91,7 @@ Browser token authentication works by the frontend asking for a token. The token | `Dashboard:Frontend:OpenIdConnect:UsernameClaimType` | `preferred_username` | Specifies one or more claim types that should be used to display the authenticated user's username. Can be a single claim type or a comma-delimited list of claim types. | | `Dashboard:Frontend:OpenIdConnect:RequiredClaimType` | `null` | Specifies the claim that must be present for authorized users. Authorization fails without this claim. This value is optional. | | `Dashboard:Frontend:OpenIdConnect:RequiredClaimValue` | `null` | Specifies the value of the required claim. Only used if `Dashboard:Frontend:OpenIdConnect:RequireClaimType` is also specified. This value is optional. | +| `Dashboard:Frontend:OpenIdConnect:ClaimActions` | `null` | A collection of claim actions to configure how claims are mapped from the OpenID Connect user info endpoint. Each claim action can map JSON properties to claims. This value is optional. | | `Authentication:Schemes:OpenIdConnect:Authority` | `null` | URL to the identity provider (IdP). | | `Authentication:Schemes:OpenIdConnect:ClientId` | `null` | Identity of the relying party (RP). | | `Authentication:Schemes:OpenIdConnect:ClientSecret` | `null` | A secret that only the real RP would know. | @@ -101,6 +102,44 @@ Browser token authentication works by the frontend asking for a token. The token > > For more information, see [Configure ASP.NET Core to work with proxy servers and load balancers](/aspnet/core/host-and-deploy/proxy-load-balancer). +### Claim actions + +Claim actions configure how claims are mapped from the JSON returned by the OpenID Connect user info endpoint to the user's claims identity. Each claim action in the `Dashboard:Frontend:OpenIdConnect:ClaimActions` collection supports the following properties: + +| Property | Description | +|--|--| +| `ClaimType` (required) | The claim type to create. | +| `JsonKey` (required) | The JSON key to map from. | +| `SubKey` (optional) | The sub-key within the JSON key to map from. Used when the value is nested within another JSON object. | +| `IsUnique` (optional) | When `true`, ensures only one claim of this type exists. If a claim already exists, it won't be added again. Defaults to `false`. | +| `ValueType` (optional) | The claim value type. Defaults to `string`. | + +The following example shows how to configure claim actions using JSON configuration: + +```json +{ + "Dashboard": { + "Frontend": { + "OpenIdConnect": { + "ClaimActions": [ + { + "ClaimType": "role", + "JsonKey": "role" + } + ] + } + } + } +} +``` + +Or using environment variables for configuration: + +```bash +export Dashboard__Frontend__OpenIdConnect__ClaimActions__0__ClaimType="role" +export Dashboard__Frontend__OpenIdConnect__ClaimActions__0__JsonKey="role" +``` + ## OTLP authentication The OTLP endpoint authentication is configured with `Dashboard:Otlp:AuthMode`. The OTLP endpoint can be secured with an API key or [client certificate](/aspnet/core/security/authentication/certauth) authentication. diff --git a/docs/fundamentals/orchestrate-resources.md b/docs/fundamentals/orchestrate-resources.md index 4c50f3a659..b5cbc72767 100644 --- a/docs/fundamentals/orchestrate-resources.md +++ b/docs/fundamentals/orchestrate-resources.md @@ -285,7 +285,7 @@ var frontend = builder.AddProject("frontend") .WithReference(api); ``` -This configuration injects an environment variable like `services__api__https__0=https://api.example.com/` into the frontend project, enabling service discovery through the standard .NET service discovery mechanisms. +This configuration injects an environment variable like `API_HTTPS=https://api.example.com/` into the frontend project, enabling service discovery through the standard .NET service discovery mechanisms. ### External service lifecycle diff --git a/docs/get-started/build-aspire-apps-with-nodejs.md b/docs/get-started/build-aspire-apps-with-nodejs.md index 2aaa6da893..cd56687a88 100644 --- a/docs/get-started/build-aspire-apps-with-nodejs.md +++ b/docs/get-started/build-aspire-apps-with-nodejs.md @@ -147,7 +147,7 @@ There are several key modifications from the original Angular template. The firs :::code language="javascript" source="~/aspire-samples/samples/AspireWithJavaScript/AspireJavaScript.Angular/proxy.conf.js"::: -The Aspire AppHost sets the `services__weatherapi__http__0` environment variable, which is used to resolve the "weatherapi" service endpoint. The preceding configuration proxies HTTP requests that start with `/api` to the target URL specified in the environment variable. +The Aspire AppHost sets the `WEATHERAPI_HTTPS` and `WEATHERAPI_HTTP` environment variables, which are used to resolve the "weatherapi" service endpoints. The preceding configuration proxies HTTP requests that start with `/api` to the target URL specified in the environment variable. Then include the proxy file to in the _angular.json_ file. Update the `serve` target to include the `proxyConfig` option, referencing to the created _proxy.conf.js_ file. @@ -217,7 +217,7 @@ As the `TheWelcome` integration is `mounted`, it calls the `/api/weatherforecast :::code language="typescript" source="~/aspire-samples/samples/AspireWithJavaScript/AspireJavaScript.Vue/vite.config.ts"::: -Additionally, the Vite config specifies the `server.proxy` property to forward requests to the "weatherapi" service. This is achieved by using the `services__weatherapi__http__0` environment variable, which is set by the Aspire AppHost. +Additionally, the Vite config specifies the `server.proxy` property to forward requests to the "weatherapi" service. This is achieved by using the `WEATHERAPI_HTTPS` and `WEATHERAPI_HTTP` environment variables, which are set by the Aspire AppHost. The final update from the template is made to the _TheWelcome.vue_ file. This file calls the `/api/WeatherForecast` endpoint to retrieve the weather forecast data, and displays the data in a table. It includes [CSS, HTML, and TypeScript updates](https://github.com/dotnet/aspire-samples/blob/ef6868b0999c6eea3d42a10f2b20433c5ea93720/samples/AspireWithJavaScript/AspireJavaScript.Vue/src/components/TheWelcome.vue). diff --git a/docs/github/github-models-integration.md b/docs/github/github-models-integration.md index 9e63f86656..77fdcf40b7 100644 --- a/docs/github/github-models-integration.md +++ b/docs/github/github-models-integration.md @@ -39,7 +39,8 @@ To add a `GitHubModelResource` to your AppHost project, call the `AddGitHubModel ```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() .WithReference(chat); @@ -47,7 +48,10 @@ builder.AddProject() // After adding all resources, run the app... ``` -The preceding code adds a GitHub Model resource named `chat` using the `openai/gpt-4o-mini` model. The method passes the connection information to the `ExampleProject` project. +The preceding code adds a GitHub Model resource named `chat` using the constant for OpenAI's GPT-4o-mini model. The method passes the connection information to the `ExampleProject` project. + +> [!TIP] +> Use the strongly-typed constants to avoid typos and ensure you're using valid model identifiers. These constants are grouped by publisher (for example, `GitHubModel.OpenAI.OpenAIGpt4oMini`, `GitHubModel.Microsoft.Phi4MiniInstruct`, `GitHubModel.DeepSeek.DeepSeekV30324`). ### Specify an organization @@ -57,7 +61,8 @@ For organization-specific requests, you can specify an organization parameter: var builder = DistributedApplication.CreateBuilder(args); var organization = builder.AddParameter("github-org"); -var chat = builder.AddGitHubModel("chat", "openai/gpt-4o-mini", organization); +var model = GitHubModel.OpenAI.OpenAIGpt4oMini; +var chat = builder.AddGitHubModel("chat", model, organization); builder.AddProject() .WithReference(chat); @@ -76,7 +81,8 @@ The GitHub Models integration supports multiple ways to configure authentication By default, the integration creates a parameter named `{resource_name}-gh-apikey` that automatically falls back to the `GITHUB_TOKEN` environment variable: ```csharp -var chat = builder.AddGitHubModel("chat", "openai/gpt-4o-mini"); +var model = GitHubModel.OpenAI.OpenAIGpt4oMini; +var chat = builder.AddGitHubModel("chat", model); ``` Then in user secrets: @@ -95,7 +101,8 @@ You can also specify a custom parameter for the API key: ```csharp var apiKey = builder.AddParameter("my-api-key", secret: true); -var chat = builder.AddGitHubModel("chat", "openai/gpt-4o-mini") +var model = GitHubModel.OpenAI.OpenAIGpt4oMini; +var chat = builder.AddGitHubModel("chat", model) .WithApiKey(apiKey); ``` @@ -114,7 +121,8 @@ Then in user secrets: You can add health checks to verify the GitHub Models endpoint accessibility and API key validity: ```csharp -var chat = builder.AddGitHubModel("chat", "openai/gpt-4o-mini") +var model = GitHubModel.OpenAI.OpenAIGpt4oMini; +var chat = builder.AddGitHubModel("chat", model) .WithHealthCheck(); ``` @@ -123,14 +131,14 @@ var chat = builder.AddGitHubModel("chat", "openai/gpt-4o-mini") ### Available models -GitHub Models supports various AI models. Some popular options include: +GitHub Models supports various AI models. Use the strongly-typed constants for the most up-to-date list of available models. Some popular options include: -- `openai/gpt-4o-mini` -- `openai/gpt-4o` -- `deepseek/DeepSeek-V3-0324` -- `microsoft/Phi-4-mini-instruct` +- `GitHubModel.OpenAI.OpenAIGpt4oMini` +- `GitHubModel.OpenAI.OpenAIGpt41Mini` +- `GitHubModel.DeepSeek.DeepSeekV30324` +- `GitHubModel.Microsoft.Phi4MiniInstruct` -Check the [GitHub Models documentation](https://docs.github.com/github-models) for the most up-to-date list of available models. +Check the [GitHub Models documentation](https://docs.github.com/github-models) for more information about these models and their capabilities. ## Client integration @@ -171,7 +179,6 @@ public class ExampleService(ChatCompletionsClient client) public async Task GetResponseAsync(string prompt) { var response = await client.GetChatCompletionsAsync( - "openai/gpt-4o-mini", new[] { new ChatMessage(ChatRole.User, prompt) @@ -237,7 +244,7 @@ public class ChatService(OpenAIClient client) { public async Task GetChatResponseAsync(string prompt) { - var chatClient = client.GetChatClient("openai/gpt-4o-mini"); + var chatClient = client.GetChatClient(GitHubModel.OpenAI.OpenAIGpt4oMini); var response = await chatClient.CompleteChatAsync( new[] @@ -271,7 +278,8 @@ When running an app in GitHub Codespaces or GitHub Actions, the `GITHUB_TOKEN` e ```csharp // No additional configuration needed in Codespaces/GitHub Actions -var chat = builder.AddGitHubModel("chat", "openai/gpt-4o-mini"); +var model = GitHubModel.OpenAI.OpenAIGpt4oMini; +var chat = builder.AddGitHubModel("chat", model); ``` ##### Personal access tokens for local development diff --git a/docs/snippets/azure/AppHost/AppHost.cs b/docs/snippets/azure/AppHost/AppHost.cs index 10f1624e1a..e492135deb 100644 --- a/docs/snippets/azure/AppHost/AppHost.cs +++ b/docs/snippets/azure/AppHost/AppHost.cs @@ -6,7 +6,7 @@ var foundryDeployment = foundry.AddDeployment( name: "chat", modelName: "Phi-4", - modelVersion: "1", + modelVersion: "7", format: "Microsoft"); builder.AddAzureAppConfiguration("config"); diff --git a/docs/testing/snippets/testing/mstest/AspireApp.Tests/EnvVarTests.cs b/docs/testing/snippets/testing/mstest/AspireApp.Tests/EnvVarTests.cs index d0bfbfd2dd..6b969f3ae7 100644 --- a/docs/testing/snippets/testing/mstest/AspireApp.Tests/EnvVarTests.cs +++ b/docs/testing/snippets/testing/mstest/AspireApp.Tests/EnvVarTests.cs @@ -21,7 +21,7 @@ public async Task WebResourceEnvVarsResolveToApiService() // Assert CollectionAssert.Contains(envVars, new KeyValuePair( - key: "services__apiservice__https__0", + key: "APISERVICE_HTTPS", value: "{apiservice.bindings.https.url}")); } } diff --git a/docs/testing/snippets/testing/nunit/AspireApp.Tests/EnvVarTests.cs b/docs/testing/snippets/testing/nunit/AspireApp.Tests/EnvVarTests.cs index e93ee69c51..9aef6e94a4 100644 --- a/docs/testing/snippets/testing/nunit/AspireApp.Tests/EnvVarTests.cs +++ b/docs/testing/snippets/testing/nunit/AspireApp.Tests/EnvVarTests.cs @@ -20,7 +20,7 @@ public async Task WebResourceEnvVarsResolveToApiService() // Assert Assert.That(envVars, Does.Contain( new KeyValuePair( - key: "services__apiservice__https__0", + key: "APISERVICE_HTTPS", value: "{apiservice.bindings.https.url}"))); } } diff --git a/docs/testing/snippets/testing/xunit/AspireApp.Tests/EnvVarTests.cs b/docs/testing/snippets/testing/xunit/AspireApp.Tests/EnvVarTests.cs index 434a1beb87..febd8169bb 100644 --- a/docs/testing/snippets/testing/xunit/AspireApp.Tests/EnvVarTests.cs +++ b/docs/testing/snippets/testing/xunit/AspireApp.Tests/EnvVarTests.cs @@ -22,7 +22,7 @@ public async Task WebResourceEnvVarsResolveToApiService() { var (key, value) = kvp; - return key is "services__apiservice__https__0" + return key is "APISERVICE_HTTPS" && value is "{apiservice.bindings.https.url}"; }); }