Skip to content

AI LZ without Platform Landing Zone

Use this scenario when you want a clean, isolated deployment where the workload resource group owns the networking end to end.

Sample parameter file

bicep/infra/sample.standalone.bicepparam

What this walkthrough validates

Capability Notes
Workload-owned networking Creates a new VNet and subnets in the workload resource group.
Private connectivity Creates Private Endpoints, Private DNS Zones, and VNet links for enabled services.
Forced tunneling Sends egress (0.0.0.0/0) through Azure Firewall via UDRs.

Key settings (from the sample)

Setting Value
flagPlatformLandingZone false
deployToggles.virtualNetwork true
deployToggles.firewall true
deployToggles.userDefinedRoutes true
deployToggles.jumpVm true
deployToggles.bastionHost true

If you use a different parameter file (or modify bicep/infra/main.bicepparam), treat the validation steps as conditional. For example, disabling VNet creation changes the networking checks, and disabling specific services changes what you should expect in the private endpoint and DNS sections.

Note: this walkthrough is intended for validation and learning. Adjust naming, IP ranges, and hardening before using it for production.

Prerequisites

You need permissions to create resources in the target subscription (for example, Contributor), and you must be signed in with Azure CLI (az login). You also need Azure Developer CLI installed (azd), because this walkthrough uses it to initialize the repository locally and to run the deployment.

Deployment

Create a local working directory and run the commands from there.

mkdir deploy
cd deploy

Initialize the environment.

azd init -e ailz-standalone-RANDOM_SUFFIX

Set environment variables.

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

# Convenience variable used by the commands below
$rg = $env:AZURE_RESOURCE_GROUP

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

Copy-Item bicep/infra/sample.standalone.bicepparam bicep/infra/main.bicepparam -Force

Optional edits in bicep/infra/main.bicepparam.

Scenario Change
You change subnet CIDRs Update the firewall policy sourceAddresses entries to match.
You do not want Jump VM auto-install Set jumpVmDefinition.enableAutoInstall = false.
Your org manages RBAC outside the template If you hit RoleAssignmentExists, set jumpVmDefinition.assignContributorRoleAtResourceGroup = false.

Provision.

azd provision

Validation: networking (VNet and subnets)

If you do not see a VNet in the workload resource group after provisioning, confirm that you are inspecting the correct resource group.

Validate the VNet and subnets.

az network vnet list --resource-group $rg -o table

# If you have only one VNet in this RG, capture it:
$vnetName = (az network vnet list --resource-group $rg --query "[0].name" -o tsv)

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

Expected subnets (default template layout):

Subnet name
agent-subnet
pe-subnet
AzureBastionSubnet
AzureFirewallSubnet
appgw-subnet
aca-env-subnet
devops-agents-subnet
jumpbox-subnet

Validation: forced tunneling (Firewall and UDR)

Validate the firewall and capture its private IP.

az network firewall list --resource-group $rg -o table

$afwName = (az network firewall list --resource-group $rg --query "[0].name" -o tsv)
$afwPrivateIp = (az network firewall show --resource-group $rg --name $afwName --query "ipConfigurations[0].privateIPAddress" -o tsv)
$afwPrivateIp

The sample sets firewallPrivateIp based on the default AzureFirewallSubnet prefix (192.168.0.128/26) and the first usable IP (192.168.0.132). If you changed the firewall subnet prefix, update firewallPrivateIp in your parameter file.

Validate the route table and subnet association.

az network route-table list --resource-group $rg -o table

# Check that jumpbox-subnet has a route table attached
az network vnet subnet show --resource-group $rg --vnet-name $vnetName --name jumpbox-subnet --query routeTable.id -o tsv

Validation: private endpoints and private DNS

In standalone mode, the workload deployment creates and manages private DNS zones.

Validate that private endpoints exist.

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

# Helpful: show what each Private Endpoint targets + which FQDNs it expects to resolve
az network private-endpoint list --resource-group $rg \
  --query "[].{name:name,target:privateLinkServiceConnections[0].privateLinkServiceId,groupIds:privateLinkServiceConnections[0].groupIds,fqdns:customDnsConfigs[].fqdn}" -o jsonc

# Note: sometimes `customDnsConfigs` returns empty. In that case, query DNS zone groups per Private Endpoint:
# $peName = '<private-endpoint-name>'
# az network private-endpoint dns-zone-group list --resource-group $rg --endpoint-name $peName \
#   --query "[].privateDnsZoneConfigs[].recordSets[].fqdn" -o tsv

With the shipped sample toggles, you should see Private Endpoints for Key Vault, Storage, and AI Foundry. If you changed toggles, validate based on what you actually enabled.

Validate that private DNS zones exist and are linked.

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

# Example: check OpenAI private link zone exists and has a VNet link
az network private-dns link vnet list --resource-group $rg --zone-name privatelink.openai.azure.com -o table

Validate that A records exist.

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

Validation: access (Bastion and Jump VM)

Confirm that Bastion exists.

az network bastion list --resource-group $rg -o table

Connect to the Jump VM using Bastion. Because the VM password is auto-generated during deployment, reset it in the Azure portal before connecting. Open the workload resource group, select the jump VM (the *-jmp VM), choose Reset password, and set a strong password for azureuser (unless you changed jumpVmDefinition.adminUsername). Then connect using Bastion (RDP).

Optional (inside the Jump VM): validate outbound connectivity under forced tunneling:

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

Validate DNS resolution for the created services from inside the Jump VM. The goal is to confirm that service hostnames resolve to private IPs via the privatelink.* private DNS zones.

From your local machine, capture the Private Endpoint FQDNs created by the deployment:

# List all Private Endpoints expect to resolve
az network private-endpoint list --resource-group $rg --query "[].{name:name}" -o jsonc

# Extract FQDNs from the DNS zone groups:
$peName = '<private-endpoint-name>'
az network private-endpoint dns-zone-group list --resource-group $rg --endpoint-name $peName --query "[].privateDnsZoneConfigs[].recordSets[].fqdn" -o tsv

Then, inside the Jump VM (PowerShell), run nslookup for the exact FQDNs returned by the command above.

Examples:

nslookup <fqdn-from-customDnsConfigs>

# Optional: show the CNAME chain (useful for troubleshooting)
Resolve-DnsName <fqdn-from-customDnsConfigs> | Format-List

Expected outcome:

Check Expected
nslookup Returns a private IP address (typically within your VNet range).
Resolve-DnsName Shows a CNAME into the matching privatelink.* zone.

Cleanup

Delete the resource group.

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