Skip to content

AI LZ using an existing VNet

This walkthrough validates deploying the AI Landing Zone into a VNet that already exists. It is intended for learning and validation, and it follows the same expectations described in the networking and platform guidance.

Sample parameter files

File Scenario What happens to the VNet and subnets
bicep/infra/sample.existing-vnet.bicepparam Scenario A Reuses an existing VNet and expects the required subnets to already exist.
bicep/infra/sample.existing-vnet-create-subnets.bicepparam Scenario B Reuses an existing VNet and creates or updates the required subnets in that VNet.

What this walkthrough validates

Capability Notes
Existing VNet reuse Uses resourceIds.virtualNetworkResourceId and does not create a new VNet.
Workload-owned private connectivity Creates Private Endpoints and, in this scenario, also creates Private DNS zones and VNet links.
Forced tunneling validation When enabled by toggles, deploys UDRs and routes egress through Azure Firewall.
Split resource groups Keeps the VNet in a dedicated networking resource group and deploys workload resources to a separate workload resource group.

This walkthrough assumes standalone behavior for platform integration, with flagPlatformLandingZone set to false.

Key settings (from the samples)

Setting Value Notes
flagPlatformLandingZone false The workload deployment owns private DNS zones and links for this walkthrough.
deployToggles.virtualNetwork false The deployment reuses an existing VNet.
resourceIds.virtualNetworkResourceId Required Must be the full resource ID of the existing VNet.
existingVNetSubnetsDefinition Optional Use only for Scenario B, when the deployment should create or update subnets.

Scope and assumptions

The existing VNet must be in the same subscription used by the environment. Resources that attach to subnets, such as Private Endpoints, NICs, Bastion, and route table associations, must be deployed in the same subscription as the VNet.

Prerequisites

You need permissions to create resources in the target subscription, and you must be signed in with Azure CLI. This walkthrough uses Azure Developer CLI, so install azd and run the commands from the repository root.

Step 1: Prepare the existing VNet

Use one of the scenarios below. Both scenarios use the same default VNet address space and subnet layout.

Scenario A expects the required subnets to already exist. Scenario B creates or updates the subnets as part of the deployment.

Default addressing used by the samples

VNet address space is 192.168.0.0/23.

Subnet name CIDR Notes
agent-subnet 192.168.0.0/27 Delegated to Microsoft.App/environments.
pe-subnet 192.168.0.32/27 Private endpoint network policies must be disabled.
AzureBastionSubnet 192.168.0.64/26 Required minimum size for Bastion.
AzureFirewallSubnet 192.168.0.128/26 Required minimum size for Azure Firewall.
appgw-subnet 192.168.0.192/27 Optional depending on toggles.
aca-env-subnet 192.168.1.0/27 Delegated to Microsoft.App/environments.
devops-agents-subnet 192.168.1.32/27 Build agents subnet.
jumpbox-subnet 192.168.1.64/28 Jump VM subnet.

Scenario A: create the VNet and subnets up front

Choose a location and names that you will reuse across commands.

$location = "eastus2"
$netRg = "rg-ailz-vnet-RANDOM_SUFFIX"
$vnetName = "vnet-existing-RANDOM_SUFFIX"

Create the networking resource group and VNet.

az group create --name $netRg --location $location
az network vnet create --resource-group $netRg --name $vnetName --address-prefixes 192.168.0.0/23

Create the required subnets.

az network vnet subnet create --resource-group $netRg --vnet-name $vnetName --name agent-subnet --address-prefixes 192.168.0.0/27 --delegations Microsoft.App/environments

az network vnet subnet create --resource-group $netRg --vnet-name $vnetName --name pe-subnet --address-prefixes 192.168.0.32/27 --disable-private-endpoint-network-policies true

az network vnet subnet create --resource-group $netRg --vnet-name $vnetName --name AzureBastionSubnet --address-prefixes 192.168.0.64/26

az network vnet subnet create --resource-group $netRg --vnet-name $vnetName --name AzureFirewallSubnet --address-prefixes 192.168.0.128/26

az network vnet subnet create --resource-group $netRg --vnet-name $vnetName --name appgw-subnet --address-prefixes 192.168.0.192/27

az network vnet subnet create --resource-group $netRg --vnet-name $vnetName --name aca-env-subnet --address-prefixes 192.168.1.0/27 --delegations Microsoft.App/environments

az network vnet subnet create --resource-group $netRg --vnet-name $vnetName --name devops-agents-subnet --address-prefixes 192.168.1.32/27

az network vnet subnet create --resource-group $netRg --vnet-name $vnetName --name jumpbox-subnet --address-prefixes 192.168.1.64/28

Verify subnets and capture the VNet resource ID.

az network vnet subnet list --resource-group $netRg --vnet-name $vnetName -o table

$vnetId = az network vnet show --resource-group $netRg --name $vnetName --query id -o tsv
$vnetId

Scenario B: create only the VNet and let the template create or update subnets

Create the networking resource group and VNet.

$location = "eastus2"
$netRg = "rg-ailz-vnet-RANDOM_SUFFIX"
$vnetName = "vnet-existing-RANDOM_SUFFIX"

az group create --name $netRg --location $location
az network vnet create --resource-group $netRg --name $vnetName --address-prefixes 192.168.0.0/23

$vnetId = az network vnet show --resource-group $netRg --name $vnetName --query id -o tsv
$vnetId

If you already have a VNet that matches the expected layout, skip the creation commands and only capture the VNet resource ID.

Step 2: Deploy the workload with azd

Initialize an azd environment.

azd init -e ailz-existing-vnet-RANDOM_SUFFIX

Set environment variables.

$env:AZURE_LOCATION = "eastus2"
$env:AZURE_RESOURCE_GROUP = "rg-ailz-RANDOM_SUFFIX"
$env:AZURE_SUBSCRIPTION_ID = "00000000-1111-2222-3333-444444444444"

$workloadRg = $env:AZURE_RESOURCE_GROUP

Copy the sample parameter file into the active parameter file used by azd.

Scenario A uses the reuse-as-is sample.

Copy-Item bicep/infra/sample.existing-vnet.bicepparam bicep/infra/main.bicepparam -Force

Scenario B uses the create-update-subnets sample.

Copy-Item bicep/infra/sample.existing-vnet-create-subnets.bicepparam bicep/infra/main.bicepparam -Force

Edit bicep/infra/main.bicepparam and set resourceIds.virtualNetworkResourceId to $vnetId.

For Scenario A, do not set existingVNetSubnetsDefinition.

For Scenario B, provide an existingVNetSubnetsDefinition block with the subnet layout you want the deployment to create or update.

param existingVNetSubnetsDefinition = {
  useDefaultSubnets: false
  subnets: [
    {
      name: 'agent-subnet'
      addressPrefix: '192.168.0.0/27'
      delegation: 'Microsoft.App/environments'
      serviceEndpoints: ['Microsoft.CognitiveServices']
    }
    {
      name: 'pe-subnet'
      addressPrefix: '192.168.0.32/27'
      privateEndpointNetworkPolicies: 'Disabled'
      serviceEndpoints: ['Microsoft.AzureCosmosDB']
    }
    {
      name: 'AzureBastionSubnet'
      addressPrefix: '192.168.0.64/26'
    }
    {
      name: 'AzureFirewallSubnet'
      addressPrefix: '192.168.0.128/26'
    }
    {
      name: 'appgw-subnet'
      addressPrefix: '192.168.0.192/27'
    }
    {
      name: 'aca-env-subnet'
      addressPrefix: '192.168.1.0/27'
      delegation: 'Microsoft.App/environments'
      serviceEndpoints: ['Microsoft.AzureCosmosDB']
    }
    {
      name: 'devops-agents-subnet'
      addressPrefix: '192.168.1.32/27'
    }
    {
      name: 'jumpbox-subnet'
      addressPrefix: '192.168.1.64/28'
    }
  ]
}

Provision.

azd provision

If forced tunneling is enabled, ensure firewallPrivateIp matches the private IP assigned to Azure Firewall. With the default AzureFirewallSubnet prefix, the common value is 192.168.0.132, but you should always confirm the actual IP in your environment.

Validation

Validate that private endpoints were created in the workload resource group.

az network private-endpoint list --resource-group $workloadRg -o table

Because this walkthrough uses flagPlatformLandingZone set to false, validate that private DNS zones exist and are linked.

az network private-dns zone list --resource-group $workloadRg -o table

az network private-dns link vnet list --resource-group $workloadRg --zone-name privatelink.openai.azure.com -o table

Validate that A records exist.

az network private-dns record-set a list --resource-group $workloadRg --zone-name privatelink.openai.azure.com -o table

If forced tunneling is enabled, validate that route tables exist and are associated with the expected subnets.

az network route-table list --resource-group $workloadRg -o table
az network vnet subnet show --resource-group $netRg --vnet-name $vnetName --name jumpbox-subnet --query routeTable.id -o tsv

Validate access through Bastion and the Jump VM.

Reset the Jump VM password in the Azure portal and connect using Bastion. Then, from inside the Jump VM, validate DNS resolution for your created services.

Resolve-DnsName login.microsoftonline.com
Test-NetConnection login.microsoftonline.com -Port 443

To validate private DNS resolution for specific Private Endpoints, list the FQDNs associated with a Private Endpoint and test resolution inside the Jump VM.

$peName = "<private-endpoint-name>"
az network private-endpoint dns-zone-group list --resource-group $workloadRg --endpoint-name $peName --query "[].privateDnsZoneConfigs[].recordSets[].fqdn" -o tsv

Cleanup

Delete the workload resource group.

az group delete --name $workloadRg --yes --no-wait

If the VNet was created only for this walkthrough, delete the networking resource group.

az group delete --name $netRg --yes --no-wait
© 2026 Azure AI Landing Zones