Edit

Share via


.NET Aspire Azure integrations overview

Azure is the most popular cloud platform for building and deploying .NET applications. The Azure SDK for .NET allows for easy management and use of Azure services. .NET Aspire provides a set of integrations with Azure services, where you're free to add new resources or connect to existing ones. This article details some common aspects of all Azure integrations in .NET Aspire and aims to help you understand how to use them.

Add Azure resources

All .NET Aspire Azure hosting integrations expose Azure resources and by convention are added using AddAzure* APIs. When you add these resources to your .NET Aspire app host, they represent an Azure service. The AddAzure* API returns an IResourceBuilder<T> where T is the type of Azure resource. These IResourceBuilder<T> (builder) interfaces provide a fluent API that allows you to configure the underlying Azure resource within the app model. There are APIs for adding new Azure resources, marking resources as existing, and configuring how the resources behave in various execution contexts.

Typical developer experience

When your .NET Aspire app host contains Azure resources, and you run it locally (typical developer F5 or dotnet run experience), the Azure resources are provisioned in your Azure subscription. This allows you as the developer to debug against them locally in the context of your app host.

.NET Aspire aims to minimize costs by defaulting to Basic or Standard Stock Keeping Unit (SKU) for its Azure integrations. While these sensible defaults are provided, you can customize the Azure resources to suit your needs. Additionally, some integrations support emulators or containers, which are useful for local development, testing, and debugging. By default, when you run your app locally, the Azure resources use the actual Azure service. However, you can configure them to use local emulators or containers, avoiding costs associated with the actual Azure service during local development.

Local emulators

Some Azure services can be emulated to run locally. Currently, .NET Aspire supports the following Azure emulators:

Hosting integration Description
Azure Cosmos DB Call AzureCosmosExtensions.RunAsEmulator on the IResourceBuilder<AzureCosmosDBResource> to configure the Cosmos DB resource to be emulated with the NoSQL API.
Azure Event Hubs Call AzureEventHubsExtensions.RunAsEmulator on the IResourceBuilder<AzureEventHubsResource> to configure the Event Hubs resource to be emulated.
Azure Service Bus Call AzureServiceBusExtensions.RunAsEmulator on the IResourceBuilder<AzureServiceBusResource> to configure the Service Bus resource to be emulated with Service Bus emulator.
Azure SignalR Service Call AzureSignalRExtensions.RunAsEmulator on the IResourceBuilder<AzureSignalRResource> to configure the SignalR resource to be emulated with Azure SignalR emulator.
Azure Storage Call AzureStorageExtensions.RunAsEmulator on the IResourceBuilder<AzureStorageResource> to configure the Storage resource to be emulated with Azurite.

To have your Azure resources use the local emulators, chain a call the RunAsEmulator method on the Azure resource builder. This method configures the Azure resource to use the local emulator instead of the actual Azure service.

Important

Calling any of the available RunAsEmulator APIs on an Azure resource builder doesn't effect the publishing manifest. When you publish your app, the generated Bicep file reflects the actual Azure service, not the local emulator.

Local containers

Some Azure resources can be substituted locally using open-source or on-premises containers. To substitute an Azure resource locally in a container, chain a call to the RunAsContainer method on the Azure resource builder. This method configures the Azure resource to use a containerized version of the service for local development and testing, rather than the actual Azure service.

Currently, .NET Aspire supports the following Azure services as containers:

Hosting integration Details
Azure Cache for Redis Call AzureRedisExtensions.RunAsContainer on the IResourceBuilder<AzureRedisCacheResource> to configure it to run locally in a container, based on the docker.io/library/redis image.
Azure PostgreSQL Flexible Server Call AzurePostgresExtensions.RunAsContainer on the IResourceBuilder<AzurePostgresFlexibleServerResource> to configure it to run locally in a container, based on the docker.io/library/postgres image.
Azure SQL Server Call AzureSqlExtensions.RunAsContainer on the IResourceBuilder<AzureSqlServerResource> to configure it to run locally in a container, based on the mcr.microsoft.com/mssql/server image.

Note

Like emulators, calling RunAsContainer on an Azure resource builder doesn't effect the publishing manifest. When you publish your app, the generated Bicep file reflects the actual Azure service, not the local container.

Understand Azure integration APIs

.NET Aspire's strength lies in its ability to provide an amazing developer inner-loop. The Azure integrations are no different. They provide a set of common APIs and patterns that are shared across all Azure resources. These APIs and patterns are designed to make it easy to work with Azure resources in a consistent manner.

In the preceding containers section, you saw how to run Azure services locally in containers. If you're familiar with .NET Aspire, you might wonder how calling AddAzureRedis("redis").RunAsContainer() to get a local docker.io/library/redis container differs from AddRedis("redis")—as they both result in the same local container.

The answer is that there's no difference when running locally. However, when they're published you get different resources:

API Run mode Publish mode
AddAzureRedis("redis").RunAsContainer() Local Redis container Azure Cache for Redis
AddRedis("redis") Local Redis container Azure Container App with Redis image

The same is true for SQL and PostgreSQL services:

API Run mode Publish mode
AddAzurePostgresFlexibleServer("postgres").RunAsContainer() Local PostgreSQL container Azure PostgreSQL Flexible Server
AddPostgres("postgres") Local PostgreSQL container Azure Container App with PostgreSQL image
AddAzureSqlServer("sql").RunAsContainer() Local SQL Server container Azure SQL Server
AddSqlServer("sql") Local SQL Server container Azure Container App with SQL Server image

For more information on the difference between run and publish modes, see .NET Aspire app host: Execution context.

APIs for expressing Azure resources in different modes

The distributed application builder, part of the app host, uses the builder pattern to AddAzure* resources to the app model. Developers can configure these resources and define their behavior in different execution contexts. Azure hosting integrations provide APIs to specify how these resources should be "published" and "run."

When the app host executes, the execution context is used to determine whether the app host is in Run or Publish mode. The naming conventions for these APIs indicate the intended action for the resource.

The following table summarizes the naming conventions used to express Azure resources:

Operation API Description
Publish PublishAsConnectionString<T>(IResourceBuilder<T>) Changes the resource to be published as a connection string reference in the manifest.
Publish PublishAsExisting Uses an existing Azure resource when the application is deployed instead of creating a new one.
Run AzureSqlExtensions.RunAsContainer(IResourceBuilder<AzureSqlServerResource>, Action<IResourceBuilder<SqlServerServerResource>>)
AzureRedisExtensions.RunAsContainer(IResourceBuilder<AzureRedisCacheResource>, Action<IResourceBuilder<RedisResource>>)
RunAsContainer(IResourceBuilder<AzurePostgresFlexibleServerResource>, Action<IResourceBuilder<PostgresServerResource>>)
Configures an equivalent container to run locally. For more information, see Local containers.
Run AzureCosmosExtensions.RunAsEmulator(IResourceBuilder<AzureCosmosDBResource>, Action<IResourceBuilder<AzureCosmosDBEmulatorResource>>)
AzureSignalRExtensions.RunAsEmulator(IResourceBuilder<AzureSignalRResource>, Action<IResourceBuilder<AzureSignalREmulatorResource>>)
AzureStorageExtensions.RunAsEmulator(IResourceBuilder<AzureStorageResource>, Action<IResourceBuilder<AzureStorageEmulatorResource>>)
AzureEventHubsExtensions.RunAsEmulator(IResourceBuilder<AzureEventHubsResource>, Action<IResourceBuilder<AzureEventHubsEmulatorResource>>)
AzureServiceBusExtensions.RunAsEmulator(IResourceBuilder<AzureServiceBusResource>, Action<IResourceBuilder<AzureServiceBusEmulatorResource>>)
Configures the Azure resource to be emulated. For more information, see Local emulators.
Run RunAsExisting Uses an existing resource when the application is running instead of creating a new one.
Publish and Run AsExisting<T>(IResourceBuilder<T>, IResourceBuilder<ParameterResource>, IResourceBuilder<ParameterResource>) Uses an existing resource regardless of the operation.

Note

Not all APIs are available on all Azure resources. For example, some Azure resources can be containerized or emulated, while others can't.

For more information on execution modes, see Execution context.

General run mode API use cases

Use RunAsExisting when you need to dynamically interact with an existing resource during runtime without needing to deploy or update it. Use PublishAsExisting when declaring existing resources as part of a deployment configuration, ensuring the correct scopes and permissions are applied. Finally, use AsExisting<T>(IResourceBuilder<T>, IResourceBuilder<ParameterResource>, IResourceBuilder<ParameterResource>) when declaring existing resources in both configurations, with a requirement to parameterize the references.

You can query whether a resource is marked as an existing resource, by calling the IsExisting(IResource) extension method on the IResource. For more information, see Use existing Azure resources.

Use existing Azure resources

.NET Aspire provides support for referencing existing Azure resources. You mark an existing resource through the PublishAsExisting, RunAsExisting, and AsExisting APIs. These APIs allow developers to reference already-deployed Azure resources, configure them, and generate appropriate deployment manifests using Bicep templates.

Existing resources referenced with these APIs can be enhanced with role assignments and other customizations that are available with .NET Aspire's infrastructure as code capabilities. These APIs are limited to Azure resources that can be deployed with Bicep templates.

Configure existing Azure resources for run mode

The RunAsExisting method is used when a distributed application is executing in "run" mode. In this mode, it assumes that the referenced Azure resource already exists and integrates with it during execution without provisioning the resource. To mark an Azure resource as existing, call the RunAsExisting method on the resource builder. Consider the following example:

var builder = DistributedApplication.CreateBuilder();

var existingServiceBusName = builder.AddParameter("existingServiceBusName");
var existingServiceBusResourceGroup = builder.AddParameter("existingServiceBusResourceGroup");

var serviceBus = builder.AddAzureServiceBus("messaging")
                        .RunAsExisting(existingServiceBusName, existingServiceBusResourceGroup);

serviceBus.AddServiceBusQueue("queue");

The preceding code:

  • Creates a new builder instance.
  • Adds a parameter named existingServiceBusName to the builder.
  • Adds an Azure Service Bus resource named messaging to the builder.
  • Calls the RunAsExisting method on the serviceBus resource builder, passing the existingServiceBusName parameter—alternatively, you can use the string parameter overload.
  • Adds a queue named queue to the serviceBus resource.

By default, the Service Bus parameter reference is assumed to be in the same Azure resource group. However, if it's in a different resource group, you can pass the resource group explicitly as a parameter to correctly specify the appropriate resource grouping.

Configure existing Azure resources for publish mode

The PublishAsExisting method is used in "publish" mode when the intent is to declare and reference an already-existing Azure resource during publish mode. This API facilitates the creation of manifests and templates that include resource definitions that map to existing resources in Bicep.

To mark an Azure resource as existing in for the "publish" mode, call the PublishAsExisting method on the resource builder. Consider the following example:

var builder = DistributedApplication.CreateBuilder();

var existingServiceBusName = builder.AddParameter("existingServiceBusName");
var existingServiceBusResourceGroup = builder.AddParameter("existingServiceBusResourceGroup");

var serviceBus = builder.AddAzureServiceBus("messaging")
                        .PublishAsExisting(existingServiceBusName, existingServiceBusResourceGroup);

serviceBus.AddServiceBusQueue("queue");

The preceding code:

  • Creates a new builder instance.
  • Adds a parameter named existingServiceBusName to the builder.
  • Adds an Azure Service Bus resource named messaging to the builder.
  • Calls the PublishAsExisting method on the serviceBus resource builder, passing the existingServiceBusName parameter—alternatively, you can use the string parameter overload.
  • Adds a queue named queue to the serviceBus resource.

After the app host is executed in publish mode, the generated manifest file will include the existingResourceName parameter, which can be used to reference the existing Azure resource. Consider the following generated partial snippet of the manifest file:

"messaging": {
  "type": "azure.bicep.v0",
  "connectionString": "{messaging.outputs.serviceBusEndpoint}",
  "path": "messaging.module.bicep",
  "params": {
    "existingServiceBusName": "{existingServiceBusName.value}",
    "principalType": "",
    "principalId": ""
  }
},
"queue": {
  "type": "value.v0",
  "connectionString": "{messaging.outputs.serviceBusEndpoint}"
}

For more information on the manifest file, see .NET Aspire manifest format for deployment tool builders.

Additionally, the generated Bicep template includes the existingResourceName parameter, which can be used to reference the existing Azure resource. Consider the following generated Bicep template:

@description('The location for the resource(s) to be deployed.')
param location string = resourceGroup().location

param existingServiceBusName string

param principalType string

param principalId string

resource messaging 'Microsoft.ServiceBus/namespaces@2024-01-01' existing = {
  name: existingServiceBusName
}

resource messaging_AzureServiceBusDataOwner 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: guid(messaging.id, principalId, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '090c5cfd-751d-490a-894a-3ce6f1109419'))
  properties: {
    principalId: principalId
    roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '090c5cfd-751d-490a-894a-3ce6f1109419')
    principalType: principalType
  }
  scope: messaging
}

resource queue 'Microsoft.ServiceBus/namespaces/queues@2024-01-01' = {
  name: 'queue'
  parent: messaging
}

output serviceBusEndpoint string = messaging.properties.serviceBusEndpoint

For more information on the generated Bicep templates, see Customize Azure resources and consider other publishing APIs.

Warning

When interacting with existing resources that require authentication, ensure the authentication strategy that you're configuring in the .NET Aspire application model aligns with the authentication strategy allowed by the existing resource. For example, it's not possible to use managed identity against an existing Azure PostgreSQL resource that isn't configured to allow managed identity. Similarly, if an existing Azure Redis resource disabled access keys, it's not possible to use access key authentication.

Configure existing Azure resources in all modes

The AsExisting<T>(IResourceBuilder<T>, IResourceBuilder<ParameterResource>, IResourceBuilder<ParameterResource>) method is used when the distributed application is running in "run" or "publish" mode. Because the AsExisting method operates in both scenarios, it only supports a parameterized reference to the resource name or resource group name. This approach helps prevent the use of the same resource in both testing and production environments.

To mark an Azure resource as existing, call the AsExisting method on the resource builder. Consider the following example:

var builder = DistributedApplication.CreateBuilder();

var existingServiceBusName = builder.AddParameter("existingServiceBusName");
var existingServiceBusResourceGroup = builder.AddParameter("existingServiceBusResourceGroup");

var serviceBus = builder.AddAzureServiceBus("messaging")
                        .AsExisting(existingServiceBusName, existingServiceBusResourceGroup);

serviceBus.AddServiceBusQueue("queue");

The preceding code:

  • Creates a new builder instance.
  • Adds a parameter named existingServiceBusName to the builder.
  • Adds an Azure Service Bus resource named messaging to the builder.
  • Calls the AsExisting method on the serviceBus resource builder, passing the existingServiceBusName parameter.
  • Adds a queue named queue to the serviceBus resource.

Add existing Azure resources with connection strings

.NET Aspire provides the ability to connect to existing resources, including Azure resources. Expressing connection strings is useful when you have existing Azure resources that you want to use in your .NET Aspire app. The AddConnectionString API is used with the app host's execution context to conditionally add a connection string to the app model.

Note

Connection strings are used to represent a wide range of connection information, including database connections, message brokers, endpoint URIs, and other services. In .NET Aspire nomenclature, the term "connection string" is used to represent any kind of connection information.

Consider the following example, where in publish mode you add an Azure Storage resource while in run mode you add a connection string to an existing Azure Storage:

var builder = DistributedApplication.CreateBuilder(args);

var storage = builder.ExecutionContext.IsPublishMode
    ? builder.AddAzureStorage("storage")
    : builder.AddConnectionString("storage");

builder.AddProject<Projects.Api>("api")
       .WithReference(storage);

// After adding all resources, run the app...

The preceding code:

  • Creates a new builder instance.
  • Adds an Azure Storage resource named storage in "publish" mode.
  • Adds a connection string to an existing Azure Storage named storage in "run" mode.
  • Adds a project named api to the builder.
  • The api project references the storage resource regardless of the mode.

The consuming API project uses the connection string information with no knowledge of how the app host configured it. In "publish" mode, the code adds a new Azure Storage resource—which would be reflected in the deployment manifest accordingly. When in "run" mode the connection string corresponds to a configuration value visible to the app host. It's assumed that all role assignments for the target resource are configured. This means, you'd likely configure an environment variable or a user secret to store the connection string. The configuration is resolved from the ConnectionStrings__storage (or ConnectionStrings:storage) configuration key. These configuration values can be viewed when the app runs. For more information, see Resource details.

Unlike existing resources modeled with the first-class AsExisting API, existing resource modeled as connection strings can't be enhanced with additional role assignments or infrastructure customizations.

Publish as Azure Container App

.NET Aspire allows you to publish primitive resources as Azure Container Apps, a serverless platform that reduces infrastructure management. Supported resource types include:

To publish these resources, use the following APIs:

These APIs configure the resource to be published as an Azure Container App and implicitly call AddAzureContainerAppsInfrastructure(IDistributedApplicationBuilder) to add the necessary infrastructure and Bicep files to your app host. As an example, consider the following code:

var builder = DistributedApplication.CreateBuilder();

var env = builder.AddParameter("env");

var api = builder.AddProject<Projects.AspireApi>("api")
                 .PublishAsAzureContainerApp<Projects.AspireApi>((infra, app) =>
                 {
                     app.Template.Containers[0].Value!.Env.Add(new ContainerAppEnvironmentVariable
                     {
                         Name = "Hello",
                         Value = env.AsProvisioningParameter(infra)
                     });
                 });

The preceding code:

  • Creates a new builder instance.
  • Adds a parameter named env to the builder.
  • Adds a project named api to the builder.
  • Calls the PublishAsAzureContainerApp method on the api resource builder, passing a lambda expression that configures the Azure Container App infrastructure—where infra is the AzureResourceInfrastructure and app is the ContainerApp.
    • Adds an environment variable named Hello to the container app, using the env parameter.
    • The AsProvisioningParameter method is used to treat env as either a new ProvisioningParameter in infrastructure, or reuses an existing bicep parameter if one with the same name already exists.

To configure the Azure Container App environment, see Configure Azure Container Apps environments. For more information, see ContainerApp and AsProvisioningParameter.

Tip

If you're working with Azure Container Apps, you might also be interested in the .NET Aspire Azure Container Registry integration.

Publishing

When you publish your app, the Azure provisioning generated Bicep is used by the Azure Developer CLI to create the Azure resources in your Azure subscription. .NET Aspire outputs a publishing manifest, that's also a vital part of the publishing process. The Azure Developer CLI is a command-line tool that provides a set of commands to manage Azure resources.

For more information on publishing and deployment, see Deploy a .NET Aspire project to Azure Container Apps using the Azure Developer CLI (in-depth guide).