Customize Azure resources
यह कंटेंट अभी तक आपकी भाषा में उपलब्ध नहीं है।
When working with Azure integrations in Aspire, you often need to customize the provisioned infrastructure beyond the default settings. This page lives with the Azure integrations because 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.
For target-specific generated resource customization, see Deploy to Azure Container Apps and Deploy to Azure App Service.
Shared Azure customization patterns
Section titled “Shared Azure customization patterns”Use existing Azure resources
Section titled “Use existing Azure resources”Use AsExisting, RunAsExisting, and PublishAsExisting when you want Aspire to reference an Azure resource that already exists instead of provisioning a new one.
var builder = DistributedApplication.CreateBuilder(args);
var existingBusName = builder.AddParameter("existingServiceBusName");var existingBusResourceGroup = builder.AddParameter("existingServiceBusResourceGroup");
var serviceBus = builder.AddAzureServiceBus("messaging") .PublishAsExisting(existingBusName, existingBusResourceGroup);
builder.Build().Run();import { createBuilder } from './.modules/aspire.js';
const builder = await createBuilder();
const existingBusName = await builder.addParameter("existingServiceBusName");const existingBusResourceGroup = await builder.addParameter("existingServiceBusResourceGroup");
const serviceBus = await builder.addAzureServiceBus("messaging");await serviceBus.publishAsExisting(existingBusName, existingBusResourceGroup);
await builder.build().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.
Manage default role assignments
Section titled “Manage default role assignments”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.
var builder = DistributedApplication.CreateBuilder(args);
builder.AddAzureServiceBus("messaging") .ClearDefaultRoleAssignments();
builder.Build().Run();import { createBuilder } from './.modules/aspire.js';
const builder = await createBuilder();
const serviceBus = await builder.addAzureServiceBus("messaging");await serviceBus.clearDefaultRoleAssignments();
await builder.build().run();For built-in and custom RBAC guidance, see Manage Azure role assignments.
Read generated output values
Section titled “Read generated output values”Azure resources expose Bicep identifiers and 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.
var builder = DistributedApplication.CreateBuilder(args);
var identity = builder.AddAzureUserAssignedIdentity("identity");
builder.AddProject<Projects.Api>("api") .WithEnvironment("IDENTITY_CLIENT_ID", identity.Resource.ClientId);
builder.Build().Run();import { createBuilder } from './.modules/aspire.js';
const builder = await createBuilder();
const identity = await builder.addAzureUserAssignedIdentity("identity");const clientId = await identity.getOutput("clientId");
const api = await builder.addProject("api", "../Api/Api.csproj");await api.withEnvironment("IDENTITY_CLIENT_ID", clientId);
await builder.build().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.
Azure.Provisioning customization
Section titled “Azure.Provisioning customization”The ConfigureInfrastructure API provides a strongly-typed, C#-based way to customize Azure resources. This is the recommended approach for most customizations. It uses the Azure.Provisioning library to generate Bicep from C# code.
Basic infrastructure customization
Section titled “Basic infrastructure customization”All Azure resources in Aspire inherit from AzureProvisioningResource, which exposes the ConfigureInfrastructure method:
var builder = DistributedApplication.CreateBuilder(args);
var storage = builder.AddAzureStorage("storage");
storage.ConfigureInfrastructure(infra =>{ // Get the storage account resource var storageAccount = infra.GetProvisionableResources() .OfType<StorageAccount>() .Single();
// Customize the SKU storageAccount.Sku = new StorageSku { Name = StorageSkuName.PremiumLRS };
// Add tags storageAccount.Tags["Environment"] = "Development"; storageAccount.Tags["CostCenter"] = "Engineering";});Common customization scenarios
Section titled “Common customization scenarios”Configure SKUs and tiers
Section titled “Configure SKUs and tiers”storage.ConfigureInfrastructure(infra =>{ var account = infra.GetProvisionableResources() .OfType<StorageAccount>() .Single();
account.Sku = new StorageSku { Name = StorageSkuName.StandardGRS }; account.Kind = StorageKind.StorageV2;});serviceBus.ConfigureInfrastructure(infra =>{ var ns = infra.GetProvisionableResources() .OfType<ServiceBusNamespace>() .Single();
ns.Sku = new ServiceBusSku { Name = ServiceBusSkuName.Premium, Capacity = 2 };});redis.ConfigureInfrastructure(infra =>{ var cache = infra.GetProvisionableResources() .OfType<RedisCache>() .Single();
cache.Sku = new RedisCacheSku { Name = RedisCacheSkuName.Premium, Family = RedisCacheSkuFamily.P, Capacity = 1 };});Configure networking
Section titled “Configure networking”storage.ConfigureInfrastructure(infra =>{ var account = infra.GetProvisionableResources() .OfType<StorageAccount>() .Single();
// Enable firewall account.NetworkRuleSet = new StorageAccountNetworkRuleSet { DefaultAction = StorageNetworkDefaultAction.Deny, Bypass = "AzureServices" };
// Allow specific IP account.NetworkRuleSet.IpRules.Add(new StorageAccountIPRule { IPAddressOrRange = "203.0.113.0/24", Action = "Allow" });});Configure security and compliance
Section titled “Configure security and compliance”storage.ConfigureInfrastructure(infra =>{ var account = infra.GetProvisionableResources() .OfType<StorageAccount>() .Single();
// Require HTTPS account.EnableHttpsTrafficOnly = true;
// Configure TLS version account.MinimumTlsVersion = StorageMinimumTlsVersion.Tls1_2;
// Enable blob encryption account.Encryption = new StorageAccountEncryption { KeySource = StorageAccountKeySource.MicrosoftStorage, Services = new StorageAccountEncryptionServices { Blob = new StorageEncryptionService { Enabled = true }, File = new StorageEncryptionService { Enabled = true } } };});Working with multiple resources
Section titled “Working with multiple resources”When an integration creates multiple resources, you can customize each one:
var servicebus = builder.AddAzureServiceBus("messaging");var queue = servicebus.AddQueue("orders");
servicebus.ConfigureInfrastructure(infra =>{ // Customize the namespace var ns = infra.GetProvisionableResources() .OfType<ServiceBusNamespace>() .Single(); ns.Sku = new ServiceBusSku { Name = ServiceBusSkuName.Standard };
// Customize the queue var queueResource = infra.GetProvisionableResources() .OfType<ServiceBusQueue>() .FirstOrDefault(q => q.Name.Contains("orders"));
if (queueResource != null) { queueResource.MaxDeliveryCount = 5; queueResource.DefaultMessageTimeToLive = TimeSpan.FromHours(24); }});Adding Azure resources
Section titled “Adding Azure resources”You can add additional Azure resources to the infrastructure:
storage.ConfigureInfrastructure(infra =>{ // Add a private endpoint var privateEndpoint = new PrivateEndpoint("storagepe") { Location = "eastus", Subnet = new SubnetReference { Id = "/subscriptions/.../subnets/mysubnet" } };
infra.Add(privateEndpoint);});Customize naming and provisioning behavior with an infrastructure resolver
Section titled “Customize naming and provisioning behavior with an infrastructure resolver”Another method you can use to customize Azure provisioning is to create an InfrastructureResolver and write code in it to implement your requirements. Then add that class to the configuration options for your AppHost.
A custom infrastructure resolver is a class that inherits from InfrastructureResolver and can override one or more of its virtual members to apply the desired customizations. In the following example, the ResolveProperties method is overridden to set the name of a Cosmos DB resource, but other members can also be overridden depending on your needs.
using Azure.Provisioning;using Azure.Provisioning.CosmosDB;using Azure.Provisioning.Primitives;
internal sealed class FixedNameInfrastructureResolver : InfrastructureResolver{ public override void ResolveProperties(ProvisionableConstruct construct, ProvisioningBuildOptions options) { if (construct is CosmosDBAccount account) { account.Name = "ContosoCosmosDb"; }
base.ResolveProperties(construct, options); }}Having created that class, add it to the configuration options using code like this in the AppHost:
using Aspire.Hosting.Azure;using Microsoft.Extensions.DependencyInjection;
var builder = DistributedApplication.CreateBuilder(args);
builder.Services.Configure<AzureProvisioningOptions>(options =>{ options.ProvisioningBuildOptions.InfrastructureResolvers.Add(new FixedNameInfrastructureResolver());});
builder.Build().Run();This pattern is useful when you need organization-wide naming conventions or other centralized provisioning behavior that should apply across many Azure resources.
Custom Bicep templates
Section titled “Custom Bicep templates”For more complex scenarios or when you need full control, you can provide custom Bicep templates.
Using custom Bicep files
Section titled “Using custom Bicep files”Create a custom Bicep file in your AppHost project:
@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 connectionString string = 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};...'Reference it in your AppHost:
var builder = DistributedApplication.CreateBuilder(args);
var storage = builder.AddAzureBicepResource( name: "storage", bicepFilePath: "./custom-storage.bicep") .WithParameter("storageAccountName", "mystorageaccount");
builder.AddProject<Projects.WebApp>("webapp") .WithReference(storage);Combining approaches
Section titled “Combining approaches”You can combine ConfigureInfrastructure with custom Bicep for maximum flexibility:
// Start with Aspire's defaultvar storage = builder.AddAzureStorage("storage");
// Customize with ConfigureInfrastructurestorage.ConfigureInfrastructure(infra =>{ var account = infra.GetProvisionableResources() .OfType<StorageAccount>() .Single(); account.Sku = new StorageSku { Name = StorageSkuName.PremiumLRS };});
// Add custom resources via Bicepvar customResource = builder.AddAzureBicepResource( name: "custom", bicepFilePath: "./custom-resource.bicep");Best practices
Section titled “Best practices”Use ConfigureInfrastructure for standard customizations
Section titled “Use ConfigureInfrastructure for standard customizations”For common property changes like SKUs, tags, and basic configuration, prefer ConfigureInfrastructure:
// ✅ Good - Simple and maintainablestorage.ConfigureInfrastructure(infra =>{ var account = infra.GetProvisionableResources() .OfType<StorageAccount>() .Single(); account.Sku = new StorageSku { Name = StorageSkuName.PremiumLRS };});Use custom Bicep for complex scenarios
Section titled “Use custom Bicep for complex scenarios”Use custom Bicep when you need:
- Complex networking configurations
- Multiple interconnected resources
- Custom resource types not in Azure.Provisioning
- Advanced policy configurations
Keep infrastructure code close to application code
Section titled “Keep infrastructure code close to application code”Define infrastructure customizations in your AppHost alongside resource declarations:
// All storage configuration in one placevar storage = builder.AddAzureStorage("storage") .ConfigureInfrastructure(infra => { // Customizations here });Use configuration for environment-specific values
Section titled “Use configuration for environment-specific values”Don’t hard-code environment-specific values:
var skuName = builder.Configuration["Azure:Storage:Sku"] ?? "Standard_LRS";
storage.ConfigureInfrastructure(infra =>{ var account = infra.GetProvisionableResources() .OfType<StorageAccount>() .Single(); account.Sku = new StorageSku { Name = skuName };});Generated Bicep inspection
Section titled “Generated Bicep inspection”After customization, you can inspect the generated Bicep:
- Run your AppHost locally
- Check the
./infradirectory in your AppHost project - Review the generated
.bicepfiles
The generated files reflect all customizations made via ConfigureInfrastructure.
Troubleshooting
Section titled “Troubleshooting”Finding resource types
Section titled “Finding resource types”If you’re unsure what resource type to use:
storage.ConfigureInfrastructure(infra =>{ // List all resources var resources = infra.GetProvisionableResources(); foreach (var resource in resources) { Console.WriteLine($"Type: {resource.GetType().Name}"); }});Property not available
Section titled “Property not available”If a property isn’t available in the Azure.Provisioning types, you may need to:
- Update to the latest Aspire version
- Use custom Bicep for that specific property
- File an issue on the Aspire GitHub repository
Validation errors
Section titled “Validation errors”Azure will validate your configuration during deployment. Common issues:
- Invalid SKU names or combinations
- Location mismatches
- Naming convention violations
- Missing required properties