Watch Aspire live streamsDocumentazioneProva Aspire
Watch Aspire live streamsDocumentazioneProva

Customize Azure resources

Questi contenuti non sono ancora disponibili nella tua lingua.

Azure logo

When working with Azure integrations in Aspire, you often need to customize the provisioned infrastructure beyond the default settings. The same customization patterns apply across Azure hosting integrations such as Storage, Service Bus, Key Vault, user-assigned identities, Azure Container Apps, and Azure App Service. C# AppHosts use Azure Provisioning SDK types directly; in Aspire 13.6 and later, TypeScript AppHosts can opt into typed provisioning proxies for supported SDK models.

For target-specific generated resource customization, see Deploy to Azure Container Apps and Deploy to Azure App Service.

Use AsExisting, RunAsExisting, and PublishAsExisting when you want Aspire to reference an Azure resource that already exists instead of provisioning a new one.

apphost.mts
import {
function createBuilder(): IDistributedApplicationBuilder

Creates a new distributed application builder

createBuilder
} from './.aspire/modules/aspire.mjs';
const
const builder: IDistributedApplicationBuilder
builder
= await
function createBuilder(): IDistributedApplicationBuilder

Creates a new distributed application builder

createBuilder
();
const
const existingBusName: ParameterResource
existingBusName
= await
const builder: IDistributedApplicationBuilder
builder
.
IDistributedApplicationBuilder.addParameter(name: string, options?: {
value?: string;
publishValueAsDefault?: boolean;
secret?: boolean;
}): ParameterResource (+1 overload)

Adds a parameter resource

addParameter
("existingServiceBusName");
const
const existingBusResourceGroup: ParameterResource
existingBusResourceGroup
= await
const builder: IDistributedApplicationBuilder
builder
.
IDistributedApplicationBuilder.addParameter(name: string, options?: {
value?: string;
publishValueAsDefault?: boolean;
secret?: boolean;
}): ParameterResource (+1 overload)

Adds a parameter resource

addParameter
("existingServiceBusResourceGroup");
const
const serviceBus: AzureServiceBusResource
serviceBus
= await
const builder: IDistributedApplicationBuilder
builder
.
IDistributedApplicationBuilder.addAzureServiceBus(name: string): AzureServiceBusResource

Adds an Azure Service Bus Namespace resource to the application model. This resource can be used to create queue, topic, and subscription resources.

addAzureServiceBus
("messaging");
await
const serviceBus: AzureServiceBusResource
serviceBus
.
AzureBicepResource.publishAsExisting(name: string | ParameterResource, options?: {
resourceGroup?: string | ParameterResource;
} | undefined): AzureServiceBusResource (+1 overload)

Marks the resource as an existing resource when the application is deployed.

publishAsExisting
(
const existingBusName: ParameterResource
existingBusName
, {
resourceGroup?: string | ParameterResource | undefined
resourceGroup
:
const existingBusResourceGroup: ParameterResource
existingBusResourceGroup
});
await
const builder: IDistributedApplicationBuilder
builder
.
IDistributedApplicationBuilder.build(): DistributedApplication

Builds the distributed application

build
().
DistributedApplication.run(cancellationToken?: cancellationToken): void

Runs the distributed application

run
();

Use RunAsExisting when only local run mode should use the existing resource, PublishAsExisting when only deployed Azure environments should use it, and AsExisting when both modes should point at the same Azure resource.

Aspire automatically assigns Azure RBAC roles based on how resources reference one another. When you need to opt out of those defaults before applying different permissions, use ClearDefaultRoleAssignments.

apphost.mts
import {
function createBuilder(): IDistributedApplicationBuilder

Creates a new distributed application builder

createBuilder
} from './.aspire/modules/aspire.mjs';
const
const builder: IDistributedApplicationBuilder
builder
= await
function createBuilder(): IDistributedApplicationBuilder

Creates a new distributed application builder

createBuilder
();
const
const serviceBus: AzureServiceBusResource
serviceBus
= await
const builder: IDistributedApplicationBuilder
builder
.
IDistributedApplicationBuilder.addAzureServiceBus(name: string): AzureServiceBusResource

Adds an Azure Service Bus Namespace resource to the application model. This resource can be used to create queue, topic, and subscription resources.

addAzureServiceBus
("messaging");
await
const serviceBus: AzureServiceBusResource
serviceBus
.
AzureBicepResource.clearDefaultRoleAssignments(): IAzureResource

Clears all default role assignments for the specified Azure resource.

clearDefaultRoleAssignments
();
await
const builder: IDistributedApplicationBuilder
builder
.
IDistributedApplicationBuilder.build(): DistributedApplication

Builds the distributed application

build
().
DistributedApplication.run(cancellationToken?: cancellationToken): void

Runs the distributed application

run
();

For built-in and custom RBAC guidance, see Manage Azure role assignments.

Azure resources expose output references for values that Azure assigns during provisioning. Use those references when another resource, deployment step, or app setting needs the resolved Azure value.

apphost.mts
import {
function createBuilder(): IDistributedApplicationBuilder

Creates a new distributed application builder

createBuilder
} from './.aspire/modules/aspire.mjs';
const
const builder: IDistributedApplicationBuilder
builder
= await
function createBuilder(): IDistributedApplicationBuilder

Creates a new distributed application builder

createBuilder
();
const
const identity: AzureUserAssignedIdentityResource
identity
= await
const builder: IDistributedApplicationBuilder
builder
.
IDistributedApplicationBuilder.addAzureUserAssignedIdentity(name: string): AzureUserAssignedIdentityResource

Adds an Azure user‑assigned identity resource to the application model.

addAzureUserAssignedIdentity
("identity");
const
const clientId: BicepOutputReference
clientId
= await
const identity: AzureUserAssignedIdentityResource
identity
.
AzureUserAssignedIdentityResource.getOutput(name: string): BicepOutputReference

Gets a reference to an output from a bicep template.

getOutput
("clientId");
const
const api: ProjectResource
api
= await
const builder: IDistributedApplicationBuilder
builder
.
IDistributedApplicationBuilder.addProject(name: string, projectPath: string, options?: {
launchProfileOrOptions?: string | ProjectResourceOptions;
}): ProjectResource (+1 overload)

Adds a .NET project resource

addProject
("api", "../Api/Api.csproj");
await
const api: ProjectResource
api
.
ProjectResource.withEnvironment(name: string, value: string | IResourceWithConnectionString | IValueProvider): ProjectResource

Sets an environment variable

withEnvironment
("IDENTITY_CLIENT_ID",
const clientId: BicepOutputReference
clientId
);
await
const builder: IDistributedApplicationBuilder
builder
.
IDistributedApplicationBuilder.build(): DistributedApplication

Builds the distributed application

build
().
DistributedApplication.run(cancellationToken?: cancellationToken): void

Runs the distributed application

run
();

Use GetBicepIdentifier() inside ConfigureInfrastructure or infrastructure resolvers when you need a stable identifier for a provisioned Azure construct. Use output references such as ClientId, NameOutputReference, or getOutput("...") when you need Azure-assigned values like a client ID, endpoint, or resource name.

The ConfigureInfrastructure API lets you customize the Azure resources that Aspire generates during provisioning. In C#, it provides a strongly-typed surface over the Azure.Provisioning library, letting you modify any property of the generated Azure resource types before Bicep is emitted.

For TypeScript and other polyglot AppHosts, add the Aspire.Hosting.Azure.Provisioning.* package for the SDK you want to customize. A hosting package alone doesn’t expose its Azure SDK models.

Add typed Storage infrastructure customization
aspire add Aspire.Hosting.Azure.Provisioning.Storage

Select a provisioning package for the SDK models you want to customize:

The Selected SDK models column lists representative models, not every model exposed by each package. Packages can also expose child resources and supporting models.

Hosting integrationOpt-in provisioning packageSelected SDK models
Azure App ConfigurationAspire.Hosting.Azure.Provisioning.AppConfigurationAppConfigurationStore
Azure Container AppsAspire.Hosting.Azure.Provisioning.AppContainersContainerAppManagedEnvironment, ContainerApp, ContainerAppJob
Azure App ServiceAspire.Hosting.Azure.Provisioning.AppServiceAppServicePlan, WebSite
Azure Front DoorAspire.Hosting.Azure.Provisioning.CdnCdnProfile
Azure Kubernetes ServiceAspire.Hosting.Azure.Provisioning.ContainerServiceContainerServiceManagedCluster
Azure Data ExplorerAspire.Hosting.Azure.Provisioning.KustoKustoCluster
Azure networkingAspire.Hosting.Azure.Provisioning.NetworkVirtualNetwork, NetworkSecurityGroup, NatGateway, PublicIPAddress, PrivateEndpoint, NetworkSecurityPerimeter
Azure private DNSAspire.Hosting.Azure.Provisioning.PrivateDnsPrivateDnsZone
Azure Database for PostgreSQLAspire.Hosting.Azure.Provisioning.PostgreSqlPostgreSqlFlexibleServer
Azure Cache for RedisAspire.Hosting.Azure.Provisioning.RedisRedisResource
Azure Managed RedisAspire.Hosting.Azure.Provisioning.RedisEnterpriseRedisEnterpriseCluster
Azure SignalR ServiceAspire.Hosting.Azure.Provisioning.SignalRSignalRService
Azure StorageAspire.Hosting.Azure.Provisioning.StorageStorageAccount
Azure Service BusAspire.Hosting.Azure.Provisioning.ServiceBusServiceBusNamespace
Azure Key VaultAspire.Hosting.Azure.Provisioning.KeyVaultKeyVaultService

Package names follow the Azure Provisioning SDK, which can differ from the hosting integration name: Front Door uses Cdn, Kubernetes uses ContainerService, and PostgreSQL uses the SDK spelling PostgreSql. Network and PrivateDns are separate opt-ins, as are Redis and RedisEnterprise. To customize supporting resources such as a container registry or Log Analytics workspace, also add the corresponding provisioning package; a transitive hosting dependency doesn’t enable its SDK proxies.

Each provisioning package references its hosting integration and the shared Aspire.Hosting.Azure.Provisioning runtime. Install only the SDK proxies your AppHost uses. Existing C# customization through Azure.Provisioning.* doesn’t require these proxy packages.

Proxy properties use asynchronous get() and set(...) operations. Dictionary and list properties expose collection handles after get(). Properties backed by BicepValue<T> accept compatible primitives or shared Bicep expression values from infrastructure.bicep(), preserving deployment-time references and secure-value metadata.

For the complete factory method reference, C# SDK and Bicep mappings, and Storage and Key Vault examples, see Compose Azure infrastructure with Bicep helpers.

Lookups are scoped to the current infrastructure callback. A no-argument root lookup matches the hosting resource’s Bicep identifier, not its physical Azure name. Use an identifier-based lookup or typed resource list for child resources; a companion resource can have a separate callback.

The selected models in the table aren’t all no-argument lookup roots:

  • App Configuration: Use getAppConfigurationStore() in the store’s infrastructure callback.
  • Container Apps: Use getContainerAppManagedEnvironment() in the environment callback. Apps and jobs require identifier-based lookup in their publish callbacks. Their SDK Bicep identifiers use the normalized workload name, not the synthetic hosting resource identifier.
  • App Service: Use getAppServicePlanByIdentifier(...) with the environment’s Bicep identifier followed by _asplan. Sites use the Bicep identifier webapp and must be accessed in the website publish callback, not the environment callback. Neither plans nor sites have a no-argument root lookup.
  • Child resources: Use identifier-based lookup for resources such as Kusto databases. Don’t assume the parent resource’s no-argument lookup selects a child.

For a complete TypeScript and C# example using Azure Storage, see Basic infrastructure customization. The example looks up the storage account inside its infrastructure callback and adds tags. The callback runs before Aspire emits the resource’s Bicep; the lookup selects a generated resource rather than querying an existing Azure deployment.

IP address collections and projection limits

Section titled “IP address collections and projection limits”

The AppContainers SDK’s OutboundIPAddressList and the AppService SDK’s IPAddresses, ExternalInboundIPAddresses, InternalInboundIPAddresses, LinuxOutboundIPAddresses, and WindowsOutboundIPAddresses use IP address collection proxies.

Writable collections accept IPv4 or IPv6 strings and compatible Bicep value handles for add, insert, and set operations. Invalid address strings fail validation. Element getters return Bicep value handles that preserve literal values, expressions, resource references, and secure-value metadata. An exported collection isn’t necessarily writable: Azure SDK read-only output restrictions still apply.

The projection deliberately excludes members without a type-safe representation:

Provisioning package suffixExcluded memberUnsupported SDK shape
ContainerServiceCustomCATrustCertificatesBicepList<BinaryData>
NetworkAdditionalPropertiesBicepDictionary<BinaryData>
RedisAdditionalPropertiesBicepDictionary<BinaryData>

These exclusions apply to the polyglot proxy surface, not direct Azure Provisioning SDK access in C#. Adding a proxy package doesn’t change authentication, resource lifecycles, or deployment defaults.

Azure hosting resources backed by AzureProvisioningResource expose ConfigureInfrastructure. The callback receives an AzureResourceInfrastructure instance that gives you access to the provisioned constructs for that resource.

With Aspire.Hosting.Azure.Provisioning.Storage installed, use the typed storage-account lookup and its tags collection:

apphost.mts
import { createBuilder } from './.aspire/modules/aspire.mjs';
const builder = await createBuilder();
const storage = await builder.addAzureStorage('storage');
await storage.configureInfrastructure(async (infra) => {
const account = await infra.getStorageAccount();
const tags = await account.tags.get();
await tags.set('Environment', 'Development');
await tags.set('CostCenter', 'Engineering');
});
await builder.build().run();
apphost.mts (Storage)
import {
createBuilder,
StorageKind,
StorageSkuName,
} from './.aspire/modules/aspire.mjs';
const builder = await createBuilder();
const storage = await builder.addAzureStorage('storage');
await storage.configureInfrastructure(async (infra) => {
const account = await infra.getStorageAccount();
const sku = await infra.createStorageSku();
await sku.name.set(StorageSkuName.StandardGrs);
await account.sku.set(sku);
await account.kind.set(StorageKind.StorageV2);
});
await builder.build().run();
apphost.mts (Service Bus)
import { createBuilder, ServiceBusSkuName } from './.aspire/modules/aspire.mjs';
const builder = await createBuilder();
const serviceBus = await builder.addAzureServiceBus('messaging');
await serviceBus.configureInfrastructure(async (infra) => {
const namespace = await infra.getServiceBusNamespace();
const sku = await infra.createServiceBusSku();
await sku.name.set(ServiceBusSkuName.Premium);
await sku.capacity.set(2);
await namespace.sku.set(sku);
});
await builder.build().run();
apphost.mts (Azure Managed Redis)
import {
createBuilder,
RedisEnterpriseSkuName,
} from './.aspire/modules/aspire.mjs';
const builder = await createBuilder();
const redis = await builder.addAzureManagedRedis('redis');
await redis.configureInfrastructure(async (infra) => {
const cluster = await infra.getRedisEnterpriseCluster();
const sku = await infra.createRedisEnterpriseSku();
await sku.name.set(RedisEnterpriseSkuName.BalancedB0);
await cluster.sku.set(sku);
});
await builder.build().run();

This example restricts public access to an IP range while allowing trusted Azure services. Replace the documentation-only range with your application’s approved network range before deploying.

apphost.mts
import {
createBuilder,
StorageAccountNetworkRuleAction,
StorageNetworkBypass,
StorageNetworkDefaultAction,
} from './.aspire/modules/aspire.mjs';
const builder = await createBuilder();
const storage = await builder.addAzureStorage('storage');
await storage.configureInfrastructure(async (infra) => {
const account = await infra.getStorageAccount();
const rules = await infra.createStorageAccountNetworkRuleSet();
await rules.defaultAction.set(StorageNetworkDefaultAction.Deny);
await rules.bypass.set(StorageNetworkBypass.AzureServices);
const rule = await infra.createStorageAccountIPRule();
await rule.iPAddressOrRange.set('203.0.113.0/24');
await rule.action.set(StorageAccountNetworkRuleAction.Allow);
const ipRules = await rules.iPRules.get();
await ipRules.add(rule);
await account.networkRuleSet.set(rules);
});
await builder.build().run();
apphost.mts
import {
createBuilder,
StorageAccountKeySource,
StorageMinimumTlsVersion,
} from './.aspire/modules/aspire.mjs';
const builder = await createBuilder();
const storage = await builder.addAzureStorage('storage');
await storage.configureInfrastructure(async (infra) => {
const account = await infra.getStorageAccount();
await account.enableHttpsTrafficOnly.set(true);
await account.minimumTlsVersion.set(StorageMinimumTlsVersion.Tls1_2);
const encryption = await infra.createStorageAccountEncryption();
await encryption.keySource.set(StorageAccountKeySource.Storage);
const services = await infra.createStorageAccountEncryptionServices();
const blob = await infra.createStorageEncryptionService();
const file = await infra.createStorageEncryptionService();
await blob.isEnabled.set(true);
await file.isEnabled.set(true);
await services.blob.set(blob);
await services.file.set(file);
await encryption.services.set(services);
await account.encryption.set(encryption);
});
await builder.build().run();

When an integration creates multiple resources (for example, a Service Bus namespace and its queues), you can customize each one inside a single ConfigureInfrastructure call:

apphost.mts
import { createBuilder, ServiceBusSkuName } from './.aspire/modules/aspire.mjs';
const builder = await createBuilder();
const serviceBus = await builder.addAzureServiceBus('messaging');
const queue = await serviceBus.addServiceBusQueue('orders');
await serviceBus.configureInfrastructure(async (infra) => {
const namespace = await infra.getServiceBusNamespace();
const sku = await infra.createServiceBusSku();
await sku.name.set(ServiceBusSkuName.Standard);
await namespace.sku.set(sku);
for (const queueResource of await infra.getServiceBusQueues()) {
await queueResource.maxDeliveryCount.set(5);
}
});
await builder.build().run();

The callback above applies the queue setting to every queue modeled under that namespace. To select a particular child, use its generated Bicep identifier with getServiceBusQueueByIdentifier, not a comparison against its deployment-time Azure name.

For common scenarios, prefer Aspire’s higher-level resource builders. For example, to add a private endpoint to a storage account, use AddPrivateEndpoint from the Aspire.Hosting.Azure.Network package, which automatically wires up the Private DNS Zone, VNet link, and DNS Zone Group:

Install Aspire.Hosting.Azure.Network for this example. These networking APIs are experimental under ASPIREAZURE003; the C# example explicitly acknowledges that diagnostic. This hosting API is separate from the opt-in SDK proxies.

apphost.mts
import {
function createBuilder(): IDistributedApplicationBuilder

Creates a new distributed application builder

createBuilder
} from './.aspire/modules/aspire.mjs';
const
const builder: IDistributedApplicationBuilder
builder
= await
function createBuilder(): IDistributedApplicationBuilder

Creates a new distributed application builder

createBuilder
();
const
const vnet: AzureVirtualNetworkResource
vnet
= await
const builder: IDistributedApplicationBuilder
builder
.
IDistributedApplicationBuilder.addAzureVirtualNetwork(name: string, options?: {
addressPrefix?: string | ParameterResource;
}): AzureVirtualNetworkResource (+1 overload)

Adds an Azure Virtual Network resource to the application model.

addAzureVirtualNetwork
('vnet');
const
const peSubnet: AzureSubnetResource
peSubnet
=
const vnet: AzureVirtualNetworkResource
vnet
.
AzureVirtualNetworkResource.addSubnet(name: string, addressPrefix: string | ParameterResource, options?: {
subnetName?: string;
} | undefined): AzureSubnetResource (+1 overload)

Adds an Azure subnet resource to an Azure Virtual Network resource.

addSubnet
('pe-subnet', '10.0.1.0/24');
const
const storage: AzureStorageResource
storage
= await
const builder: IDistributedApplicationBuilder
builder
.
IDistributedApplicationBuilder.addAzureStorage(name: string): AzureStorageResource

Adds an Azure Storage resource to the application model. This resource can be used to create Azure blob, table, and queue resources.

addAzureStorage
('storage');
const
const blobs: AzureBlobStorageResource
blobs
=
const storage: AzureStorageResource
storage
.
AzureStorageResource.addBlobs(name: string): AzureBlobStorageResource

Adds an Azure Blob Storage resource

addBlobs
('blobs');
const peSubnet: AzureSubnetResource
peSubnet
.
AzureSubnetResource.addPrivateEndpoint(target: IAzurePrivateEndpointTarget): AzurePrivateEndpointResource

Adds an Azure Private Endpoint resource to the subnet.

addPrivateEndpoint
(
const blobs: AzureBlobStorageResource
blobs
);
await
const builder: IDistributedApplicationBuilder
builder
.
IDistributedApplicationBuilder.build(): DistributedApplication

Builds the distributed application

build
().
DistributedApplication.run(cancellationToken?: cancellationToken): void

Runs the distributed application

run
();

When no Aspire-native resource builder exists for what you need, fall back to ConfigureInfrastructure to add an Azure.Provisioning construct directly. See the Azure.Provisioning API reference for the available construct types and their properties.

Customize naming and provisioning with an infrastructure resolver

Section titled “Customize naming and provisioning with an infrastructure resolver”

Another way to customize Azure provisioning is to create an InfrastructureResolver. This is a C#-only API that lets you apply organization-wide naming conventions or other centralized provisioning behavior across many Azure resources.

For scenarios requiring full control, you can supply a custom Bicep file and reference it from your AppHost. This approach works in both C# and TypeScript.

Create a Bicep file in your AppHost project:

custom-storage.bicep
@description('Storage account name')
param storageAccountName string
@description('Location')
param location string = resourceGroup().location
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
name: storageAccountName
location: location
sku: {
name: 'Premium_LRS'
}
kind: 'BlockBlobStorage'
properties: {
minimumTlsVersion: 'TLS1_2'
allowBlobPublicAccess: false
networkAcls: {
defaultAction: 'Deny'
bypass: 'AzureServices'
}
}
}
output storageAccountName string = storageAccount.name

Reference the file from the AppHost using AddBicepTemplate (C#) or addBicepTemplate (TypeScript). Use WithParameter / withParameter to supply input parameters, and GetOutput / getOutput to consume a named output by passing the resulting reference to another resource:

apphost.mts
import {
function createBuilder(): IDistributedApplicationBuilder

Creates a new distributed application builder

createBuilder
} from './.aspire/modules/aspire.mjs';
const
const builder: IDistributedApplicationBuilder
builder
= await
function createBuilder(): IDistributedApplicationBuilder

Creates a new distributed application builder

createBuilder
();
const
const storage: AzureBicepResource
storage
= await
const builder: IDistributedApplicationBuilder
builder
.
IDistributedApplicationBuilder.addBicepTemplate(name: string, bicepFile: string): AzureBicepResource

Adds an Azure Bicep resource to the application model.

addBicepTemplate
(
'storage',
'./custom-storage.bicep'
);
await
const storage: AzureBicepResource
storage
.
AzureBicepResource.withParameter(name: string, options?: {
value?: string | string[] | ParameterResource | IResourceWithConnectionString | BicepOutputReference | ReferenceExpression | EndpointReference;
} | undefined): AzureBicepResource (+1 overload)

Adds a Bicep parameter

withParameter
('storageAccountName', {
value?: string | string[] | ParameterResource | IResourceWithConnectionString | BicepOutputReference | ReferenceExpression | EndpointReference | undefined
value
: 'mystorageaccount',
});
const
const storageAccountName: BicepOutputReference
storageAccountName
= await
const storage: AzureBicepResource
storage
.
AzureBicepResource.getOutput(name: string): BicepOutputReference

Gets a reference to an output from a bicep template.

getOutput
('storageAccountName');
const
const webapp: ProjectResource
webapp
= await
const builder: IDistributedApplicationBuilder
builder
.
IDistributedApplicationBuilder.addProject(name: string, projectPath: string, options?: {
launchProfileOrOptions?: string | ProjectResourceOptions;
}): ProjectResource (+1 overload)

Adds a .NET project resource

addProject
('webapp', '../WebApp/WebApp.csproj');
await
const webapp: ProjectResource
webapp
.
ProjectResource.withEnvironment(name: string, value: string | IResourceWithConnectionString | IValueProvider): ProjectResource

Sets an environment variable

withEnvironment
('STORAGE_ACCOUNT_NAME',
const storageAccountName: BicepOutputReference
storageAccountName
);
await
const builder: IDistributedApplicationBuilder
builder
.
IDistributedApplicationBuilder.build(): DistributedApplication

Builds the distributed application

build
().
DistributedApplication.run(cancellationToken?: cancellationToken): void

Runs the distributed application

run
();

WithParameter accepts several kinds of values beyond plain strings, so a custom Bicep template can take values that are computed at deployment time. Common sources include:

  • A ParameterResource declared with AddParameter (including secret parameters).
  • A BicepOutputReference from another Bicep resource — useful when one template’s output feeds another template’s input.
  • A ReferenceExpression that composes values from multiple resources.
  • A connection-string-bearing resource via IResourceBuilder<IResourceWithConnectionString>.
  • An EndpointReference from a resource’s endpoint.

The example below adds a secret administrator password as a parameter and then reads the deployed SQL server name back out of the template so that another resource can consume it:

Create the following Bicep file in your AppHost directory:

custom-sql.bicep
@secure()
param administratorLoginPassword string
param location string = resourceGroup().location
resource sqlServer 'Microsoft.Sql/servers@2021-11-01' = {
name: 'sql-${uniqueString(resourceGroup().id)}'
location: location
properties: {
administratorLogin: 'sqladmin'
administratorLoginPassword: administratorLoginPassword
}
}
output sqlServerName string = sqlServer.name
apphost.mts
import {
function createBuilder(): IDistributedApplicationBuilder

Creates a new distributed application builder

createBuilder
} from './.aspire/modules/aspire.mjs';
const
const builder: IDistributedApplicationBuilder
builder
= await
function createBuilder(): IDistributedApplicationBuilder

Creates a new distributed application builder

createBuilder
();
const
const adminPassword: ParameterResource
adminPassword
= await
const builder: IDistributedApplicationBuilder
builder
.
IDistributedApplicationBuilder.addParameter(name: string, options?: {
value?: string;
publishValueAsDefault?: boolean;
secret?: boolean;
}): ParameterResource (+1 overload)

Adds a parameter resource

addParameter
('adminPassword', {
secret?: boolean | undefined
secret
: true,
});
const
const sql: AzureBicepResource
sql
= await
const builder: IDistributedApplicationBuilder
builder
.
IDistributedApplicationBuilder.addBicepTemplate(name: string, bicepFile: string): AzureBicepResource

Adds an Azure Bicep resource to the application model.

addBicepTemplate
('sql', './custom-sql.bicep');
await
const sql: AzureBicepResource
sql
.
AzureBicepResource.withParameter(name: string, options?: {
value?: string | string[] | ParameterResource | IResourceWithConnectionString | BicepOutputReference | ReferenceExpression | EndpointReference;
} | undefined): AzureBicepResource (+1 overload)

Adds a Bicep parameter

withParameter
('administratorLoginPassword', {
value?: string | ParameterResource | string[] | IResourceWithConnectionString | BicepOutputReference | ReferenceExpression | EndpointReference | undefined
value
:
const adminPassword: ParameterResource
adminPassword
});
const
const sqlServerName: BicepOutputReference
sqlServerName
= await
const sql: AzureBicepResource
sql
.
AzureBicepResource.getOutput(name: string): BicepOutputReference

Gets a reference to an output from a bicep template.

getOutput
('sqlServerName');
const
const api: ProjectResource
api
= await
const builder: IDistributedApplicationBuilder
builder
.
IDistributedApplicationBuilder.addProject(name: string, projectPath: string, options?: {
launchProfileOrOptions?: string | ProjectResourceOptions;
}): ProjectResource (+1 overload)

Adds a .NET project resource

addProject
('api', '../Api/Api.csproj');
await
const api: ProjectResource
api
.
ProjectResource.withEnvironment(name: string, value: string | IResourceWithConnectionString | IValueProvider): ProjectResource

Sets an environment variable

withEnvironment
('SQL_SERVER_NAME',
const sqlServerName: BicepOutputReference
sqlServerName
);
await
const builder: IDistributedApplicationBuilder
builder
.
IDistributedApplicationBuilder.build(): DistributedApplication

Builds the distributed application

build
().
DistributedApplication.run(cancellationToken?: cancellationToken): void

Runs the distributed application

run
();

For more end-to-end examples — including chaining outputs between Bicep resources and using the existing Bicep keyword to reference resources that Aspire didn’t provision — see the Aspire playground/bicep sample.

By default, Aspire deploys Bicep resources at the resource group scope. Some Bicep templates require a different deployment scope, such as the subscription or the tenant.

To see the Bicep that Aspire emits after applying your ConfigureInfrastructure callbacks, publish the AppHost and read the files from the output folder:

  1. From the AppHost directory, run aspire publish.
  2. Open the aspire-output folder in the AppHost directory. To write somewhere else, pass --output-path to the publish command.
  3. Review the generated .bicep files to verify your customizations.