.NET Aspire Azure Blob Storage integration
Includes: Hosting integration and Client integration
Azure Blob Storage is a service for storing large amounts of unstructured data. The .NET Aspire Azure Blob Storage integration enables you to connect to existing Azure Blob Storage instances or create new instances from .NET applications.
Hosting integration
The .NET Aspire Azure Storage hosting integration models the various storage resources as the following types:
- AzureStorageResource: Represents an Azure Storage resource.
- AzureStorageEmulatorResource: Represents an Azure Storage emulator resource (Azurite).
- AzureBlobStorageResource: Represents an Azure Blob storage resource.
- AzureQueueStorageResource: Represents an Azure Queue storage resource.
- AzureTableStorageResource: Represents an Azure Table storage resource.
To access these types and APIs for expressing them, add the 📦 Aspire.Hosting.Azure.Storage NuGet package in the app host project.
dotnet add package Aspire.Hosting.Azure.Storage
For more information, see dotnet add package or Manage package dependencies in .NET applications.
Add Azure Storage resource
In your app host project, call AddAzureStorage to add and return an Azure Storage resource builder.
var builder = DistributedApplication.CreateBuilder(args);
var storage = builder.AddAzureStorage("storage");
// An Azure Storage resource is required to add any of the following:
//
// - Azure Blob storage resource.
// - Azure Queue storage resource.
// - Azure Table storage resource.
// After adding all resources, run the app...
When you add an AzureStorageResource
to the app host, it exposes other useful APIs to add Azure Blob, Queue, and Table storage resources. In other words, you must add an AzureStorageResource
before adding any of the other storage resources.
Important
When you call AddAzureStorage, it implicitly calls AddAzureProvisioning—which adds support for generating Azure resources dynamically during app startup. The app must configure the appropriate subscription and location. For more information, see Local provisioning: Configuration.
Generated provisioning Bicep
If you're new to Bicep, it's a domain-specific language for defining Azure resources. With .NET Aspire, you don't need to write Bicep by-hand, instead the provisioning APIs generate Bicep for you. When you publish your app, the generated Bicep is output alongside the manifest file. When you add an Azure Storage resource, the following Bicep is generated:
Toggle Azure Storage Bicep.
@description('The location for the resource(s) to be deployed.')
param location string = resourceGroup().location
param principalId string
param principalType string
resource storage 'Microsoft.Storage/storageAccounts@2024-01-01' = {
name: take('storage${uniqueString(resourceGroup().id)}', 24)
kind: 'StorageV2'
location: location
sku: {
name: 'Standard_GRS'
}
properties: {
accessTier: 'Hot'
allowSharedKeyAccess: false
minimumTlsVersion: 'TLS1_2'
networkAcls: {
defaultAction: 'Allow'
}
}
tags: {
'aspire-resource-name': 'storage'
}
}
resource blobs 'Microsoft.Storage/storageAccounts/blobServices@2024-01-01' = {
name: 'default'
parent: storage
}
resource storage_StorageBlobDataContributor 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: guid(storage.id, principalId, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe'))
properties: {
principalId: principalId
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')
principalType: principalType
}
scope: storage
}
resource storage_StorageTableDataContributor 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: guid(storage.id, principalId, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3'))
properties: {
principalId: principalId
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3')
principalType: principalType
}
scope: storage
}
resource storage_StorageQueueDataContributor 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: guid(storage.id, principalId, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '974c5e8b-45b9-4653-ba55-5f855dd0fb88'))
properties: {
principalId: principalId
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '974c5e8b-45b9-4653-ba55-5f855dd0fb88')
principalType: principalType
}
scope: storage
}
output blobEndpoint string = storage.properties.primaryEndpoints.blob
output queueEndpoint string = storage.properties.primaryEndpoints.queue
output tableEndpoint string = storage.properties.primaryEndpoints.table
The preceding Bicep is a module that provisions an Azure Storage account with the following defaults:
kind
: The kind of storage account. The default isStorageV2
.sku
: The SKU of the storage account. The default isStandard_GRS
.properties
: The properties of the storage account:accessTier
: The access tier of the storage account. The default isHot
.allowSharedKeyAccess
: A boolean value that indicates whether the storage account permits requests to be authorized with the account access key. The default isfalse
.minimumTlsVersion
: The minimum supported TLS version for the storage account. The default isTLS1_2
.networkAcls
: The network ACLs for the storage account. The default is{ defaultAction: 'Allow' }
.
In addition to the storage account, it also provisions a blob container.
The following role assignments are added to the storage account to grant your application access. See the built-in Azure role-based access control (Azure RBAC) roles for more information:
Role / ID | Description |
---|---|
Storage Blob Data Contributorba92f5b4-2d11-453d-a403-e96b0029c9fe |
Read, write, and delete Azure Storage containers and blobs. |
Storage Table Data Contributor0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3 |
Read, write, and delete Azure Storage tables and entities. |
Storage Queue Data Contributor974c5e8b-45b9-4653-ba55-5f855dd0fb88 |
Read, write, and delete Azure Storage queues and queue messages. |
The generated Bicep is a starting point and can be customized to meet your specific requirements.
Customize provisioning infrastructure
All .NET Aspire Azure resources are subclasses of the AzureProvisioningResource type. This type enables the customization of the generated Bicep by providing a fluent API to configure the Azure resources—using the ConfigureInfrastructure<T>(IResourceBuilder<T>, Action<AzureResourceInfrastructure>) API. For example, you can configure the kind
, sku
, properties
, and more. The following example demonstrates how to customize the Azure Storage resource:
builder.AddAzureStorage("storage")
.ConfigureInfrastructure(infra =>
{
var storageAccount = infra.GetProvisionableResources()
.OfType<StorageAccount>()
.Single();
storageAccount.AccessTier = StorageAccountAccessTier.Cool;
storageAccount.Sku = new StorageSku { Name = StorageSkuName.PremiumZrs };
storageAccount.Tags.Add("ExampleKey", "Example value");
});
The preceding code:
- Chains a call to the ConfigureInfrastructure API:
- The
infra
parameter is an instance of the AzureResourceInfrastructure type. - The provisionable resources are retrieved by calling the GetProvisionableResources() method.
- The single StorageAccount is retrieved.
- The StorageAccount.AccessTier is assigned to StorageAccountAccessTier.Cool.
- The StorageAccount.Sku is assigned to a new StorageSku with a
Name
of PremiumZrs. - A tag is added to the storage account with a key of
ExampleKey
and a value ofExample value
.
- The
There are many more configuration options available to customize the Azure Storage resource. For more information, see Azure.Provisioning.Storage.
Connect to an existing Azure Storage account
You might have an existing Azure Storage account that you want to connect to. Instead of representing a new Azure Storage resource, you can add a connection string to the app host. To add a connection to an existing Azure Storage account, call the AddConnectionString method:
var builder = DistributedApplication.CreateBuilder(args);
var blobs = builder.AddConnectionString("blobs");
builder.AddProject<Projects.WebApplication>("web")
.WithReference(blobs);
// After adding all resources, run the app...
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.
The connection string is configured in the app host's configuration, typically under User Secrets, under the ConnectionStrings
section. The app host injects this connection string as an environment variable into all dependent resources, for example:
{
"ConnectionStrings": {
"blobs": "https://{account_name}.blob.core.windows.net/"
}
}
The dependent resource can access the injected connection string by calling the GetConnectionString method, and passing the connection name as the parameter, in this case "blobs"
. The GetConnectionString
API is shorthand for IConfiguration.GetSection("ConnectionStrings")[name]
.
Add Azure Storage emulator resource
To add an Azure Storage emulator resource, chain a call on an IResourceBuilder<AzureStorageResource>
to the RunAsEmulator API:
var builder = DistributedApplication.CreateBuilder(args);
var storage = builder.AddAzureStorage("storage")
.RunAsEmulator();
// After adding all resources, run the app...
When you call RunAsEmulator
, it configures your storage resources to run locally using an emulator. The emulator in this case is Azurite. The Azurite open-source emulator provides a free local environment for testing your Azure Blob, Queue Storage, and Table Storage apps and it's a perfect companion to the .NET Aspire Azure hosting integration. Azurite isn't installed, instead, it's accessible to .NET Aspire as a container. When you add a container to the app host, as shown in the preceding example with the mcr.microsoft.com/azure-storage/azurite
image, it creates and starts the container when the app host starts. For more information, see Container resource lifecycle.
Configure Azurite container
There are various configurations available to container resources, for example, you can configure the container's ports, environment variables, it's lifetime, and more.
Configure Azurite container ports
By default, the Azurite container when configured by .NET Aspire, exposes the following endpoints:
Endpoint | Container port | Host port |
---|---|---|
blob |
10000 | dynamic |
queue |
10001 | dynamic |
table |
10002 | dynamic |
The port that they're listening on is dynamic by default. When the container starts, the ports are mapped to a random port on the host machine. To configure the endpoint ports, chain calls on the container resource builder provided by the RunAsEmulator
method as shown in the following example:
var builder = DistributedApplication.CreateBuilder(args);
var storage = builder.AddAzureStorage("storage").RunAsEmulator(
azurite =>
{
azurite.WithBlobPort("blob", 27000)
.WithQueuePort("queue", 27001)
.WithTablePort("table", 27002);
});
// After adding all resources, run the app...
The preceding code configures the Azurite container's existing blob
, queue
, and table
endpoints to listen on ports 27000
, 27001
, and 27002
, respectively. The Azurite container's ports are mapped to the host ports as shown in the following table:
Endpoint name | Port mapping (container:host ) |
---|---|
blob |
10000:27000 |
queue |
10001:27001 |
table |
10002:27002 |
Configure Azurite container with persistent lifetime
To configure the Azurite container with a persistent lifetime, call the WithLifetime method on the Azurite container resource and pass ContainerLifetime.Persistent:
var builder = DistributedApplication.CreateBuilder(args);
var storage = builder.AddAzureStorage("storage").RunAsEmulator(
azurite =>
{
azurite.WithLifetime(ContainerLifetime.Persistent);
});
// After adding all resources, run the app...
For more information, see Container resource lifetime.
Configure Azurite container with data volume
To add a data volume to the Azure Storage emulator resource, call the WithDataVolume method on the Azure Storage emulator resource:
var builder = DistributedApplication.CreateBuilder(args);
var storage = builder.AddAzureStorage("storage").RunAsEmulator(
azurite =>
{
azurite.WithDataVolume();
});
// After adding all resources, run the app...
The data volume is used to persist the Azurite data outside the lifecycle of its container. The data volume is mounted at the /data
path in the Azurite container and when a name
parameter isn't provided, the name is formatted as .azurite/{resource name}
. For more information on data volumes and details on why they're preferred over bind mounts, see Docker docs: Volumes.
Configure Azurite container with data bind mount
To add a data bind mount to the Azure Storage emulator resource, call the WithDataBindMount method:
var builder = DistributedApplication.CreateBuilder(args);
var storage = builder.AddAzureStorage("storage").RunAsEmulator(
azurite =>
{
azurite.WithDataBindMount("../Azurite/Data");
});
// After adding all resources, run the app...
Important
Data bind mounts have limited functionality compared to volumes, which offer better performance, portability, and security, making them more suitable for production environments. However, bind mounts allow direct access and modification of files on the host system, ideal for development and testing where real-time changes are needed.
Data bind mounts rely on the host machine's filesystem to persist the Azurite data across container restarts. The data bind mount is mounted at the ../Azurite/Data
path on the host machine relative to the app host directory (IDistributedApplicationBuilder.AppHostDirectory) in the Azurite container. For more information on data bind mounts, see Docker docs: Bind mounts.
Connect to storage resources
When the .NET Aspire app host runs, the storage resources can be accessed by external tools, such as the Azure Storage Explorer. If your storage resource is running locally using Azurite, it will automatically be picked up by the Azure Storage Explorer.
Note
The Azure Storage Explorer discovers Azurite storage resources assuming the default ports are used. If you've configured the Azurite container to use different ports, you'll need to configure the Azure Storage Explorer to connect to the correct ports.
To connect to the storage resource from Azure Storage Explorer, follow these steps:
Run the .NET Aspire app host.
Open the Azure Storage Explorer.
View the Explorer pane.
Select the Refresh all link to refresh the list of storage accounts.
Expand the Emulator & Attached node.
Expand the Storage Accounts node.
You should see a storage account with your resource's name as a prefix:
You're free to explore the storage account and its contents using the Azure Storage Explorer. For more information on using the Azure Storage Explorer, see Get started with Storage Explorer.
Add Azure Blob Storage resource
In your app host project, register the Azure Blob Storage integration by chaining a call to AddBlobs on the IResourceBuilder<IAzureStorageResource>
instance returned by AddAzureStorage. The following example demonstrates how to add an Azure Blob Storage resource named storage
and a blob container named blobs
:
var builder = DistributedApplication.CreateBuilder(args);
var blobs = builder.AddAzureStorage("storage")
.RunAsEmulator();
.AddBlobs("blobs");
builder.AddProject<Projects.ExampleProject>()
.WithReference(blobs)
.WaitFor(blobs);
// After adding all resources, run the app...
The preceding code:
- Adds an Azure Storage resource named
storage
. - Chains a call to RunAsEmulator to configure the storage resource to run locally using an emulator. The emulator in this case is Azurite.
- Adds a blob container named
blobs
to the storage resource. - Adds the
blobs
resource to theExampleProject
and waits for it to be ready before starting the project.
Hosting integration health checks
The Azure Storage hosting integration automatically adds a health check for the storage resource. It's added only when running as an emulator, and verifies the Azurite container is running and that a connection can be established to it. The hosting integration relies on the 📦 AspNetCore.HealthChecks.Azure.Storage.Blobs NuGet package.
Client integration
To get started with the .NET Aspire Azure Blob Storage client integration, install the 📦 Aspire.Azure.Storage.Blobs NuGet package in the client-consuming project, that is, the project for the application that uses the Azure Blob Storage client. The Azure Blob Storage client integration registers a BlobServiceClient instance that you can use to interact with Azure Blob Storage.
dotnet add package Aspire.Azure.Storage.Blobs
Add Azure Blob Storage client
In the Program.cs file of your client-consuming project, call the AddAzureBlobClient extension method on any IHostApplicationBuilder to register a BlobServiceClient
for use via the dependency injection container. The method takes a connection name parameter.
builder.AddAzureBlobClient("blobs");
You can then retrieve the BlobServiceClient
instance using dependency injection. For example, to retrieve the client from a service:
public class ExampleService(BlobServiceClient client)
{
// Use client...
}
Configuration
The .NET Aspire Azure Blob Storage integration provides multiple options to configure the BlobServiceClient
based on the requirements and conventions of your project.
Use a connection string
When using a connection string from the ConnectionStrings
configuration section, you can provide the name of the connection string when calling AddAzureBlobClient:
builder.AddAzureBlobClient("blobs");
Then the connection string is retrieved from the ConnectionStrings
configuration section, and two connection formats are supported:
Service URI
The recommended approach is to use a ServiceUri
, which works with the AzureStorageBlobsSettings.Credential property to establish a connection. If no credential is configured, the Azure.Identity.DefaultAzureCredential is used.
{
"ConnectionStrings": {
"blobs": "https://{account_name}.blob.core.windows.net/"
}
}
Connection string
Alternatively, an Azure Storage connection string can be used.
{
"ConnectionStrings": {
"blobs": "AccountName=myaccount;AccountKey=myaccountkey"
}
}
For more information, see Configure Azure Storage connection strings.
Use configuration providers
The .NET Aspire Azure Blob Storage integration supports Microsoft.Extensions.Configuration. It loads the AzureStorageBlobsSettings and BlobClientOptions from configuration by using the Aspire:Azure:Storage:Blobs
key. The following snippet is an example of a appsettings.json file that configures some of the options:
{
"Aspire": {
"Azure": {
"Storage": {
"Blobs": {
"DisableHealthChecks": true,
"DisableTracing": false,
"ClientOptions": {
"Diagnostics": {
"ApplicationId": "myapp"
}
}
}
}
}
}
}
For the complete Azure Blob Storage client integration JSON schema, see Aspire.Azure.Storage.Blobs/ConfigurationSchema.json.
Use inline delegates
You can also pass the Action<AzureStorageBlobsSettings> configureSettings
delegate to set up some or all the options inline, for example to configure health checks:
builder.AddAzureBlobClient(
"blobs",
settings => settings.DisableHealthChecks = true);
You can also set up the BlobClientOptions using the Action<IAzureClientBuilder<BlobServiceClient, BlobClientOptions>> configureClientBuilder
delegate, the second parameter of the AddAzureBlobClient
method. For example, to set the first part of user-agent headers for all requests issues by this client:
builder.AddAzureBlobClient(
"blobs",
configureClientBuilder: clientBuilder =>
clientBuilder.ConfigureOptions(
options => options.Diagnostics.ApplicationId = "myapp"));
Client integration health checks
By default, .NET Aspire integrations enable health checks for all services. For more information, see .NET Aspire integrations overview.
The .NET Aspire Azure Blob Storage integration:
- Adds the health check when AzureStorageBlobsSettings.DisableHealthChecks is
false
, which attempts to connect to the Azure Blob Storage. - Integrates with the
/health
HTTP endpoint, which specifies all registered health checks must pass for app to be considered ready to accept traffic.
Observability and telemetry
.NET Aspire integrations automatically set up Logging, Tracing, and Metrics configurations, which are sometimes known as the pillars of observability. For more information about integration observability and telemetry, see .NET Aspire integrations overview. Depending on the backing service, some integrations may only support some of these features. For example, some integrations support logging and tracing, but not metrics. Telemetry features can also be disabled using the techniques presented in the Configuration section.
Logging
The .NET Aspire Azure Blob Storage integration uses the following log categories:
Azure.Core
Azure.Identity
Tracing
The .NET Aspire Azure Blob Storage integration emits the following tracing activities using OpenTelemetry:
Azure.Storage.Blobs.BlobContainerClient
Metrics
The .NET Aspire Azure Blob Storage integration currently doesn't support metrics by default due to limitations with the Azure SDK.
See also
.NET Aspire