Watch Aspire live streamsBelgelerAspire'ı deneyin
Watch Aspire live streamsBelgelerDeneyin

Building custom deployment pipelines

Bu içerik henüz dilinizde mevcut değil.

Aspire provides powerful APIs for building container images from your resources during publishing and deployment operations. This article covers the key components that enable programmatic container image creation and progress reporting.

During publishing and deployment, the container image manager is available to create images for resources that need them. Aspire uses this manager when a resource requires a container image, such as when publishing with Docker Compose. The process involves two main components:

  • IResourceContainerImageManager: The service that builds and pushes container images for resources.
  • 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.

Consider using the container image building and progress reporting APIs in these scenarios:

  • Custom deployment targets: When you need to deploy to platforms that require specific image formats or build configurations.
  • Complex build pipelines: When your publishing process involves multiple steps that users should see.
  • Enterprise scenarios: When you need custom progress reporting for integration with CI/CD systems or dashboards.
  • Custom resource types: When implementing custom resources that need to participate in the publishing and deployment process.

The IResourceContainerImageManager is the core service in the Aspire.Hosting.Publishing layer that converts resource definitions into container images. It analyzes each resource in your distributed application model and determines whether to:

  • Reuse an existing image.
  • Build from a .NET project using dotnet publish /t:PublishContainer.
  • Build from a Dockerfile using the local container runtime.

Configure each compute resource with WithContainerBuildOptions. The callback receives a ContainerBuildOptionsCallbackContext that allows you to specify:

  • Destination: Push the image to a registry or save it as an archive.
  • Image format: Docker or Open Container Initiative (OCI) format.
  • Target platform: Linux x64, Windows, ARM64, etc.
  • Output path: Where to save the built images.

The manager 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 manager throws an explicit InvalidOperationException to surface the problem early.

The PipelineActivityReporter API enables structured progress reporting during aspire publish and aspire deploy commands. This reduces uncertainty during long-running operations and surfaces failures early.

The progress reporter uses a hierarchical model with guaranteed ordering and thread-safe operations:

ConceptDescriptionCLI RenderingBehavior
StepTop-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.
TaskDiscrete unit of work nested under a step.Task message with indentation.Belongs to a single step; supports parallel creation with deterministic completion ordering.
Completion stateFinal status: Completed, CompletedWithWarning, or CompletedWithError.✅ (Completed)
⚠️ (Completed with warning)
❌ (Completed with error)
Each step/task transitions exactly once to a final state.

The reporter API provides structured access to progress reporting with the following characteristics:

  • Acquisition: The pipeline runner creates an IReportingStep for each pipeline step and exposes it through PipelineStepContext.ReportingStep.
  • Step creation: Register steps with WithPipelineStepFactory or builder.Pipeline.AddStep. The pipeline runner creates the corresponding reporting step during execution.
  • Task creation: IReportingStep.CreateTaskAsync(title, ct) returns an IReportingTask.
  • State transitions: SucceedAsync, WarnAsync, FailAsync methods accept a summary message.
  • Completion: Complete individual tasks in the callback. The runner disposes the reporting step after the callback and completes the overall pipeline operation.
  • Ordering: Creation and completion events preserve call order; updates are serialized.
  • Cancellation: All APIs accept CancellationToken and propagate cancellation to the CLI.
  • Disposal contract: Disposing steps automatically completes them if unfinished, preventing orphaned phases.

Example: Build container images and report progress

Section titled “Example: Build container images and report progress”

To use these APIs, register pipeline steps with WithPipelineStepFactory (resource-level) or builder.Pipeline.AddStep (application-level). This lets custom resources participate in publish and deploy flows with explicit step dependencies.

As a developer, you can choose to:

  • Register a step that is requiredBy WellKnownPipelineSteps.Publish when your resource needs custom publishing behavior.
  • Register a step that is requiredBy WellKnownPipelineSteps.Deploy when your resource needs custom deployment behavior.
  • Register both when your resource performs work in both phases and needs ordering between those steps.

For broader pipeline orchestration patterns, see Deployment pipelines. For custom resource authoring basics, see Create custom resources.

For example, consider an extension method that registers two pipeline steps for a custom ComputeEnvironmentResource:

ComputeEnvironmentResourceExtensions.cs
public static class ComputeEnvironmentResourceExtensions
{
public static IResourceBuilder<ComputeEnvironmentResource> AddComputeEnvironment(
this IDistributedApplicationBuilder builder,
[ResourceName] string name)
{
var resource = new ComputeEnvironmentResource(name);
return builder.AddResource(resource)
.WithPipelineStepFactory(
stepName: $"{name}-build-images",
callback: PublishAsync,
requiredBy: [WellKnownPipelineSteps.Publish],
description: "Build container images and write deployment artifacts.")
.WithPipelineStepFactory(
stepName: $"{name}-deploy",
callback: DeployAsync,
dependsOn: [$"{name}-build-images"],
requiredBy: [WellKnownPipelineSteps.Deploy],
description: "Deploy generated artifacts to the target environment.");
}
}

In the same class, define PublishAsync and DeployAsync as private static methods that each accept PipelineStepContext (shown in the next sections). The explicit dependsOn relationship ensures the deploy step waits for this resource’s image-build step, even when you add other custom publish or deploy steps to the pipeline.

The preceding code:

  • Defines an extension method on the IDistributedApplicationBuilder.
  • Accepts a name for the compute environment resource, protected by the ResourceNameAttribute.
  • Instantiates a ComputeEnvironmentResource given the name.
  • Registers a publish step and a deploy step with explicit dependencies.
  • Adds both steps through WithPipelineStepFactory.
  • Uses the validated resource name as the prefix for stable, unambiguous step names.

In your AppHost, configure the project image output and add the ComputeEnvironmentResource to the application model:

AppHost.cs
var builder = DistributedApplication.CreateBuilder(args);
var cache = builder.AddRedis("redis");
builder.AddProject<Projects.Api>("api")
.WithReference(cache)
.WithContainerBuildOptions(options =>
{
options.Destination = ContainerImageDestination.Archive;
options.ImageFormat = ContainerImageFormat.Oci;
options.TargetPlatform = ContainerTargetPlatform.LinuxAmd64;
options.OutputPath = Path.Combine(
builder.AppHostDirectory,
"artifacts",
"images");
});
builder.AddComputeEnvironment("compute-env");
builder.Build().Run();

The preceding code configures the project image as an OCI archive, then uses the AddComputeEnvironment extension method to add the ComputeEnvironmentResource to the application model.

The publish step callback can build container images and generate deployment artifacts:

ComputeEnvironmentResourceExtensions.cs
private static async Task PublishAsync(PipelineStepContext context)
{
var imageManager = context.Services.GetRequiredService<IResourceContainerImageManager>();
var reportingStep = context.ReportingStep;
var projectResources = context.Model.Resources
.OfType<ProjectResource>()
.ToList();
if (projectResources.Count > 0)
{
var buildTask = await reportingStep.CreateTaskAsync(
$"Building {projectResources.Count} container image(s)",
context.CancellationToken);
await imageManager.BuildImagesAsync(projectResources, context.CancellationToken);
await buildTask.SucceedAsync(
$"Built {projectResources.Count} image(s) successfully",
context.CancellationToken);
}
else
{
var skipTask = await reportingStep.CreateTaskAsync(
"No container images to build",
context.CancellationToken);
await skipTask.SucceedAsync("Skipped - no project resources found", context.CancellationToken);
}
var manifestTask = await reportingStep.CreateTaskAsync(
"Generate deployment manifests",
context.CancellationToken);
// Write deployment files...
await manifestTask.SucceedAsync("Manifests ready", context.CancellationToken);
}

The preceding code:

  • Implements a publish step that builds container images and generates deployment manifests.
  • Uses the IResourceContainerImageManager API to build container images.
  • Reports progress with IReportingStep tasks from PipelineStepContext.ReportingStep.

Your publish step might use IResourceContainerImageManager to build images, while your deploy step might use those artifacts and push them to a registry or target environment.

The deploy step callback can apply deployment artifacts and report task-level progress:

ComputeEnvironmentResourceExtensions.cs
private static async Task DeployAsync(PipelineStepContext context)
{
var reportingStep = context.ReportingStep;
var applyTask = await reportingStep.CreateTaskAsync(
"Apply Kubernetes manifests",
context.CancellationToken);
// Simulate deploying to Kubernetes cluster
await Task.Delay(1_000, context.CancellationToken);
await applyTask.SucceedAsync("All workloads deployed", context.CancellationToken);
}

The preceding code:

  • Simulates deploying workloads to a Kubernetes cluster.
  • Uses PipelineStepContext.ReportingStep to create and complete deployment tasks.
  • Handles cancellation through the provided CancellationToken.

When using these APIs, follow these guidelines:

  • Configure production resources with WithContainerBuildOptions.
  • Consider target platform requirements when building for deployment.
  • Use OCI format for maximum compatibility with container registries.
  • Handle InvalidOperationException when container runtime health checks fail.
  • Encapsulate long-running logical phases in steps rather than emitting raw tasks.
  • Keep titles concise (under 60 characters) as the CLI truncates longer strings.
  • Treat warnings as recoverable and allow subsequent steps to proceed.
  • Treat errors as fatal and fail fast with clear diagnostics.
  • Use asynchronous, cancellation-aware operations to avoid blocking event processing.
  • Each step and task starts in Running state and transitions exactly once to Completed, Warning, or Error.
  • Throw an exception when attempting multiple state transitions.
  • Leverage the reporter to guarantee ordered events and prevent interleaving.
  • Dispose of IReportingStep to automatically complete unfinished steps.