Skip to content

Deploy infra only with Bicep

Objective

Provision the Azure foundation without building or deploying application images. Use the Agent Landing Zone repository and its infra/ source, including when no Agent Application images will be deployed. Bicep is the default infrastructure provider. For Terraform, use Deploy infra only with Terraform.

When to use

Use this runbook when you need foundation resources first, or will supply your own application. To deploy the Agent Application now, use the Deploy full stack runbook.

Prerequisites

Complete the permissions and tooling prerequisites. The deployment requires Git, Azure CLI, azd, PowerShell 7, Python and the platform hook dependencies. Review the resource inventory and cost scenarios and regional capacity caveats. Infrastructure-only still creates billable resources.

Blocks marked bash use Bash syntax. In PowerShell, put commands with a trailing backslash (\) on one line and remove the continuation backslashes. Replace every <placeholder> before running a command.

The effective footprint is the combination of enabled services, resource-reuse inputs, application definition, orchestrator topology and network settings. This determines which AI services, backing resources, runtimes, data stores, monitoring, networking and optional add-ons are deployed. Review the root main.parameters.json, Bicep conditions and preview to identify the resources for your deployment.

Procedure

Step 1: Clone Agent Landing Zone

Use this source for infrastructure-only and full-stack deployments:

git clone https://github.com/Azure/agent-landing-zone.git
cd agent-landing-zone

Run all lifecycle commands from this root, not from infra/. Its infra/ contains the foundation source. manifest.json records the coordinated application component pins.

Set up Python from the cloned repository before lifecycle commands.

Step 2: Create and confirm the target

Replace the placeholders for a new, dedicated environment:

az login
azd auth login
az account set --subscription <subscription-id>
azd env new <environment-name>
azd env set AZURE_SUBSCRIPTION_ID <subscription-id>
azd env set AZURE_RESOURCE_GROUP <dedicated-resource-group>
azd env set AZURE_LOCATION <region>

Use azd env select <environment-name> for an existing environment and keep its approved inputs.

Use separate environment, resource-group, identity, configuration and data boundaries for each stage; protect local .azure/ state. Fork or mirror the source only when maintaining your own inputs or automation.

The Bicep deployment through azd uses three resource groups by default:

Group What it contains Default name
Foundry Foundry and its related resources The AZURE_RESOURCE_GROUP name you set above
Application The selected application's services and data resources The same name with -app added
Network Workload networking and private connectivity resources The same name with -network added

For example, choosing rg-agent-dev above gives you rg-agent-dev, rg-agent-dev-app and rg-agent-dev-network.

The deployment scripts, also called hooks, create missing application and network groups. The identity running deployment needs permission to create them. If an administrator creates those groups in advance, set CREATE_RESOURCE_GROUPS=false so the scripts do not create groups.

To use different group names or combine resources into one or two groups, set the resource group destinations before first provision.

Run one azd provision command from the repository root to deploy across all three groups. Do not run a separate deployment for each group. These instructions apply to this Bicep procedure. The integrated Terraform runbook uses the same product lifecycle and root parameter source, with separate backend settings. The portal has its own deployment procedure.

Step 3: Choose the network setup

For production and workloads with sensitive data, use private service endpoints. Choose a self-contained private network or connect the workload's private network to an existing corporate hub. Both options are described below.

The template currently defaults to NETWORK_ISOLATION=false. Set it to true explicitly for a private deployment; the template default is not the recommended production configuration.

Use azd env set for the individual settings shown below. For example, azd env set NETWORK_ISOLATION true selects private networking for the selected environment.

Settings containing a list of values or a group of related fields, such as allowed IP ranges or the network address plan, go in the root main.parameters.json file. See Existing networks, private DNS and parameter files below for details.

DEPLOYMENT_MODE identifies the network design, but does not configure routing, DNS or isolation by itself. Configure those through the settings for your chosen approach below. DEPLOYMENT_TOPOLOGY separately chooses where the orchestrator runs. See How it works for how deployment settings are applied.

Private standalone network

Choose this when your workload team will manage its own network, private DNS and outbound firewall rather than use shared hub services.

azd env set NETWORK_ISOLATION true
azd env set DEPLOYMENT_MODE standalone
azd env set DEPLOY_AZURE_FIREWALL true
azd env set DEPLOY_JUMPBOX true
azd env set DEPLOY_BASTION true
azd env set DEPLOY_NAT_GATEWAY true

Review the standalone reference for nonoverlapping subnets, network security groups (NSGs), outbound routing and private DNS. For native Bastion Remote Desktop Protocol (RDP), choose BASTION_SKU_NAME=Standard and BASTION_ENABLE_TUNNELING=true; portal access does not require the native tunneling workflow. Have a connected host ready for Agent post-provision configuration, or use explicit deferral below.

Private spoke using an existing hub

Choose this when your organization already provides shared networking services. The workload gets its own private network, called a spoke, connected to that shared hub.

Agree hub routing, reverse peering, DNS and permissions with their owners first. For a new spoke VNet with a hub firewall:

azd env set NETWORK_ISOLATION true
azd env set DEPLOYMENT_MODE ailz-integrated
azd env set DEPLOY_AZURE_FIREWALL false
azd env set DEPLOY_BASTION false
azd env set DEPLOY_NAT_GATEWAY false
azd env set DEPLOY_JUMPBOX true
azd env set EXISTING_BASTION_RESOURCE_ID <hub-bastion-resource-id>
azd env set HUB_INTEGRATION_HUB_VNET_RESOURCE_ID <hub-vnet-resource-id>
azd env set HUB_INTEGRATION_CREATE_HUB_PEERING true
azd env set HUB_INTEGRATION_EGRESS_NEXT_HOP_IP <hub-firewall-private-ip>

Instead of a next-hop IP, a platform-managed route table can be selected with HUB_INTEGRATION_EXISTING_ROUTE_TABLE_RESOURCE_ID; these inputs are mutually exclusive. Set one when hub egress is required. Shared monitoring uses EXISTING_LOG_ANALYTICS_WORKSPACE_RESOURCE_ID and, when applicable, EXISTING_APPLICATION_INSIGHTS_RESOURCE_ID with its protected connection string.

Apply the hub-and-spoke DNS and peering contract before provisioning. A network operator may need to complete reverse peering and DNS links as soon as a fresh spoke VNet exists, before bootstrap can finish. Use an approved production hub, not the infra/tests/hub/ fixture.

Public service endpoints

Use this option only for an organization-approved, short-lived evaluation or demonstration with synthetic or non-sensitive data, when private connectivity is not available. It is not the recommended production setup.

The commands below select public service endpoints, not private networking. Authentication and authorization are still required, but they do not prevent network access to those public endpoints or make them private.

Use a separate test environment. Do not disable isolation on an existing private environment to fix a deployment or connectivity failure.

azd env set NETWORK_ISOLATION false
azd env set DEPLOYMENT_MODE standalone

Existing networks, private DNS and parameter files

Choice Required review
Existing VNet Choose subnet ownership and apply the matching inputs. Both peering directions are externally managed for BYO VNet
Shared private DNS Supply the applicable EXISTING_PRIVATE_DNS_ZONE_*_RESOURCE_ID inputs. Their owner supplies VNet links or resolver forwarding; peering alone does not propagate DNS
Azure Policy-managed DNS Use POLICY_MANAGED_PRIVATE_DNS=true instead of BYO zone overrides and agree who creates zone groups, records and links
Approved public exception Review native allowedIpRanges and Storage trusted-service/resource-instance exceptions to confirm the approved service-specific public access
Controlled public frontend Review Application Gateway ingress, certificate ownership, WAF policy and cost before opting in

Use azd env set for settings documented as environment variables that take one value, such as NETWORK_ISOLATION. Put settings containing lists or groups of related fields, such as the complete address plan, allowedIpRanges, Storage controls and publicIngress, in the reviewed root main.parameters.json.

Platform hooks regenerate infra/main.parameters.json; do not edit that generated copy. The parameter reference describes foundation inputs; Deployment settings describes platform composition and overrides.

Bind the future application

Choose the orchestrator topology and ingestion selection now, even if images will be deployed later. For a different application:

azd env set AGENTLZ_APP_DEFINITION <path-to-app-definition.json>

Otherwise use the Agent Application definition. The first provision binds the environment to its application ID; a different ID requires a new environment. Preserve the existing layout and ingestion selection during recovery.

The default Agent layout is hosted-no-panel, using maf_lite and Foundry IQ without Agent App Ingestion. Set HOSTED_AGENT_RESOURCE_SCOPE to the approved delegated Foundry audience followed by /.default before provisioning, even when application deployment will happen later. See the linked full-stack runbook for identity inputs, downstream consent and protected-retrieval acceptance.

Step 4: Preview and provision

Review the selected application's runtime egress plan before approving provisioning: python -m config.deployment.egress --plan --environment <environment-name>. This command is local and read-only; it does not run the preparation hook or create resource groups.

First run azd hooks run preup to validate the definition and binding before azd's resource-group initialization. Normal azd provision automatically runs the Agent preprovision hook to compose parameters and bootstrap authorized additional groups. Manual preparation is needed only if you choose to preview first.

Optional preview

Preview preparation can create resource groups

With azd 1.37.1, azd provision --preview does not run preprovision hooks. It reads the existing infra/main.parameters.json, which may still be the checked-in seed or another environment's composition, and cannot bootstrap missing additional destination groups.

Before an optional preview, select the intended azd environment, activate the prepared Python environment and run one matching hook from the platform root: pwsh -File scripts/preProvision.ps1 on Windows, or sh scripts/preProvision.sh on Linux/macOS. These hooks load selected azd values, compose parameters and can create missing additional groups when CREATE_RESOURCE_GROUPS=true. Otherwise those groups must already exist, or a composed split-group preview can fail with NotFound.

Native preview can also create the empty Foundry/root resource group. Neither fresh-environment preparation nor preview is guaranteed write-free; authorize group creation before running them. A successful preview does not prove private connectivity, service configuration or application readiness.

After this preparation, the optional preview command is:

azd provision --preview

If previewing, review its scopes, names, routes, resource changes, public access and role assignments. Resolve quota, Policy, subnet and capacity findings rather than skipping preflight.

Provision

With the resource changes approved, provision normally:

azd provision

Test connectivity after provisioning and application behavior after deploying your selected application.

Provisioning completes validation and binding, foundation deployment, then service configuration, role reconciliation and App Configuration publication unless explicitly deferred. It does not build or deploy application images.

Selected Container Apps can run placeholder images at this point. A placeholder response checks basic network and runtime reachability. Validate sign-in, tools, ingestion and document authorization after deploying the Agent Application. For initial infrastructure provisioning, inspect platform outputs below and continue to Validation. Use lifecycle tasks only when needed.

Platform outputs

After configuration completes, App Configuration publishes the JSON AGENTLZ_PLATFORM_OUTPUTS and matching flat keys under label agent-lz. Search and Cosmos sections are omitted when their endpoints are absent; other required fields must validate. These derived keys are not written back to the azd environment.

Flat key JSON path
AGENTLZ_FOUNDRY_PROJECT_ENDPOINT foundry.projectEndpoint
AGENTLZ_FOUNDRY_ACCOUNT_NAME foundry.accountName
AGENTLZ_ACR_LOGIN_SERVER registry.loginServer
AGENTLZ_APPCONFIG_ENDPOINT appConfig.endpoint
AGENTLZ_SEARCH_ENDPOINT search.endpoint
AGENTLZ_STORAGE_BLOB_ENDPOINT storage.blobEndpoint
AGENTLZ_COSMOS_ENDPOINT cosmos.endpoint
AGENTLZ_KEYVAULT_URI keyVault.uri

AGENTLZ_NETWORK_ISOLATED records isolation. AGENTLZ_IDENTITY_<COMPONENT>_CLIENT_ID and the JSON identities array contain discovered Container App identity client IDs, not principal IDs. Hosted runtime identities are created later, during application deployment. Missing or ambiguous identities fail publication; system-assigned identity resolution also requires the exact Entra service-principal read. AGENTLZ_COMPONENT_IDENTITIES is an explicit override input, not an automatic output.

az appconfig kv show --endpoint <app-configuration-endpoint> \
  --key AGENTLZ_PLATFORM_OUTPUTS --label agent-lz --auth-mode login

If only output publication needs refreshing after successful configuration:

azd env refresh
python -m config.deployment.outputs

This does not replace deferred or failed service configuration.

Complete a deferred post-provision

Private configuration can be explicitly deferred by RUN_FROM_JUMPBOX=false, or by AZURE_SKIP_NETWORK_ISOLATION_WARNING=true when RUN_FROM_JUMPBOX is not truthy. Set the approved deferral before the first provision when the connected host does not exist yet. Successful provisioning then reports that application setup is incomplete. Deferral does not bypass earlier topology/private-service checks.

On a new connected host, restore the same reviewed source revision and root parameter file. Install the prerequisites and Python dependencies, authenticate both CLIs, then initialize local state only if absent:

azd env new <same-environment-name>
azd env set AZURE_SUBSCRIPTION_ID <subscription-id>
azd env set AZURE_RESOURCE_GROUP <existing-resource-group>
azd env set AZURE_LOCATION <region>
azd env refresh

If the environment is already present, select it instead. Reapply the approved deployment inputs: refresh restores outputs, not your full configuration.

For the multi-group path, restore the same approved application/network destination mapping as well; the root group alone is not sufficient. Securely restore .azure/<same-environment-name>/resource-groups.json from that environment's original host before running its hooks. Output refresh does not recreate this immutable destination binding; do not generate or edit a replacement to bypass the check.

Do not publish or copy another stage's .azure/ state as an input artifact. An authorized runner can use managed-identity sign-in. Verify its scoped configuration and build roles before running the hooks.

From the connected host, clear deferral and finish configuration:

azd env set RUN_FROM_JUMPBOX true
azd env set AZURE_SKIP_NETWORK_ISOLATION_WARNING false
azd hooks run postprovision

RUN_FROM_JUMPBOX requests connectivity checks on the connected host. Verify platform outputs and required data-plane access before deploying. Private native Blob sources can require an administrator to approve the exact Search shared private-link request reported by the readiness gate. Do not approve every pending connection or bypass the gate.

Reconfigure the foundation

Record the deployed source revision, approved parameters, outputs and dependent applications. Back up required data and configuration, check compatibility and retain existing names, locations, layout and ingestion intent. If previewing changes, repeat the selected-environment preparation before reviewing the preview. Apply approved root parameter changes with the provisioning step above.

Update the coordinated platform and its infra/ tree together. Use the reapply procedure and rollback in the Deploy full stack runbook.

Retry a partial deployment with the same approved revision and environment after repairing the specific failure. Review resource and data changes separately when reverting code. Do not delete data, rename subnets, move existing resources to another region, weaken isolation or bypass preflight to recover. Turning a deploy flag off is not deletion or permission cleanup.

Add an application later

Deploy the same bound application and selected components. Follow Step 4 of the Deploy full stack runbook: run azd deploy from the configured foundation. Deployment automatically prepares the hosted image and its handoff; no second provision is required. Custom definitions follow their own component contract.

Remove the foundation

Use the safe environment removal procedure from the selected source root and environment. Confirm ownership and backups before deletion. For a spoke, have network/DNS owners remove only the recorded spoke-specific attachments and preserve shared services. Purge is a separate irreversible decision, not a normal retry or rollback.

Validation

  1. Inspect the selected target and inventory:

    azd env get-value AZURE_SUBSCRIPTION_ID
    azd env get-value AZURE_RESOURCE_GROUP
    az resource list -g <resource-group> --query "[].{name:name,type:type}" -o table
    az network private-endpoint list -g <resource-group> \
      --query "[].{name:name,state:provisioningState,connections:privateLinkServiceConnections[].privateLinkServiceConnectionState.status}" -o json
    

    For the multi-group path, repeat resource inventory for every distinct destination, and inspect private endpoints in their actual group. Do not infer a Foundry or network resource's group from the application group.

  2. Compare the inventory with enabled features and reuse inputs. Account for both new and reused resources.

  3. For private deployments, verify approved connections, private DNS and TCP/TLS reachability from the actual deployment host. Use the standalone checks or hub checks.
  4. Test required service operations with the authorized configuration identity and confirm telemetry reaches the intended workspace.
  5. Inspect post-provision logs and platform outputs. Record explicit deferral or any logged configuration failure as incomplete. A placeholder HTTP response does not prove application readiness.

Rollback

  1. Stop retries and record which resources and configuration changed. If only local inputs changed, restore reviewed inputs before provisioning.
  2. Compare previous inputs with deployed state. Preview the previous compatible source and parameters with azd provision --preview from the selected root, following the Agent preview preparation when applicable.
  3. Apply with azd provision only if reviewed changes preserve data, shared resources and the approved network posture.
  4. Repeat Validation. If inputs cannot safely reverse a change, stop and arrange service-specific forward repair or recovery with its owner.

Successful provisioning does not restore deleted data or reverse schema changes. Disabling a flag does not delete resources or revoke grants. Use the separately authorized removal procedure only for intentional retirement, not as a retry.

© 2026 Azure AI Landing Zones