
Geschreven door Funs Janssen
Software Consultant
Ik ben Funs Janssen. Ik ontwikkel software en schrijf over de beslissingen die daarbij komen kijken: architectuur, ontwikkelmethoden, AI-tools en de zakelijke impact van technische keuzes. Deze blog is een verzameling praktische aantekeningen van echte projecten: wat schaalbaar is, wat misgaat en wat in blogvriendelijke voorbeelden vaak over het hoofd wordt gezien.
On October 8, 2026, Microsoft announced that Azure Deployment Environments (ADE) will retire on February 22, 2027. After that date you can no longer create, deploy or redeploy environments. If your developers use the Dev portal to spin up a sandbox or a feature environment without filing a ticket, that workflow stops working in a little over four months.
The official retirement guide tells you what is going away and lists a few directions: Bicep or ARM, Azure Verified Modules, or CI/CD pipelines. What it doesn't give you is a worked design. In this post I'll fill that gap for teams that already run on Azure DevOps. You'll get a concept-by-concept mapping from ADE to Azure Pipelines, a single pipeline that gives developers self-service environments with an expiry date, a teardown job that replaces ADE's built-in expiration, and a migration timeline that finishes well before February.
What retires on February 22, 2027 (and what keeps working)
Read the Azure Deployment Environments retirement guide on Microsoft Learn first. In short, these are the parts that affect your planning.
Blocked: create, deploy, redeploy
From February 22, 2027, write operations on ADE environments are expected to be blocked. A developer can't create a new environment from a definition, and an existing environment can't be redeployed with a new template version. If a team relies on "redeploy" to refresh a long-lived test environment, that environment is effectively frozen on that date.
Kept for a while: read, logs, delete
Inventory, read, log and delete operations are planned to stay available for a time-bound cleanup period. That's helpful, but don't treat it as extra migration time. It exists so you can tidy up, not so you can keep running.
Dev Box is not affected (until 2028)
ADE and Microsoft Dev Box share the Dev Center resource. Dev Box keeps working until its own retirement on September 18, 2028. If your organization uses Dev Box, don't delete the dev center, projects or network connections as part of the ADE cleanup. Remove only what belongs to ADE.
Inventory: what you actually use in ADE
Most teams use a fraction of what ADE offers. Before you design anything, write down what you depend on. I create one Azure Boards work item per item below, which comes in handy later for tracking.
Environment types, catalogs and definitions
- Environment types (for example Dev, Test, Sandbox): each maps to a target subscription, a deployment identity and role assignments for the people who create environments.
- Catalogs: the Git repositories (GitHub or Azure Repos) that hold your environment definitions.
- Environment definitions: an
environment.yamlplus a Bicep, ARM or Terraform template, with the parameters developers fill in.
Export every definition and its parameter defaults into a Git repository you control. If your catalog already lives in Azure Repos, this is mostly a matter of confirming which branch is current.
Identities, role assignments and azd configs
List the managed identities each environment type uses to deploy, and the roles they hold on the target subscriptions. Also search your application repos for azd configuration that targets the Dev Center platform, because the retirement guide points out these integrations need to be rebuilt.
Live environments and who owns them
Export the list of running environments with their owner, environment type, resource group and expiration date. This list drives your cutover. Every environment on it needs one of three outcomes: recreate in the new setup, keep as a plain resource group, or delete.
Mapping ADE concepts to Azure DevOps
This is the table I wish the retirement guide included. Every ADE concept has a reasonable equivalent in Azure DevOps and Azure Resource Manager.
- Dev center and project become an Azure DevOps project that owns the environment pipelines and template repo.
- Catalog becomes a Git repo in Azure Repos with one folder per template.
- Environment definition becomes a Bicep template plus a parameters file in that repo.
- Environment type becomes a combination of a service connection (target subscription and identity), a pipeline environment (approvals and checks) and a variables file (location, naming prefix, default expiry).
- Deployment identity becomes a service connection using workload identity federation. If you haven't moved your service connections yet, my Azure DevOps issuer migration runbook covers that.
- Developer self-service in the Dev portal becomes a pipeline run with runtime parameters.
- Expiration date becomes resource group tags plus a scheduled teardown pipeline.
The key difference is that ADE gave you all this as a managed service. In Azure DevOps you assemble it from building blocks you already know. That's more work up front, but you own every piece and nothing retires underneath you again.
One pipeline for self-service environments
The goal is that a developer can still say "give me a Test environment with the API template for five days" without filing a ticket. A single parameterized pipeline does that.
Runtime parameters as the portal
Runtime parameters render as a form when someone clicks Run pipeline. With values lists, the developer can only pick what you allow.
1trigger: none23parameters:4 - name: template5 displayName: Environment template6 type: string7 values: [api, web-app, data-sandbox]8 - name: environmentType9 displayName: Environment type10 type: string11 values: [dev, test]12 - name: name13 displayName: Short name (letters and digits)14 type: string15 - name: expiresInDays16 displayName: Expires in (days)17 type: number18 default: 519 values: [1, 3, 5, 10]2021variables:22 - template: env-types/${{ parameters.environmentType }}.yml2324stages:25 - stage: Provision26 jobs:27 - deployment: Create28 environment: env-${{ parameters.environmentType }}29 pool:30 vmImage: ubuntu-latest31 strategy:32 runOnce:33 deploy:34 steps:35 - checkout: self36 - task: AzureCLI@237 inputs:38 azureSubscription: $(serviceConnection)39 scriptType: bash40 scriptLocation: inlineScript41 inlineScript: |42 rg="rg-${{ parameters.environmentType }}-${{ parameters.name }}"43 expires=$(date -u -d "+${{ parameters.expiresInDays }} days" +%Y-%m-%dT%H:%M:%SZ)44 az group create --name "$rg" --location "$(location)" \45 --tags owner="$(Build.RequestedForEmail)" \46 expiresOn="$expires" \47 template="${{ parameters.template }}" \48 createdBy="$(Build.BuildUri)"49 az deployment group create --resource-group "$rg" \50 --template-file "templates/${{ parameters.template }}/main.bicep" \51 --parameters "templates/${{ parameters.template }}/${{ parameters.environmentType }}.bicepparam"
Each env-types/<type>.yml file holds the values that an ADE environment type used to hold: serviceConnection, location and anything else that differs per type. Because the variables template is resolved at compile time, the service connection is fixed before the run starts. Developers can't point it at a subscription you didn't intend. The deployment step itself is plain Bicep, exactly as described in Deploy Bicep files with Azure CLI.
Approvals and checks per environment type
The deployment job targets a pipeline environment (env-dev, env-test). That's where governance lives. With approvals and checks you can let anyone create a Dev environment instantly, while Test environments need an approval from a lead or must run within business hours. This replaces the per-type permissions you had in ADE, and the history lands in the pipeline environment's deployment log.
Restrict which pipelines may use each service connection as well. Otherwise someone could reference the Test connection from an unrelated pipeline and skip your approvals.
Tag everything
Tags are what make the rest of this design work. Every resource group gets at least:
owner: who requested it, so you know who to ask before deleting.expiresOn: an ISO 8601 UTC timestamp that the teardown job reads.templateandcreatedBy: which definition built it and which pipeline run did it, for traceability.
Add an Azure Policy that requires owner and expiresOn on resource groups in your sandbox subscriptions. That way environments created by hand outside the pipeline can't slip past the teardown.
Auto-expiry: the teardown job ADE gave you for free
ADE could delete an environment when its expiration date passed. Without that, sandbox subscriptions slowly fill with forgotten resource groups. One scheduled pipeline fixes it.
A scheduled pipeline that finds expired resource groups
Scheduled triggers run the teardown every night, whether or not anything changed in the repo.
1trigger: none23schedules:4 - cron: "0 2 * * *"5 displayName: Nightly environment teardown6 branches:7 include: [main]8 always: true910pool:11 vmImage: ubuntu-latest1213steps:14 - task: AzureCLI@215 inputs:16 azureSubscription: sandbox-cleanup17 scriptType: bash18 scriptLocation: inlineScript19 inlineScript: |20 now=$(date -u +%Y-%m-%dT%H:%M:%SZ)21 for rg in $(az group list \22 --query "[?tags.expiresOn && tags.expiresOn < '$now'].name" -o tsv); do23 echo "Deleting expired resource group $rg"24 az group delete --name "$rg" --yes --no-wait25 done
Because the timestamps are ISO 8601 in UTC, comparing them as strings in the JMESPath query is safe. Give the sandbox-cleanup service connection only the rights it needs on the sandbox subscriptions, and never point it at production.
Soft warning first, delete second
Deleting someone's environment without warning is the fastest way to make people hate the new setup. I run a second query that finds groups expiring within 24 hours and posts the owners to a Teams channel or creates a task for them. To keep an environment, they rerun the pipeline with a new expiry, or a lead updates the tag. It's a small addition that makes the teardown feel like a feature instead of a trap.
Cleanup traps when you switch off ADE
Resource groups that outlive their environment
The retirement guide warns that deleting an ADE environment might not delete every resource it deployed. After each delete, check the deployment resource group and look at Azure Cost Management for a few days to confirm billing has actually stopped. Pay particular attention to resources in other resource groups, such as DNS records, role assignments or shared Key Vault secrets.
Shared Dev Center resources and leftover role assignments
Once nothing depends on them anymore, remove the ADE-only parts: environment types, catalogs, definitions, the deployment identities and their role assignments on target subscriptions. Leave anything Dev Box uses in place. Leftover identities with Contributor rights on a subscription are a security finding waiting to happen, so don't skip this step just because nothing is visibly broken.
A migration timeline that ends well before February
Four months is enough if you start now. This is the order I'd use:
- October: inventory and template repo. Export definitions, set up the repo, the service connections and the two pipelines.
- November: pilot with one team. Recreate their environments with the pipeline and run the teardown in report-only mode first.
- December: parallel run. All teams create new environments through the pipeline. ADE stays available for existing ones.
- January: freeze and cutover. Stop creating environments in ADE, recreate or convert the remaining live environments, and turn on real deletes.
- Early February: clean up ADE. Remove ADE-only resources and identities, and keep Dev Box intact.
Track each step and each environment type as a work item on your board. A migration like this usually fails on one forgotten detail, not on the big pieces. A work item gets closed while the old identity still holds Contributor rights, or nobody checked billing after the delete. That's where the Checklist Extension for Azure DevOps helps. Attach a "migrated" checklist template to each item (definition exported, pipeline run verified, old environment deleted, billing checked, identity removed) and block the transition to Done until every item is ticked. If you're running the new pipelines on hosted agents, my comparison of pay-as-you-go agents versus parallel jobs helps you estimate what all those short provisioning runs will cost.
Conclusion
ADE's retirement isn't a reason to give up on self-service environments. The three things that made it useful (a governed catalog, a self-service form and automatic expiry) map cleanly onto Azure DevOps building blocks you already run: a template repo, a parameterized pipeline with approvals per environment type, and a nightly teardown driven by tags. If you also want short-lived environments per pull request, the same templates plug straight into the pattern from my post on per pull request environments.
Start the inventory this month, pilot in November, and you'll be done long before February 22. To make sure no environment or identity gets "done" with steps still open, install the Checklist Extension and give each migration item a blocking checklist. If you'd rather have a real "request environment" hub inside Azure DevOps instead of the pipeline run form, that's exactly the kind of thing I build as a custom Azure DevOps extension.
Frequently asked questions
Reacties
Nog geen reacties. Wees de eerste om te reageren.
Plaats een reactie

Geschreven door Funs Janssen
Software Consultant
Ik ben Funs Janssen. Ik ontwikkel software en schrijf over de beslissingen die daarbij komen kijken: architectuur, ontwikkelmethoden, AI-tools en de zakelijke impact van technische keuzes. Deze blog is een verzameling praktische aantekeningen van echte projecten: wat schaalbaar is, wat misgaat en wat in blogvriendelijke voorbeelden vaak over het hoofd wordt gezien.
Inhoud

.png%3F2025-08-20T13%3A39%3A31.637Z&w=3840&q=80)
Learn how per pull request environments boost code quality, reduce release risks, and speed up feedback in modern CI/CD pipelines.

