Azure Virtual Network
Azure Virtual Network enables Azure resources to securely communicate with each other, the internet, and on-premises networks. The Aspire Azure Virtual Network integration provides APIs to configure virtual networks, subnets, NAT gateways, network security groups (NSGs), and private endpoints directly in your AppHost.
Hosting integration
The Azure Virtual Network hosting integration models network resources as the following types:
AzureVirtualNetworkResource: Represents an Azure Virtual Network.AzureSubnetResource: Represents a subnet within a virtual network.AzureNetworkSecurityGroupResource: Represents a network security group for traffic control.AzureNatGatewayResource: Represents a NAT gateway for deterministic outbound IP addresses.AzurePublicIPAddressResource: Represents a public IP address.
To access these types and APIs, add the 📦 Aspire.Hosting.Azure.Network NuGet package in the AppHost project:
dotnet add package Aspire.Hosting.Azure.NetworkTip
When you call any of the Azure Virtual Network APIs, it implicitly calls
AddAzureProvisioning—which adds support for generating Azure resources
dynamically during app startup. The app must configure the appropriate
subscription and location. For more information, see Local provisioning:
Configuration.
Note
The Azure Virtual Network integration is a hosting and publish-only integration. It provisions Azure networking infrastructure (virtual networks, subnets, NSGs, etc.) and has no corresponding client integration. During local development, these resources are not provisioned in your Azure subscription.
Add a virtual network
To add a virtual network, call AddAzureVirtualNetwork on the builder instance:
var builder = DistributedApplication.CreateBuilder(args);
var vnet = builder.AddAzureVirtualNetwork("vnet");
// After adding all resources, run the app...
builder.Build().Run();
By default, the virtual network uses the address prefix 10.0.0.0/16. You can specify a custom address prefix:
var vnet = builder.AddAzureVirtualNetwork("vnet", "10.1.0.0/16");
Add subnets
Call AddSubnet on the virtual network builder to add a subnet with a name and address prefix:
var vnet = builder.AddAzureVirtualNetwork("vnet");
var subnet = vnet.AddSubnet("my-subnet", "10.0.1.0/24");
You can add multiple subnets with different address ranges to a single virtual network. Subnet address prefixes must fall within the virtual network's address space (for example, 10.0.1.0/24 is valid within a 10.0.0.0/16 network).
Add NAT gateways
A NAT gateway provides deterministic outbound public IP addresses for subnet resources. Call AddNatGateway to create a NAT gateway, then associate it with a subnet using WithNatGateway:
var natGateway = builder.AddNatGateway("nat");
var vnet = builder.AddAzureVirtualNetwork("vnet");
var subnet = vnet.AddSubnet("aca-subnet", "10.0.0.0/23")
.WithNatGateway(natGateway);
By default, a public IP address is automatically created for the NAT gateway. To provide an explicit public IP address for full control, call AddPublicIPAddress and associate it with WithPublicIPAddress:
var pip = builder.AddPublicIPAddress("nat-pip");
var natGateway = builder.AddNatGateway("nat")
.WithPublicIPAddress(pip);
For advanced settings like idle timeout or availability zones, use ConfigureInfrastructure.
Add network security groups
Network security groups (NSGs) control inbound and outbound traffic flow on subnets. The integration provides two APIs for configuring NSGs: a shorthand API for common scenarios and an explicit API for full control.
Shorthand API
The shorthand API uses fluent methods on subnet builders that automatically create an NSG, auto-increment priority, and auto-generate rule names:
var vnet = builder.AddAzureVirtualNetwork("vnet");
var subnet = vnet.AddSubnet("web", "10.0.1.0/24")
.AllowInbound(
port: "443",
from: AzureServiceTags.AzureLoadBalancer,
protocol: SecurityRuleProtocol.Tcp)
.DenyInbound(from: AzureServiceTags.Internet);
The available shorthand methods are:
AllowInbound— Allow inbound traffic.DenyInbound— Deny inbound traffic.AllowOutbound— Allow outbound traffic.DenyOutbound— Deny outbound traffic.
Priority auto-increments by 100 (100, 200, 300...) and rule names are auto-generated from the access, direction, port, and source (for example, allow-inbound-443-AzureLoadBalancer).
Note
The SecurityRuleProtocol, SecurityRuleDirection, and
SecurityRuleAccess types require a using directive for the
Azure.Provisioning.Network namespace. The AzureServiceTags class is in
the Aspire.Hosting.Azure namespace.
Explicit API
For full control over security rules, create a standalone NSG with AddNetworkSecurityGroup on the builder and attach it to a subnet with WithNetworkSecurityGroup:
var builder = DistributedApplication.CreateBuilder(args);
var vnet = builder.AddAzureVirtualNetwork("vnet");
var nsg = builder.AddNetworkSecurityGroup("web-nsg")
.WithSecurityRule(new AzureSecurityRule
{
Name = "allow-https",
Priority = 100,
Direction = SecurityRuleDirection.Inbound,
Access = SecurityRuleAccess.Allow,
Protocol = SecurityRuleProtocol.Tcp,
DestinationPortRange = "443"
});
var subnet = vnet.AddSubnet("web-subnet", "10.0.1.0/24")
.WithNetworkSecurityGroup(nsg);
The AzureSecurityRule type provides sensible defaults—SourcePortRange, SourceAddressPrefix, and DestinationAddressPrefix all default to "*". The Name, Priority, Direction, Access, Protocol, and DestinationPortRange properties are required.
A single NSG can be shared across multiple subnets.
Caution
Don't mix the shorthand and explicit APIs on the same subnet. Calling
WithNetworkSecurityGroup after shorthand methods throws an
InvalidOperationException to prevent silent rule loss.
Add private endpoints
Private endpoints enable secure connectivity to Azure services over a private network. Call AddPrivateEndpoint on a subnet builder and pass the Azure resource to connect to:
var vnet = builder.AddAzureVirtualNetwork("vnet");
var peSubnet = vnet.AddSubnet("private-endpoints", "10.0.2.0/24");
var storage = builder.AddAzureStorage("storage");
var blobs = storage.AddBlobs("blobs");
peSubnet.AddPrivateEndpoint(blobs);
When you add a private endpoint, the following resources are automatically created:
- A Private DNS Zone for the service (for example,
privatelink.blob.core.windows.net). - A Virtual Network Link connecting the DNS zone to your virtual network.
- A DNS Zone Group on the private endpoint for automatic DNS registration.
- The target resource is automatically configured to deny public network access.
The private DNS zone ensures that services within the virtual network automatically resolve the target resource's hostname to its private IP address. Resources that reference the target (for example, via WithReference) use the same connection information—DNS resolution handles routing traffic over the private network.
Caution
Adding a private endpoint automatically disables public network access on the target resource. This means any clients outside the virtual network won't be able to reach the resource. See Override public network access if you need to keep public access enabled.
Override public network access
To keep public access enabled on a resource that has a private endpoint, use ConfigureInfrastructure to override the automatic lockdown:
storage.ConfigureInfrastructure(infra =>
{
var storageAccount = infra.GetProvisionableResources()
.OfType<StorageAccount>()
.Single();
storageAccount.PublicNetworkAccess = StoragePublicNetworkAccess.Enabled;
});
Service-specific requirements
Azure Service Bus
Azure Service Bus requires the Premium tier to support private endpoints. When you add a private endpoint for a Service Bus resource, Aspire automatically sets the SKU to Premium.
Azure SQL Server
Azure SQL Server uses a deployment script to grant your application's managed identity access to the SQL database. When you add a private endpoint for a SQL Server resource, the deployment script needs to run inside the virtual network via Azure Container Instances (ACI). Aspire automatically creates a delegated subnet and a storage account for the deployment script container. For details on customizing this behavior, see Admin deployment script.
Complete example
The following example demonstrates a complete network topology with a virtual network, subnets, NSG rules, a NAT gateway, private endpoints, and an Azure Container Apps environment:
Note
The AZPROVISION001 warning suppressed below is required because the
Azure.Provisioning.Network APIs are in preview. When the APIs are marked stable,
the warning can be removed.
#pragma warning disable AZPROVISION001
using Aspire.Hosting.Azure;
using Azure.Provisioning.Network;
var builder = DistributedApplication.CreateBuilder(args);
// Create a virtual network with two subnets
var vnet = builder.AddAzureVirtualNetwork("vnet");
// Container Apps subnet with NSG rules and NAT gateway
var containerAppsSubnet = vnet.AddSubnet("container-apps", "10.0.0.0/23")
.AllowInbound(
port: "443",
from: AzureServiceTags.AzureLoadBalancer,
protocol: SecurityRuleProtocol.Tcp)
.DenyInbound(from: AzureServiceTags.VirtualNetwork)
.DenyInbound(from: AzureServiceTags.Internet);
var natGateway = builder.AddNatGateway("nat");
containerAppsSubnet.WithNatGateway(natGateway);
// Private endpoints subnet
var peSubnet = vnet.AddSubnet("private-endpoints", "10.0.2.0/27")
.AllowInbound(
port: "443",
from: AzureServiceTags.VirtualNetwork,
protocol: SecurityRuleProtocol.Tcp)
.DenyInbound(from: AzureServiceTags.Internet);
// Configure the Container App Environment to use the VNet.
// WithDelegatedSubnet assigns the subnet to the ACA environment,
// enabling subnet delegation so ACA can inject its infrastructure.
// For more information, see: https://learn.microsoft.com/azure/container-apps/custom-virtual-networks
builder.AddAzureContainerAppEnvironment("env")
.WithDelegatedSubnet(containerAppsSubnet);
// Add storage with private endpoints
var storage = builder.AddAzureStorage("storage");
var blobs = storage.AddBlobs("blobs");
var queues = storage.AddQueues("queues");
peSubnet.AddPrivateEndpoint(blobs);
peSubnet.AddPrivateEndpoint(queues);
builder.AddProject<Projects.Api>("api")
.WithExternalHttpEndpoints()
.WithReference(blobs)
.WithReference(queues);
builder.Build().Run();