Edit

Layered provisioning

Azure Developer CLI (azd) supports layered provisioning, which you can use to define multiple provisioning layers in your azure.yaml file. Each layer points to its own set of Infrastructure as Code (IaC) templates. The CLI provisions layers one at a time, ordered by the dependencies between them. You can also provision or tear down individual layers independently.

This feature solves complex dependency scenarios where resources in one layer depend on resources from another layer. Instead of mixing IaC with imperative hook scripts, layered provisioning keeps everything declarative.

Note

Layered provisioning is currently a beta feature. Learn more about the versioning strategy.

When to use layered provisioning

Use layered provisioning when a single azd provision deployment can't handle all of your infrastructure needs in one step. Consider using layered provisioning when:

  • Circular dependencies: Some resources need to reference other resources that must be created first, such as a virtual network that must exist before a private endpoint can be configured.
  • Foundational infrastructure differs from application infrastructure: You manage shared networking, security, or identity resources separately from per-application resources.
  • Independent lifecycle management is needed: You update and tear down different infrastructure components at different times. For example, a networking layer might be long-lived, while an application layer is frequently redeployed.
  • Monorepo projects with distinct infrastructure groups: A single repository contains multiple independent services (such as an Event Hub, a Container App, and a Function App), each with its own infrastructure templates.

Configure layers in azure.yaml

Define layers under the infra section of your azure.yaml file. Each layer requires a name and a path pointing to the directory that contains the IaC templates for that layer.

name: my-app
infra:
  layers:
    - name: networking
      path: ./infra/networking
    - name: application
      path: ./infra/application
services:
  api:
    project: ./src/api
    language: js
    host: containerapp

Important

Layer processing order: azd provision processes layers based on their dependencies. For Bicep layers and custom or extension providers, azd infers dependencies by scanning parameter files (*.bicepparam and *.parameters.json) for environment variable and layer output references, and it orders layers accordingly instead of strictly following the order listed in azure.yaml. Layers that have no inferred or declared dependencies run in the order they're listed. azd down follows the corresponding reverse order, so dependent layers are removed before the layers they depend on. Use the dependsOn property to declare ordering that azd can't infer.

Layer properties

Each layer supports the following properties:

Property Required Description
name Yes A unique name for the layer. Use this name when targeting a specific layer with commands.
path Yes The relative path to the directory containing the IaC templates for this layer.
module No The name of the module within the layer's directory. Defaults to main.
provider No The IaC provider for this layer (bicep or terraform). Inherits from the root infra.provider if you don't specify it.
dependsOn No The names of other layers that this layer depends on. Use this property when ordering matters but azd can't infer the dependency from parameter references.

Important

When you define infra.layers, you can't declare other properties on the infra section (path, module, deploymentStacks) at the root level. You must specify all infrastructure configuration within each layer.

Directory structure

A typical project using layered provisioning might have the following directory structure:

my-app/
├── azure.yaml
├── infra/
│   ├── networking/
│   │   └── main.bicep
│   └── application/
│       └── main.bicep
└── src/
    └── api/
        └── ...

Each layer directory contains its own complete set of IaC templates, just as a standard azd project's infra directory would.

Provision and manage layers

You can provision all layers at once or target a specific layer by name. The following sections describe common commands for provisioning, tearing down, and refreshing layer state.

Provision all layers

Run azd provision without arguments to provision all layers:

azd provision

azd processes each layer one at a time, ensuring that a layer completes before any layer that depends on it begins. For Bicep layers and custom or extension providers, azd infers dependencies from environment variable and layer output references in *.bicepparam and *.parameters.json files, so the execution order can differ from the order listed in azure.yaml. Layers with no inferred or declared dependencies run in the listed order. This process guarantees that dependent resources exist before layers that reference them are deployed.

Provision a specific layer

To provision only a specific layer, pass the layer name as an argument:

azd provision networking

This command deploys only the resources defined in the networking layer. Provisioning a specific layer is useful when:

  • You're iterating on a single layer during development.
  • You need to update one layer without redeploying others.
  • You're setting up a new layer on top of existing infrastructure.

Tear down all layers

Run azd down without arguments to tear down resources from all layers. When multiple layers exist, azd processes them in the reverse of the provisioning order, so dependent resources are removed before the foundational resources they depend on:

azd down

Tear down a specific layer

To tear down only a specific layer, pass the layer name as an argument:

azd down application

This command removes only the resources deployed by the application layer, leaving the other layers intact.

Refresh environment state

You can refresh the environment state from a specific layer by using the --layer flag with azd env refresh:

azd env refresh --layer networking

This command updates the environment variables and outputs based on the most recent deployment of the specified layer.

Example: Monorepo with multiple services

The following example demonstrates layered provisioning for a monorepo that contains an Event Hub, a Container App running multiple containers, and an Azure Function App:

name: logging-app
infra:
  layers:
    - name: eventhub
      path: ./infra/eventhub
    - name: aca
      path: ./infra/aca
    - name: functionapp
      path: ./infra/functionapp
services:
  functionapp:
    resourceName: ${site_name}
    language: dotnet
    project: ./src/function/functionapp.csproj
    host: appservice
    resourceGroup: ${rg_name}

The corresponding directory structure:

logging-app/
├── azure.yaml
├── infra/
│   ├── eventhub/
│   │   └── main.bicep
│   ├── aca/
│   │   └── main.bicep
│   └── functionapp/
│       └── main.bicep
└── src/
    └── function/
        └── functionapp.csproj

With this configuration, you can:

  1. Provision only the Event Hub infrastructure: azd provision eventhub
  2. Provision only the Container App infrastructure: azd provision aca
  3. Provision everything in order: azd provision
  4. Tear down only the Function App layer: azd down functionapp

Example: Base and application layers

A common pattern separates shared or foundational infrastructure from per-application infrastructure:

name: my-app
infra:
  layers:
    - name: base
      path: ./infra/base
    - name: app
      path: ./infra/app
services:
  web:
    project: ./src/web
    language: js
    host: containerapp

The base layer creates shared resources like networking, identity, and monitoring. The app layer creates the application-specific resources (such as a Container App environment and container apps) that reference the base resources.

During development, you might provision the base layer once and iterate on the application layer:

azd provision base
azd provision app
azd provision app  # re-provision only the app layer after changes

Example: Mixed IaC providers

Each layer can use a different IaC provider. For example, you might use Bicep for networking and Terraform for the application layer:

name: my-app
infra:
  layers:
    - name: networking
      path: ./infra/networking
      provider: bicep
    - name: application
      path: ./infra/application
      provider: terraform

The built-in non-Bicep providers (Terraform, Pulumi, ARM, and the test provider) are opaque to dependency inference, because azd can't analyze their inputs for references to other layers. Use an explicit dependsOn property whenever ordering between those layers, or between those layers and other layers, matters:

name: my-app
infra:
  layers:
    - name: networking
      path: ./infra/networking
      provider: bicep
    - name: application
      path: ./infra/application
      provider: terraform
      dependsOn:
        - networking

Considerations and limitations

  • When you provision all layers, azd processes them one at a time based on their dependencies. For Bicep layers and custom or extension providers, azd infers dependencies from environment variable and layer output references in *.bicepparam and *.parameters.json files. Layers without inferred or declared dependencies run in the order you define.
  • The built-in non-Bicep providers (Terraform, Pulumi, ARM, and the test provider) are opaque for dependency inference. Use an explicit dependsOn property when ordering involving those layers matters.
  • Explicit dependsOn is also required when ordering matters but azd can't infer the dependency from parameter references. For example, use dependsOn when one layer's postprovision hook produces a value that another layer consumes, because hook-produced values don't appear as output references in *.bicepparam or *.parameters.json files.
  • When you tear down all layers, azd processes them in the reverse of the provisioning order.
    • If multiple layers deploy resources into the same Azure resource group and you use the default resource-group–based deletion behavior, shared resources may be deleted when running azd down.
    • To allow independent tracking and deletion of layered infrastructure, enable deployment stacks by running the command azd config set alpha.deployment.stacks on.
  • You can't use the --preview flag when provisioning multiple layers at once. Specify a <layer> name to use preview mode.
  • Layers operate independently in terms of IaC. To reference outputs from one layer in another layer, use environment variables that azd sets after each layer's deployment. Referencing those environment variables from a *.bicepparam or *.parameters.json file also lets azd infer the ordering between the layers.
  • All standard azd provisioning features (deployment state caching, hooks, parameters, Bicep, or Terraform) work within each individual layer.
    • Command-level hooks (for example, preprovision, postprovision) are invoked once per layer. When multiple layers are defined, hooks run for each layer in the order layers are processed.

Next steps