VNet
Attach a sandbox group to one or more customer Azure Virtual Networks (VNets) so its sandboxes can reach private endpoints, peered networks, and on-prem resources alongside the egress proxy.
A sandbox group holds a list of VNET connections. Each connection points at a delegated subnet in a VNet that lives in the same region as the group. You can add connections at group creation time or any time after.
What you configure
Each VNET connection has three fields:
| Field | Description |
|---|---|
| Connection name | Friendly name unique within the group (e.g. prod-vnet, shared-services). |
| Virtual network | A VNet in the same subscription and region as the sandbox group. |
| Subnet | A subnet inside the chosen VNet, delegated to Microsoft.App/environments. Pick an empty subnet sized for your peak concurrent sandboxes plus headroom (/23 or larger is a safe default). |
When you create a connection inline at group creation time, you can also let the portal create the VNet and subnet for you (10.0.0.0/16 VNet, 10.0.0.0/23 subnet by default).
Add a VNET connection at group creation
- In the portal, start Create sandbox group and switch to the Advanced tier.
- Fill in Basics (name, subscription, resource group, region).
- Under Networking, turn on Use custom virtual network.
- Pick an existing VNet and subnet delegated to
Microsoft.App/environments, or accept the defaults to create new ones inline. - Create the group.
The first VNET connection is wired up as part of the deployment. Add more later from the group's Networking page.
Creating a sandbox group and its first VNET connection together is currently supported in the portal flow only. CLI/SDK support for inline VNET connection creation is coming soon. For now, create the group first, then follow the next section to add a VNET connection after group creation.
Manage VNET connections after creation
Use one of these paths after the group exists:
- Portal: If you already added a VNET connection while creating the sandbox group, you do not need to add another one unless you want additional options.
- CLI/SDK: Create the group's first VNET connection here (or add another one later).
Portal
Use Sandbox group → Networking to add extra connections:
- Open the sandbox group in the portal and select Networking in the left nav.
- Click + Add VNET connection.
- Provide a Connection name, pick a Virtual network (the list is filtered to the group's region), then pick a Subnet delegated to
Microsoft.App/environments. - Click Add. The new connection appears in the list with its status.
CLI or SDK
- Bash
- PowerShell
- SDK
Set VNET_SUBNET_ID to the resource id of a subnet delegated to Microsoft.App/environments in the same region as the group.
aca sandboxgroup network create \
--group my-group \
--name infra-subnet-conn \
--vnet-subnet-id "$VNET_SUBNET_ID"
Set VNET_SUBNET_ID to the resource id of a subnet delegated to Microsoft.App/environments in the same region as the group.
aca sandboxgroup network create `
--group my-group `
--name infra-subnet-conn `
--vnet-subnet-id $env:VNET_SUBNET_ID
mgmt = SandboxGroupManagementClient(
credential=credential,
subscription_id="<subscription-id>",
resource_group="<resource-group>",
)
mgmt.create_or_update_vnet_connection(
"my-group",
"infra-subnet-conn",
"/subscriptions/<subscription-id>/resourceGroups/<resource-group>/providers/Microsoft.Network/virtualNetworks/<vnet>/subnets/<subnet>",
)
Create a sandbox with a VNET connection
When you create a sandbox, choose which existing group-level VNET connection it should use.
- Open your sandbox group, then go to Sandboxes.
- Select + Create sandbox.
- Fill in the sandbox basics (name, image, and compute options).
- In networking settings, choose the VNET connection to attach.
- Finish the remaining settings and create the sandbox.
The selected VNET connection is immutable for that sandbox after creation. To switch a sandbox to a different VNET connection, create a new sandbox with the target connection.
- Bash
- PowerShell
- SDK
aca sandbox create \
--group my-group \
--disk ubuntu \
--customer-vnet-connection-name infra-subnet-conn \
--label name=demo-vnet
aca sandbox create `
--group my-group `
--disk ubuntu `
--customer-vnet-connection-name infra-subnet-conn `
--label name=demo-vnet
group_client = SandboxGroupClient(
endpoint_for_region("westus2"),
credential,
subscription_id="<subscription-id>",
resource_group="<resource-group>",
sandbox_group="my-group",
)
sandbox = group_client.begin_create_sandbox(
disk="ubuntu",
customer_vnet_connection_name="infra-subnet-conn",
).result()
print(sandbox.sandbox_id)
Verify VNET connectivity from a sandbox
After creating a sandbox with a VNET connection, you can validate private data-plane reachability by creating a Storage private endpoint in the same VNet, wiring private DNS, and then testing from the sandbox.
This example does three things:
- Creates an additional subnet for the private endpoint in the same VNet.
- Creates a storage account with public network access disabled.
- Creates a private endpoint plus private DNS integration, then runs a request from the sandbox.
Set VNET_SUBNET_ID to the delegated subnet resource id already used by your sandbox group's VNET connection.
This verification flow is currently documented for Bash and PowerShell.
- Bash
- PowerShell
SANDBOX_SUBNET_ID="$VNET_SUBNET_ID"
RG="$(echo "$SANDBOX_SUBNET_ID" | awk -F/ '{print $5}')"
VNET_NAME="$(echo "$SANDBOX_SUBNET_ID" | awk -F/ '{print $9}')"
LOCATION="$(az network vnet show --resource-group "$RG" --name "$VNET_NAME" --query location -o tsv)"
PE_SUBNET_NAME="storage-pe-subnet"
PE_SUBNET_PREFIX="10.0.3.0/27"
PE_NAME="pe-stvnet-$RANDOM"
ZONE_NAME="privatelink.blob.core.windows.net"
LINK_NAME="link-$VNET_NAME-blob"
ZONE_GROUP_NAME="blob-zone-group"
SA_NAME="stvnet$RANDOM$RANDOM"
CONTAINER_NAME="plcheck"
SAS_EXPIRY="2035-01-01T00:00Z"
SANDBOX_GROUP="my-group"
SANDBOX_NAME="demo-vnet"
az network vnet subnet create \
--resource-group "$RG" \
--vnet-name "$VNET_NAME" \
--name "$PE_SUBNET_NAME" \
--address-prefixes "$PE_SUBNET_PREFIX" \
--disable-private-endpoint-network-policies true \
--output none
PE_SUBNET_ID="$(az network vnet subnet show \
--resource-group "$RG" \
--vnet-name "$VNET_NAME" \
--name "$PE_SUBNET_NAME" \
--query id -o tsv)"
az storage account create \
--resource-group "$RG" \
--name "$SA_NAME" \
--location "$LOCATION" \
--sku Standard_LRS \
--kind StorageV2 \
--public-network-access Enabled \
--output none
STORAGE_CONNECTION_STRING="$(az storage account show-connection-string \
--resource-group "$RG" \
--name "$SA_NAME" \
--query connectionString -o tsv)"
az storage container create \
--name "$CONTAINER_NAME" \
--connection-string "$STORAGE_CONNECTION_STRING" \
--auth-mode key \
--output none
SAS_TOKEN="$(az storage container generate-sas \
--name "$CONTAINER_NAME" \
--permissions rl \
--expiry "$SAS_EXPIRY" \
--connection-string "$STORAGE_CONNECTION_STRING" \
--auth-mode key \
--output tsv)"
SAS_URL="https://$SA_NAME.blob.core.windows.net/$CONTAINER_NAME?restype=container&comp=list&$SAS_TOKEN"
az storage account update \
--resource-group "$RG" \
--name "$SA_NAME" \
--public-network-access Disabled \
--output none
SA_ID="$(az storage account show --resource-group "$RG" --name "$SA_NAME" --query id -o tsv)"
MSYS_NO_PATHCONV=1 az network private-endpoint create \
--resource-group "$RG" \
--name "$PE_NAME" \
--vnet-name "$VNET_NAME" \
--subnet "$PE_SUBNET_ID" \
--private-connection-resource-id "$SA_ID" \
--group-id blob \
--connection-name "$PE_NAME-conn" \
--output none
az network private-dns zone create \
--resource-group "$RG" \
--name "$ZONE_NAME" \
--output none
az network private-dns link vnet create \
--resource-group "$RG" \
--zone-name "$ZONE_NAME" \
--name "$LINK_NAME" \
--virtual-network "$VNET_NAME" \
--registration-enabled false \
--output none
az network private-endpoint dns-zone-group create \
--resource-group "$RG" \
--endpoint-name "$PE_NAME" \
--name "$ZONE_GROUP_NAME" \
--private-dns-zone "$ZONE_NAME" \
--zone-name blob \
--output none
aca sandbox exec --group "$SANDBOX_GROUP" -l name="$SANDBOX_NAME" -c "getent hosts $SA_NAME.blob.core.windows.net"
aca sandbox exec --group "$SANDBOX_GROUP" -l name="$SANDBOX_NAME" -c "curl -sS --http1.1 --connect-timeout 10 --max-time 30 -o /dev/null -w '%{http_code}\n' \"$SAS_URL\""
az network private-endpoint delete \
--resource-group "$RG" \
--name "$PE_NAME" \
--output none
az network private-dns link vnet delete \
--resource-group "$RG" \
--zone-name "$ZONE_NAME" \
--name "$LINK_NAME" \
--yes \
--output none
az network private-dns zone delete \
--resource-group "$RG" \
--name "$ZONE_NAME" \
--yes \
--output none
az storage account delete \
--resource-group "$RG" \
--name "$SA_NAME" \
--yes \
--output none
$sandboxSubnetId = $env:VNET_SUBNET_ID
$parts = $sandboxSubnetId -split '/'
$rgName = $parts[4]
$vnetName = $parts[8]
$location = az network vnet show --resource-group $rgName --name $vnetName --query location --output tsv
$peSubnetName = "storage-pe-subnet"
$peSubnetPrefix = "10.0.16.0/27"
$peName = "pe-stvnet-$((Get-Random -Minimum 10000 -Maximum 99999))"
$zoneName = "privatelink.blob.core.windows.net"
$linkName = "link-$vnetName-blob"
$zoneGroupName = "blob-zone-group"
$saName = "stvnet$((Get-Random -Minimum 10000 -Maximum 99999))$((Get-Random -Minimum 10000 -Maximum 99999))"
$containerName = "plcheck"
$sasExpiry = "2035-01-01T00:00Z"
$sandboxGroup = "my-group"
$sandboxName = "demo-vnet"
# Prepare: create private endpoint subnet, storage account, and private DNS wiring.
az network vnet subnet create `
--resource-group $rgName `
--vnet-name $vnetName `
--name $peSubnetName `
--address-prefixes $peSubnetPrefix `
--disable-private-endpoint-network-policies true `
--output none
$peSubnetId = az network vnet subnet show `
--resource-group $rgName `
--vnet-name $vnetName `
--name $peSubnetName `
--query id `
--output tsv
az storage account create `
--resource-group $rgName `
--name $saName `
--location $location `
--sku Standard_LRS `
--kind StorageV2 `
--public-network-access Enabled `
--output none
$storageConnectionString = az storage account show-connection-string `
--resource-group $rgName `
--name $saName `
--query connectionString `
--output tsv
az storage container create `
--name $containerName `
--connection-string $storageConnectionString `
--auth-mode key `
--output none
$sasToken = az storage container generate-sas `
--name $containerName `
--permissions rl `
--expiry $sasExpiry `
--connection-string $storageConnectionString `
--auth-mode key `
--output tsv
$sasUrl = "https://$saName.blob.core.windows.net/$containerName`?restype=container&comp=list&$sasToken"
az storage account update `
--resource-group $rgName `
--name $saName `
--public-network-access Disabled `
--output none
$saId = az storage account show --resource-group $rgName --name $saName --query id --output tsv
az network private-endpoint create `
--resource-group $rgName `
--name $peName `
--vnet-name $vnetName `
--subnet $peSubnetId `
--private-connection-resource-id $saId `
--group-id blob `
--connection-name "$peName-conn" `
--output none
az network private-dns zone create `
--resource-group $rgName `
--name $zoneName `
--output none
az network private-dns link vnet create `
--resource-group $rgName `
--zone-name $zoneName `
--name $linkName `
--virtual-network $vnetName `
--registration-enabled false `
--output none
az network private-endpoint dns-zone-group create `
--resource-group $rgName `
--endpoint-name $peName `
--name $zoneGroupName `
--private-dns-zone $zoneName `
--zone-name blob `
--output none
# Validation: expect a private IP (10.0.x.x style) for blob endpoint resolution.
aca sandbox exec --group $sandboxGroup -l name=$sandboxName -c "getent hosts $saName.blob.core.windows.net"
# Validation: expect HTTP status 200 from SAS-authenticated storage request.
aca sandbox exec --group $sandboxGroup -l name=$sandboxName -c "curl -sS --http1.1 --connect-timeout 10 --max-time 30 -o /dev/null -w '%{http_code}\n' `"$sasUrl`""
# Tear down: remove private endpoint/DNS resources and delete the storage account.
az network private-endpoint delete `
--resource-group $rgName `
--name $peName `
--output none
az network private-dns link vnet delete `
--resource-group $rgName `
--zone-name $zoneName `
--name $linkName `
--yes `
--output none
az network private-dns zone delete `
--resource-group $rgName `
--name $zoneName `
--yes `
--output none
az storage account delete `
--resource-group $rgName `
--name $saName `
--yes `
--output none
getent hosts should resolve <storage-account>.blob.core.windows.net to a private IP. 200 from the SAS curl confirms authenticated data-plane access over the same private path.
Remove a VNET connection
Remove a VNET connection when it is no longer needed.
- Open the sandbox group and select Networking.
- Find the VNET connection row you want to remove.
- Ensure no sandboxes still depend on that connection.
- Select Remove from the row actions and confirm.
After removal, new sandboxes cannot use that connection.
- Bash
- PowerShell
- SDK
aca sandbox delete -l name=demo-vnet --yes
aca sandboxgroup network delete \
--group my-group \
--name infra-subnet-conn
aca sandbox delete -l name=demo-vnet --yes
aca sandboxgroup network delete `
--group my-group `
--name infra-subnet-conn
sandbox.delete()
mgmt = SandboxGroupManagementClient(
credential=credential,
subscription_id="<subscription-id>",
resource_group="<resource-group>",
)
mgmt.delete_vnet_connection("my-group", "infra-subnet-conn")
How it interacts with egress
VNET connections control where sandboxes get IP addresses and what they can reach over the data plane. They do not change the egress proxy posture — outbound traffic still flows through the proxy with deny-default host allowlists. Private endpoints reachable from your VNet (Azure SQL, Storage, Key Vault, internal APIs) work the same way they do from any other VNet-integrated compute.
Deeper guidance — subnet delegation requirements, peering patterns, private DNS, ExpressRoute and VPN scenarios, and troubleshooting connectivity — is in progress.