# Customize Azure resources

<Image
  src={azureIcon}
  alt="Azure logo"
  height={80}
  width={80}
  class:list={'float-inline-left icon'}
  data-zoom-off
/>

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. This page documents all customization APIs supported across both C# and TypeScript AppHost projects.

<LearnMore>
For target-specific generated resource customization, see [Deploy to Azure Container Apps](/deployment/azure/container-apps/) and [Deploy to Azure App Service](/deployment/azure/app-service/).
</LearnMore>

## Shared Azure customization patterns

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

```csharp title="C# — 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();
```
```typescript title="TypeScript — apphost.mts" twoslash
import { createBuilder } from './.aspire/modules/aspire.mjs';

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.
**Note:** Cross-subscription resource references aren't supported. The existing resource
  must be in the same Azure subscription as your deployment target.

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

```csharp title="C# — AppHost.cs"
var builder = DistributedApplication.CreateBuilder(args);

builder.AddAzureServiceBus("messaging")
    .ClearDefaultRoleAssignments();

builder.Build().Run();
```
```typescript title="TypeScript — apphost.mts" twoslash
import { createBuilder } from './.aspire/modules/aspire.mjs';

const builder = await createBuilder();

const serviceBus = await builder.addAzureServiceBus("messaging");
await serviceBus.clearDefaultRoleAssignments();

await builder.build().run();
```
<LearnMore>
For built-in and custom RBAC guidance, see [Manage Azure role assignments](/integrations/cloud/azure/role-assignments/).
</LearnMore>

### Read generated output values

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.

```csharp title="C# — 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();
```
```typescript title="TypeScript — apphost.mts" twoslash
import { createBuilder } from './.aspire/modules/aspire.mjs';

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

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](https://www.nuget.org/packages/Azure.Provisioning) library, letting you modify any property of the generated Azure resource types before Bicep is emitted.
**Azure.Provisioning resources:** For detailed API documentation and examples, see the [Azure.Provisioning API reference](https://learn.microsoft.com/dotnet/api/overview/azure/provisioning) and the [Azure.Provisioning README](https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/provisioning/Azure.Provisioning/README.md).

### Basic infrastructure customization

All Azure hosting resources in Aspire inherit from `AzureProvisioningResource`, which exposes `ConfigureInfrastructure`. The callback receives an `AzureResourceInfrastructure` instance that gives you access to all provisioned constructs for that resource.

In C#, use `GetProvisionableResources()` with the strongly-typed Azure SDK types (for example `StorageAccount`, `ServiceBusNamespace`) to navigate and mutate each construct:

```csharp title="C# — AppHost.cs"
var builder = DistributedApplication.CreateBuilder(args);

var storage = builder.AddAzureStorage("storage");

storage.ConfigureInfrastructure(infra =>
{
    var storageAccount = infra.GetProvisionableResources()
                               .OfType<StorageAccount>()
                               .Single();

    storageAccount.Sku = new StorageSku { Name = StorageSkuName.PremiumLRS };

    storageAccount.Tags["Environment"] = "Development";
    storageAccount.Tags["CostCenter"] = "Engineering";
});

builder.Build().Run();
```
**Note:** The TypeScript AppHost exposes `ConfigureInfrastructure` through the generic
  `AzureResourceInfrastructure` type. Strongly-typed Azure SDK resource classes
  (such as `StorageAccount` or `ServiceBusNamespace`) are not available in
  TypeScript — use `getProvisionableResources()` and work with the returned
  objects dynamically, or prefer the C# AppHost for deep infrastructure
  customization.

```typescript title="TypeScript — apphost.mts" twoslash
import { createBuilder } from './.aspire/modules/aspire.mjs';

const builder = await createBuilder();

const storage = await builder.addAzureStorage("storage");

await storage.configureInfrastructure(async (infra) => {
    // Strongly-typed Azure SDK types are not available in TypeScript.
    // Use getProvisionableResources() to access constructs dynamically.
    const resources = await infra.getProvisionableResources();
    // Work with resources generically as needed.
});

await builder.build().run();
```

### Configure SKUs and tiers

```csharp title="C# — AppHost.cs (Storage)"
storage.ConfigureInfrastructure(infra =>
{
    var account = infra.GetProvisionableResources()
                       .OfType<StorageAccount>()
                       .Single();

    account.Sku = new StorageSku { Name = StorageSkuName.StandardGRS };
    account.Kind = StorageKind.StorageV2;
});
```

```csharp title="C# — AppHost.cs (Service Bus)"
serviceBus.ConfigureInfrastructure(infra =>
{
    var ns = infra.GetProvisionableResources()
                  .OfType<ServiceBusNamespace>()
                  .Single();

    ns.Sku = new ServiceBusSku
    {
        Name = ServiceBusSkuName.Premium,
        Capacity = 2
    };
});
```

```csharp title="C# — AppHost.cs (Redis)"
redis.ConfigureInfrastructure(infra =>
{
    var cache = infra.GetProvisionableResources()
                     .OfType<RedisCache>()
                     .Single();

    cache.Sku = new RedisCacheSku
    {
        Name = RedisCacheSkuName.Premium,
        Family = RedisCacheSkuFamily.P,
        Capacity = 1
    };
});
```
**Note:** Strongly-typed SKU and tier classes (such as `StorageSku`, `ServiceBusSku`,
  `RedisCacheSku`) are part of the Azure SDK for .NET and are not available in
  TypeScript. Use the C# AppHost to perform strongly-typed SKU configuration.

### Configure networking

```csharp title="C# — AppHost.cs"
storage.ConfigureInfrastructure(infra =>
{
    var account = infra.GetProvisionableResources()
                       .OfType<StorageAccount>()
                       .Single();

    account.NetworkRuleSet = new StorageAccountNetworkRuleSet
    {
        DefaultAction = StorageNetworkDefaultAction.Deny,
        Bypass = "AzureServices"
    };

    account.NetworkRuleSet.IpRules.Add(new StorageAccountIPRule
    {
        IPAddressOrRange = "203.0.113.0/24",
        Action = "Allow"
    });
});
```
**Note:** Strongly-typed networking types (such as `StorageAccountNetworkRuleSet`) are
  part of the Azure SDK for .NET and are not available in TypeScript. Use the C#
  AppHost for networking customization via `ConfigureInfrastructure`.

### Configure security and compliance

```csharp title="C# — AppHost.cs"
storage.ConfigureInfrastructure(infra =>
{
    var account = infra.GetProvisionableResources()
                       .OfType<StorageAccount>()
                       .Single();

    account.EnableHttpsTrafficOnly = true;
    account.MinimumTlsVersion = StorageMinimumTlsVersion.Tls1_2;

    account.Encryption = new StorageAccountEncryption
    {
        KeySource = StorageAccountKeySource.MicrosoftStorage,
        Services = new StorageAccountEncryptionServices
        {
            Blob = new StorageEncryptionService { Enabled = true },
            File = new StorageEncryptionService { Enabled = true }
        }
    };
});
```
**Note:** Strongly-typed security types (such as `StorageAccountEncryption`,
  `StorageMinimumTlsVersion`) are part of the Azure SDK for .NET and are not
  available in TypeScript. Use the C# AppHost for security and compliance
  customization via `ConfigureInfrastructure`.

### Work with multiple resources

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:

```csharp title="C# — AppHost.cs"
var servicebus = builder.AddAzureServiceBus("messaging");
var queue = servicebus.AddQueue("orders");

servicebus.ConfigureInfrastructure(infra =>
{
    var ns = infra.GetProvisionableResources()
                  .OfType<ServiceBusNamespace>()
                  .Single();
    ns.Sku = new ServiceBusSku { Name = ServiceBusSkuName.Standard };

    var queueResource = infra.GetProvisionableResources()
                             .OfType<ServiceBusQueue>()
                             .FirstOrDefault(q => q.Name.Contains("orders"));

    if (queueResource != null)
    {
        queueResource.MaxDeliveryCount = 5;
        queueResource.DefaultMessageTimeToLive = TimeSpan.FromHours(24);
    }
});
```
**Note:** Strongly-typed construct types (such as `ServiceBusNamespace`,
  `ServiceBusQueue`) are not available in TypeScript. Use `getProvisionableResources()`
  to iterate constructs dynamically, or use the C# AppHost for multi-resource
  customization.

### Add Azure resources to the infrastructure

You can inject additional Azure constructs (for example, a private endpoint) into an integration's generated infrastructure:

```csharp title="C# — AppHost.cs"
storage.ConfigureInfrastructure(infra =>
{
    var privateEndpoint = new PrivateEndpoint("storagepe")
    {
        Location = "eastus",
        Subnet = new SubnetReference
        {
            Id = "/subscriptions/.../subnets/mysubnet"
        }
    };

    infra.Add(privateEndpoint);
});
```
**Note:** Adding strongly-typed Azure SDK constructs such as `PrivateEndpoint` is not
  supported in the TypeScript AppHost. Use the C# AppHost when you need to inject
  additional Azure resources into the generated infrastructure.

### Customize naming and provisioning with an infrastructure resolver

Another way to customize Azure provisioning is to create an [`InfrastructureResolver`](https://learn.microsoft.com/dotnet/api/azure.provisioning.primitives.infrastructureresolver). This is a C#-only API that lets you apply organization-wide naming conventions or other centralized provisioning behavior across many Azure resources.

Define a resolver by inheriting from `InfrastructureResolver` and overriding the relevant virtual members:

```csharp title="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);
    }
}
```

Register the resolver in the AppHost:

```csharp title="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();
```
**Note:** `InfrastructureResolver` is a C#-only API from the Azure SDK for .NET and is
  not available in the TypeScript AppHost. Use the C# AppHost when you need
  custom infrastructure resolvers.

## Custom Bicep templates

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.

### Use custom Bicep files

Create a Bicep file in your AppHost project:

```bicep title="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 from the AppHost:

```csharp title="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);

builder.Build().Run();
```

```typescript title="TypeScript — apphost.mts" twoslash
import { createBuilder } from './.aspire/modules/aspire.mjs';

const builder = await createBuilder();

const storage = await builder.addBicepTemplate(
    "storage",
    "./custom-storage.bicep");

const webapp = await builder.addProject("webapp", "../WebApp/WebApp.csproj");
await webapp.withReference(storage);

await builder.build().run();
```

### Inspect generated Bicep

After customizing with `ConfigureInfrastructure`, inspect the Bicep that Aspire generates:

1. Run your AppHost locally.
2. Open the `./infra` directory inside your AppHost project.
3. Review the generated `.bicep` files to verify your customizations.
**Tip:** Use the Azure Portal's **Export template** feature to view Bicep for manually configured resources, then adapt it for Aspire.

## See also

- [Azure integrations overview](/integrations/cloud/azure/overview/)
- [Manage Azure role assignments](/integrations/cloud/azure/role-assignments/)
- [Local Azure provisioning](/integrations/cloud/azure/local-provisioning/)
- [Azure.Provisioning API reference](https://learn.microsoft.com/dotnet/api/overview/azure/provisioning)
- [Deploy to Azure Container Apps](/deployment/azure/container-apps/)
- [Deploy to Azure App Service](/deployment/azure/app-service/)