इसे छोड़कर कंटेंट पर जाएं
Docs Try Aspire
Docs Try

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.

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

AppHost.cs
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();

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.cs
var builder = DistributedApplication.CreateBuilder(args);
builder.AddAzureServiceBus("messaging")
.ClearDefaultRoleAssignments();
builder.Build().Run();

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

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.

AppHost.cs
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();

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 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.

All Azure resources in Aspire inherit from AzureProvisioningResource, which exposes the ConfigureInfrastructure method:

C# — AppHost.cs
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";
});
storage.ConfigureInfrastructure(infra =>
{
var account = infra.GetProvisionableResources()
.OfType<StorageAccount>()
.Single();
account.Sku = new StorageSku
{
Name = StorageSkuName.StandardGRS
};
account.Kind = StorageKind.StorageV2;
});
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"
});
});
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 }
}
};
});

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);
}
});

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.

C# — AppHost.cs
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:

C# — AppHost.cs
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.

For more complex scenarios or when you need full control, you can provide custom Bicep templates.

Create a custom 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 connectionString string = 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};...'

Reference it in your AppHost:

C# — AppHost.cs
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);

You can combine ConfigureInfrastructure with custom Bicep for maximum flexibility:

// Start with Aspire's default
var storage = builder.AddAzureStorage("storage");
// Customize with ConfigureInfrastructure
storage.ConfigureInfrastructure(infra =>
{
var account = infra.GetProvisionableResources()
.OfType<StorageAccount>()
.Single();
account.Sku = new StorageSku { Name = StorageSkuName.PremiumLRS };
});
// Add custom resources via Bicep
var customResource = builder.AddAzureBicepResource(
name: "custom",
bicepFilePath: "./custom-resource.bicep");

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 maintainable
storage.ConfigureInfrastructure(infra =>
{
var account = infra.GetProvisionableResources()
.OfType<StorageAccount>()
.Single();
account.Sku = new StorageSku { Name = StorageSkuName.PremiumLRS };
});

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 place
var 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 };
});

After customization, you can inspect the generated Bicep:

  1. Run your AppHost locally
  2. Check the ./infra directory in your AppHost project
  3. Review the generated .bicep files

The generated files reflect all customizations made via ConfigureInfrastructure.

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}");
}
});

If a property isn’t available in the Azure.Provisioning types, you may need to:

  1. Update to the latest Aspire version
  2. Use custom Bicep for that specific property
  3. File an issue on the Aspire GitHub repository

Azure will validate your configuration during deployment. Common issues:

  • Invalid SKU names or combinations
  • Location mismatches
  • Naming convention violations
  • Missing required properties