Set up Azure App Configuration in the AppHost
This article is the reference for the Aspire Azure App Configuration Hosting integration. It enumerates the AppHost APIs — with examples for both AppHost.cs and apphost.ts — that you use to model an Azure App Configuration store in your AppHost project.
If you’re new to the Azure App Configuration integration, start with the Get started with Azure App Configuration integrations guide. For how consuming apps read the connection information this page exposes, see Connect to Azure App Configuration.
Installation
Section titled “Installation”To start building an Aspire app that uses Azure App Configuration, install the 📦 Aspire.Hosting.Azure.AppConfiguration NuGet package:
aspire add azure-app-configurationLearn more about aspire add in the command reference.
Or, choose a manual installation approach:
#:package Aspire.Hosting.Azure.AppConfiguration@*<PackageReference Include="Aspire.Hosting.Azure.AppConfiguration" Version="*" />aspire add azure-app-configurationLearn more about aspire add in the command reference.
This updates your aspire.config.json with the Azure App Configuration hosting integration package:
{ "packages": { "Aspire.Hosting.Azure.AppConfiguration": "13.3.0" }}Add Azure App Configuration resource
Section titled “Add Azure App Configuration resource”Once you’ve installed the hosting integration in your AppHost project, you can add an Azure App Configuration resource as shown in the following examples:
var builder = DistributedApplication.CreateBuilder(args);
var appConfig = builder.AddAzureAppConfiguration("config");
builder.AddProject<Projects.WebApplication>("web") .WithReference(appConfig);
// After adding all resources, run the app...
builder.Build().Run();import { createBuilder } from './.modules/aspire.js';
const builder = await createBuilder();
const appConfig = await builder.addAzureAppConfiguration("config");
await builder.addProject("web", "../WebApplication/WebApplication.csproj") .withReference(appConfig);
// After adding all resources, run the app...
await builder.build().run();Provisioning-generated Bicep
Section titled “Provisioning-generated Bicep”If you’re new to Bicep, it’s a domain-specific language for defining Azure resources. With 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 App Configuration resource, the following Bicep is generated:
@description('The location for the resource(s) to be deployed.')param location string = resourceGroup().location
resource config 'Microsoft.AppConfiguration/configurationStores@2024-06-01' = { name: take('config-${uniqueString(resourceGroup().id)}', 50) location: location properties: { disableLocalAuth: true } sku: { name: 'standard' } tags: { 'aspire-resource-name': 'config' }}
output appConfigEndpoint string = config.properties.endpoint
output name string = config.nameThe preceding Bicep is a module that provisions an Azure App Configuration store. Additionally, role assignments are created for the Azure resource in a separate module:
@description('The location for the resource(s) to be deployed.')param location string = resourceGroup().location
param config_outputs_name string
param principalType string
param principalId string
resource config 'Microsoft.AppConfiguration/configurationStores@2024-06-01' existing = { name: config_outputs_name}
resource config_AppConfigurationDataOwner 'Microsoft.Authorization/roleAssignments@2022-04-01' = { name: guid(config.id, principalId, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5ae67dd6-50cb-40e7-96ff-dc2bfa4b606b')) properties: { principalId: principalId roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5ae67dd6-50cb-40e7-96ff-dc2bfa4b606b') principalType: principalType } scope: config}The generated Bicep is a starting point and is influenced by changes to the provisioning infrastructure in C#. Customizations to the Bicep file directly will be overwritten, so make changes through the C# provisioning APIs to ensure they’re reflected in the generated files.
Customize provisioning infrastructure
Section titled “Customize provisioning infrastructure”All 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 API (or configureInfrastructure in TypeScript). For example, you can configure the SKU, enable purge protection, add tags, and more:
var builder = DistributedApplication.CreateBuilder(args);
builder.AddAzureAppConfiguration("config") .ConfigureInfrastructure(infra => { var appConfigStore = infra.GetProvisionableResources() .OfType<AppConfigurationStore>() .Single();
appConfigStore.SkuName = "Free"; appConfigStore.EnablePurgeProtection = true; appConfigStore.Tags.Add("ExampleKey", "Example value"); });import { createBuilder } from './.modules/aspire.js';
const builder = await createBuilder();
await builder.addAzureAppConfiguration("config") .configureInfrastructure(async infra => { const resources = await infra.getProvisionableResources(); const store = resources.find(r => r.$type === 'AppConfigurationStore'); if (store) { store.skuName = "Free"; store.enablePurgeProtection = true; store.tags = { ...store.tags, ExampleKey: "Example value" }; } });The preceding code:
- Chains a call to the
ConfigureInfrastructureAPI:- The
infraparameter is an instance of theAzureResourceInfrastructuretype. - The provisionable resources are retrieved by calling the
GetProvisionableResourcesmethod. - The single
AppConfigurationStoreis retrieved. - The
AppConfigurationStore.SkuNameis assigned toFree. - A tag is added to the App Configuration store with a key of
ExampleKeyand a value ofExample value.
- The
There are many more configuration options available to customize the Azure App Configuration resource. For more information, see Azure.Provisioning customization.
Add Azure App Configuration emulator resource
Section titled “Add Azure App Configuration emulator resource”Microsoft provides the Azure App Configuration emulator for developers who want a local, lightweight implementation of the Azure App Configuration service to code and test against. In Aspire, you can use this emulator by calling the RunAsEmulator method (or runAsEmulator) when you add your resource:
var builder = DistributedApplication.CreateBuilder(args);
var appConfig = builder.AddAzureAppConfiguration("config") .RunAsEmulator();
// After adding all resources, run the app...
builder.Build().Run();import { createBuilder } from './.modules/aspire.js';
const builder = await createBuilder();
const appConfig = await builder.addAzureAppConfiguration("config") .runAsEmulator();
// After adding all resources, run the app...
await builder.build().run();The Azure App Configuration emulator isn’t installed on your local computer. Instead, it’s accessible to Aspire as a container. The RunAsEmulator method creates and starts the container when the AppHost starts using the azure-app-configuration/app-configuration-emulator image.
Configure Azure App Configuration emulator container
Section titled “Configure Azure App Configuration emulator container”There are various configurations available to container resources. For example, you can configure the container’s port, lifetime, and data persistence.
Configure Azure App Configuration emulator host port
Section titled “Configure Azure App Configuration emulator host port”By default, Aspire assigns a random host port for the emulator container. If you want to use a specific port, chain calls on the container resource builder provided by the RunAsEmulator method as shown in the following example:
var builder = DistributedApplication.CreateBuilder(args);
var appConfig = builder.AddAzureAppConfiguration("config") .RunAsEmulator(emulator => { emulator.WithHostPort(28000); });
// After adding all resources, run the app...import { createBuilder } from './.modules/aspire.js';
const builder = await createBuilder();
const appConfig = await builder.addAzureAppConfiguration("config") .runAsEmulator({ configureEmulator: async emulator => { await emulator.withHostPort({ port: 28000 }); } });
// After adding all resources, run the app...The preceding code configures the emulator container’s endpoint to listen on port 28000.
Configure Azure App Configuration emulator with persistent lifetime
Section titled “Configure Azure App Configuration emulator with persistent lifetime”To configure the emulator container with a persistent lifetime, call the WithLifetime method (or withLifetime) on the emulator container resource and pass ContainerLifetime.Persistent:
var builder = DistributedApplication.CreateBuilder(args);
var appConfig = builder.AddAzureAppConfiguration("config") .RunAsEmulator(emulator => { emulator.WithLifetime(ContainerLifetime.Persistent); });
// After adding all resources, run the app...import { createBuilder, ContainerLifetime } from './.modules/aspire.js';
const builder = await createBuilder();
const appConfig = await builder.addAzureAppConfiguration("config") .runAsEmulator({ configureEmulator: async emulator => { await emulator.withLifetime(ContainerLifetime.Persistent); } });
// After adding all resources, run the app...Configure Azure App Configuration emulator with data volume
Section titled “Configure Azure App Configuration emulator with data volume”By default, the Azure App Configuration emulator doesn’t persist data between container restarts. To enable persistent storage using a Docker volume, call the WithDataVolume method (or withDataVolume) on the emulator resource:
var builder = DistributedApplication.CreateBuilder(args);
var appConfig = builder.AddAzureAppConfiguration("config") .RunAsEmulator(emulator => { emulator.WithDataVolume(); });
// After adding all resources, run the app...import { createBuilder } from './.modules/aspire.js';
const builder = await createBuilder();
const appConfig = await builder.addAzureAppConfiguration("config") .runAsEmulator({ configureEmulator: async emulator => { await emulator.withDataVolume(); } });
// After adding all resources, run the app...The data volume is used to persist the emulator data outside the lifecycle of its container, ensuring configuration values survive container restarts. The data volume is mounted at the /data path in the emulator container. For more information on data volumes and details on why they’re preferred over bind mounts, see Docker docs: Volumes.
Configure Azure App Configuration emulator with data bind mount
Section titled “Configure Azure App Configuration emulator with data bind mount”To persist emulator data to a specific directory on your host machine, call the WithDataBindMount method (or withDataBindMount). This is useful when you want direct access to the data files on your host system:
var builder = DistributedApplication.CreateBuilder(args);
var appConfig = builder.AddAzureAppConfiguration("config") .RunAsEmulator(emulator => { emulator.WithDataBindMount("../Emulator/Data"); });
// After adding all resources, run the app...import { createBuilder } from './.modules/aspire.js';
const builder = await createBuilder();
const appConfig = await builder.addAzureAppConfiguration("config") .runAsEmulator({ configureEmulator: async emulator => { await emulator.withDataBindMount({ path: "../Emulator/Data" }); } });
// After adding all resources, run the app...Data bind mounts rely on the host machine’s filesystem to persist the emulator data across container restarts. The data bind mount is mounted at the ../Emulator/Data path on the host machine relative to the AppHost directory in the emulator container. For more information on data bind mounts, see Docker docs: Bind mounts.
Use existing Azure App Configuration resource
Section titled “Use existing Azure App Configuration resource”You might have an existing Azure App Configuration store that you want to connect to. Call the AsExisting method (or asExisting) with the config store name and resource group parameters to connect to an existing store:
var builder = DistributedApplication.CreateBuilder(args);
var configName = builder.AddParameter("configName");var configResourceGroupName = builder.AddParameter("configResourceGroupName");
var appConfig = builder.AddAzureAppConfiguration("config") .AsExisting(configName, configResourceGroupName);
// After adding all resources, run the app...
builder.Build().Run();import { createBuilder } from './.modules/aspire.js';
const builder = await createBuilder();
const configName = await builder.addParameter("configName");const configResourceGroupName = await builder.addParameter("configResourceGroupName");
const appConfig = await builder.addAzureAppConfiguration("config") .asExisting(configName, { resourceGroup: configResourceGroupName });
// After adding all resources, run the app...
await builder.build().run();For more information, see Use existing Azure resources.
Connect to existing Azure App Configuration store
Section titled “Connect to existing Azure App Configuration store”An alternative approach to using the AsExisting API enables the addition of a connection string instead, where the AppHost uses configuration to resolve the connection information. To add a connection to an existing Azure App Configuration store, call the AddConnectionString method:
var builder = DistributedApplication.CreateBuilder(args);
var config = builder.AddConnectionString("config");
builder.AddProject<Projects.WebApplication>("web") .WithReference(config);
// After adding all resources, run the app...
builder.Build().Run();import { createBuilder } from './.modules/aspire.js';
const builder = await createBuilder();
const config = await builder.addConnectionString("config");
await builder.addProject("web", "../WebApplication/WebApplication.csproj") .withReference(config);
// After adding all resources, run the app...
await builder.build().run();The connection string is configured in the AppHost’s configuration, typically under User Secrets, under the ConnectionStrings section:
{ "ConnectionStrings": { "config": "https://{store_name}.azconfig.io" }}Role-based access control
Section titled “Role-based access control”By default, Aspire provisions the AppConfigurationDataOwner role so your app can read and write configuration values. You can customize the roles assigned using the WithRoleAssignments method (or withRoleAssignments) to grant least-privilege access. For example, to grant read-only access:
var builder = DistributedApplication.CreateBuilder(args);
var appConfig = builder.AddAzureAppConfiguration("config");
builder.AddProject<Projects.WebApplication>("web") .WithReference(appConfig) .WithRoleAssignments(appConfig, AzureAppConfigurationRole.AppConfigurationDataReader);
// After adding all resources, run the app...
builder.Build().Run();import { createBuilder, AzureAppConfigurationRole } from './.modules/aspire.js';
const builder = await createBuilder();
const appConfig = await builder.addAzureAppConfiguration("config");
await appConfig.withRoleAssignments(appConfig, [ AzureAppConfigurationRole.AppConfigurationDataReader]);
// After adding all resources, run the app...
await builder.build().run();The available roles are defined in the AzureAppConfigurationRole enum:
| Role | Description |
|---|---|
AppConfigurationDataOwner | Full access to App Configuration data, including read, write, and delete operations |
AppConfigurationDataReader | Read-only access to App Configuration data |
Connection properties
Section titled “Connection properties”For the full reference of Azure App Configuration connection properties — and how consuming apps in C#, TypeScript, Python, and Go read them — see Connect to Azure App Configuration.
Hosting integration health checks
Section titled “Hosting integration health checks”The Azure App Configuration hosting integration doesn’t currently register a hosting-level health check. Client health checks are configured through the C# client integration.