Azure Deployment Environments
Azure Deployment Environments (ADE) is the piece of the Azure developer platform that solves the “I need a test environment” problem. Developers self-serve environments from approved templates without filing tickets or waiting for infrastructure teams. This page covers how ADE works, how to set up the catalog, and how to wire it into a pipeline.
How Azure Deployment Environments works
ADE has four main components that work together:
- Dev Center. The top-level Azure resource. Holds catalogs, environment types, and network settings shared across projects.
- Project. A logical group under the Dev Center, typically corresponding to a team or product. Controls which environment types are available and sets spending limits.
- Catalog. A git repository (Azure Repos or GitHub) containing environment definition folders. Each folder is one deployable template.
- Environment type. A named deployment target (Dev, Test, Staging, Prod) with an associated Azure subscription and deployment identity.
When a developer requests an environment, ADE provisions it in the subscription and resource group associated with the environment type. The developer does not need direct Azure access — ADE uses a managed identity to deploy on their behalf.
# Install the ADE CLI extension
az extension add --name devcenter --upgrade
# Create a Dev Center
az devcenter admin devcenter create \
--name "my-devcenter" \
--resource-group "platform-rg" \
--location "eastus" \
--identity-type SystemAssigned
# Create a project under the Dev Center
az devcenter admin project create \
--name "my-project" \
--resource-group "platform-rg" \
--dev-center-id $(az devcenter admin devcenter show \
--name my-devcenter \
--resource-group platform-rg \
--query id -o tsv) \
--max-dev-boxes-per-user 3
Setting up the catalog repository
The catalog is a git repository with a specific folder structure. Each subfolder is an environment definition — a folder name that developers will see in the catalog browser. Inside each folder you need at minimum an environment.yaml manifest and your IaC files.
catalog-repo/
├── environments/
│ ├── web-app-with-db/
│ │ ├── environment.yaml # manifest describing this template
│ │ ├── main.bicep # or main.tf for Terraform
│ │ └── parameters.json
│ ├── api-service/
│ │ ├── environment.yaml
│ │ ├── main.tf
│ │ └── variables.tf
│ └── microservices-sandbox/
│ ├── environment.yaml
│ └── main.bicep
# environments/web-app-with-db/environment.yaml
name: Web App with Database
version: 1.0.0
summary: App Service (B1) + Azure SQL + Storage Account for dev/test use
description: >
Provisions a complete web application stack including an App Service Plan,
App Service, Azure SQL database (Basic tier), and a storage account.
Suitable for feature branch testing and integration test runs.
runner: Bicep
templatePath: main.bicep
parameters:
- id: appName
name: Application Name
description: Short name used as a prefix for all resources
type: string
required: true
- id: location
name: Azure Region
description: Region to deploy into
type: string
default: eastus
allowed:
- eastus
- westus2
- westeurope
// environments/web-app-with-db/main.bicep
@description('Short name used as a prefix for all resources')
param appName string
@description('Azure region')
param location string = resourceGroup().location
@description('Environment type tag')
param environmentType string = 'dev'
var appServicePlanName = '${appName}-plan'
var appServiceName = '${appName}-web'
var sqlServerName = '${appName}-sql-${uniqueString(resourceGroup().id)}'
var storageAccountName = '${replace(appName, '-', '')}${uniqueString(resourceGroup().id)}'
resource appServicePlan 'Microsoft.Web/serverfarms@2023-01-01' = {
name: appServicePlanName
location: location
sku: {
name: 'B1'
tier: 'Basic'
}
properties: {}
}
resource appService 'Microsoft.Web/sites@2023-01-01' = {
name: appServiceName
location: location
properties: {
serverFarmId: appServicePlan.id
httpsOnly: true
}
tags: {
environment: environmentType
managedBy: 'azure-deployment-environments'
}
}
resource sqlServer 'Microsoft.Sql/servers@2023-05-01-preview' = {
name: sqlServerName
location: location
properties: {
administratorLogin: 'sqladmin'
administratorLoginPassword: uniqueString(resourceGroup().id, appName)
}
}
resource sqlDatabase 'Microsoft.Sql/servers/databases@2023-05-01-preview' = {
parent: sqlServer
name: '${appName}-db'
location: location
sku: {
name: 'Basic'
tier: 'Basic'
}
}
output appServiceUrl string = 'https://${appService.properties.defaultHostName}'
output sqlServerFqdn string = sqlServer.properties.fullyQualifiedDomainName
Connecting the catalog to the Dev Center
# Get the Dev Center's managed identity object ID
DEVCENTER_ID=$(az devcenter admin devcenter show \
--name my-devcenter \
--resource-group platform-rg \
--query identity.principalId -o tsv)
# Grant the Dev Center identity read access to the catalog repo
# (for Azure Repos — also need to configure PAT in the catalog connection)
# Add the catalog to the Dev Center
az devcenter admin catalog create \
--name "app-templates" \
--dev-center-name "my-devcenter" \
--resource-group "platform-rg" \
--ado-git \
branch="main" \
path="/environments" \
secret-identifier="https://mykeyvault.vault.azure.net/secrets/ado-pat" \
uri="https://dev.azure.com/contoso/PlatformInfra/_git/catalog-repo"
# Sync the catalog (also happens automatically on git push)
az devcenter admin catalog sync \
--name "app-templates" \
--dev-center-name "my-devcenter" \
--resource-group "platform-rg"
Creating environments from a pipeline
ADE environments can be provisioned programmatically from a CI/CD pipeline. This pattern is useful for creating a fresh environment per pull request, running integration tests against it, then deleting it when the PR closes.
# azure-pipelines.yml — create environment per PR, run tests, then delete
trigger: none
pr:
branches:
include:
- main
variables:
ENVIRONMENT_NAME: 'pr-$(System.PullRequest.PullRequestId)'
PROJECT_NAME: 'my-project'
DEVCENTER_NAME: 'my-devcenter'
pool:
vmImage: 'ubuntu-latest'
stages:
- stage: Provision
displayName: 'Provision PR Environment'
jobs:
- job: CreateEnv
steps:
- task: AzureCLI@2
displayName: 'Create ADE environment'
inputs:
azureSubscription: 'My Azure Subscription'
scriptType: 'bash'
scriptLocation: 'inlineScript'
inlineScript: |
az extension add --name devcenter --upgrade
az devcenter dev environment create \
--dev-center-name "$(DEVCENTER_NAME)" \
--project-name "$(PROJECT_NAME)" \
--name "$(ENVIRONMENT_NAME)" \
--environment-type "dev" \
--catalog-name "app-templates" \
--environment-definition-name "web-app-with-db" \
--parameters '{"appName": "pr$(System.PullRequest.PullRequestId)"}' \
--only-show-errors
# Get the output URL
APP_URL=$(az devcenter dev environment show \
--dev-center-name "$(DEVCENTER_NAME)" \
--project-name "$(PROJECT_NAME)" \
--name "$(ENVIRONMENT_NAME)" \
--query "outputs.appServiceUrl.value" -o tsv)
echo "##vso[task.setvariable variable=APP_URL;isOutput=true]$APP_URL"
name: createEnv
- stage: Test
displayName: 'Run Integration Tests'
dependsOn: Provision
variables:
APP_URL: $[ stageDependencies.Provision.CreateEnv.outputs['createEnv.APP_URL'] ]
jobs:
- job: IntegrationTests
steps:
- script: |
npm ci
APP_URL=$(APP_URL) npm run test:integration
displayName: 'Run integration tests'
- stage: Cleanup
displayName: 'Delete PR Environment'
dependsOn: Test
condition: always() # delete even if tests fail
jobs:
- job: DeleteEnv
steps:
- task: AzureCLI@2
displayName: 'Delete ADE environment'
inputs:
azureSubscription: 'My Azure Subscription'
scriptType: 'bash'
scriptLocation: 'inlineScript'
inlineScript: |
az devcenter dev environment delete \
--dev-center-name "$(DEVCENTER_NAME)" \
--project-name "$(PROJECT_NAME)" \
--name "$(ENVIRONMENT_NAME)" \
--yes
Configure environment expiration on environment types in ADE. Set a maximum lifetime (for example, 7 days) so environments are automatically deleted even if a PR is abandoned or the cleanup stage fails. This prevents sprawl and unnecessary Azure spending.
Common mistakes
- Using production-tier SKUs in catalog templates. Developers create and destroy environments frequently. If the template provisions P3 App Service Plans and Premium SQL databases, costs explode quickly. Use the cheapest tier that supports the testing use case (B1, Basic SQL, LRS storage) and let production infrastructure be managed separately.
- Not granting the Dev Center identity the right roles. ADE uses the Dev Center’s managed identity (or a deployment identity you configure on the environment type) to deploy resources. If that identity lacks Contributor access on the target subscription or resource group, every environment creation will fail with an authorization error.
- Storing secrets in Bicep or ARM parameters files in the catalog repo. Template parameters for things like SQL passwords should use
securestringparameter types (Bicep/ARM) or Key Vault references. Never commit actual secret values to the catalog repository, even if the repository is private.
Summary
- Azure Deployment Environments lets developers provision pre-approved infrastructure templates on demand through a catalog.
- The catalog is a git repository with environment definition folders, each containing an environment.yaml manifest and Bicep or Terraform files.
- Environment types map to Azure subscriptions and control which templates are available and what spending limits apply.
- Pipelines can create and delete ADE environments programmatically — useful for per-PR test environments that are cleaned up after the PR closes.
- Configure expiration policies on environment types to prevent orphaned environments from accumulating costs.
Frequently asked questions
What is Azure Deployment Environments?
Azure Deployment Environments (ADE) is a service that lets developers provision their own infrastructure environments on demand using pre-approved templates. Instead of waiting for an ops team to set up a test environment, a developer clicks a button and gets a full environment — App Service, database, storage — provisioned from a template in minutes.
What templates does Azure Deployment Environments support?
ADE supports ARM templates, Bicep files, Terraform configurations, and Pulumi. Templates are stored in a catalog repository (typically an Azure DevOps repo or GitHub repo). Administrators define which templates are available to which projects.
How is Azure Deployment Environments different from Azure DevOps Environments?
These are two different things with similar names. Azure DevOps Environments are a CI/CD concept — they track deployment history and host approval gates for pipelines. Azure Deployment Environments is a separate Azure service for provisioning infrastructure on demand. Both can be used together in a developer platform workflow.