Table of Contents
I recently wrote about the importance of protecting your Terraform state files and the Azure Storage accounts that hold them. One of the recurring challenges in real-world environments is striking the right balance between security and operability — specifically, how do we fully lock down a storage account to prevent public access, while still allowing Azure DevOps pipeline runners to reach it?
This challenge doesn’t stop at state files. The same issue appears whenever Terraform needs to perform data-plane operations against resources such as Storage Accounts or Azure SQL when they are secured behind private endpoints. Until recently, the only reliable solution was to use self-hosted Azure DevOps runners deployed inside an Azure Virtual Network with private access to those resources.
While effective, self-hosted runners come with their own operational overhead and complexity. Many teams attempted to avoid this by using workarounds — such as dynamically adding the public IP address of Microsoft-hosted runners to storage account or SQL firewalls during pipeline execution. These approaches are fragile, difficult to manage at scale, and often plagued by timing and reliability issues.
The good news is that Microsoft has clearly felt this pain too. With the introduction of Managed DevOps Pools, we finally have a native, supported way to run Azure DevOps pipelines inside a controlled network boundary — without the operational burden of managing self-hosted runners.
In this article, I’ll walk through what a Managed DevOps Pool is, how to provision one, and how it enables you to fully lock down your Azure resources behind private endpoints while still using Azure DevOps pipelines securely and reliably.
What is a Managed DevOps Pool
Managed DevOps Pools are the natural evolution of the classic Azure DevOps Virtual Machine Scale Set agents that many of us have historically deployed and managed ourselves in Azure. They provide a modern, managed way to run pipeline agents that combines the flexibility of self-hosted runners with the security and operational simplicity of Microsoft-hosted agents.
At their core, Managed DevOps Pools allow teams to customise the runner image to suit their tooling and build requirements, while retaining the reliability, scaling, and lifecycle management of Azure DevOps–managed infrastructure. Crucially, these runners can be integrated directly into an existing Azure Virtual Network, enabling private, network-level access to resources secured behind private endpoints — without exposing anything to the public internet.
Although the runners are network-integrated with your environment, they do not run inside your Azure subscriptions. Instead, they are deployed into Microsoft-managed subscriptions, with Azure DevOps handling provisioning, scaling, patching, and retirement. From a cost perspective, the pricing model mirrors that of self-hosted scale set agents, so there are no unexpected surprises when adopting the service.
There are, however, some important differences compared to traditional scale set agents. You can no longer rely on VM Scale Set extensions to run post-deployment scripts on the agents. Instead, customisation is achieved by building and maintaining a custom runner image with the required tools and dependencies pre-installed — a cleaner and more predictable approach once established.
Another significant advantage is reusability across Azure DevOps organisations. A single Managed DevOps Pool can be securely shared and linked to multiple DevOps organisations, whereas scale set agents required a separate deployment and configuration for each organisation.
Managed DevOps Pools are clearly positioned as the future replacement for VM Scale Set–based agents. While the feature set is still evolving — for example, direct access to the underlying machines is not yet available — it’s clear that ongoing development and innovation will focus on managed pools rather than legacy agent models.
A useful way to think about Managed DevOps Pools is as the Platform-as-a-Service version of Azure DevOps pipeline agents. Microsoft manages the runners, scaling, and network integration, while you focus purely on pipeline logic, images you want security boundaries, and the tooling your builds require.
Now that we understand what Managed DevOps Pools are and why they matter, let’s walk through deployment.
Deploying a Managed DevOps Pool
Managed DevOps Pools can be deployed in several ways, including the Azure Portal, Azure CLI, ARM/Bicep templates, and Terraform. In this article, I’ll walk through the Portal-based deployment step by step, and later provide Terraform examples showing how the same resources can be provisioned as code.
Prerequisites
Quick Checklist
✅ RBAC Roles Assigned
✅ Resource Providers Registered
✅ Quotas Validated
✅ Subnet Delegated
RBAC
Before deploying a Managed DevOps Pool, ensure that the identity you are using has the correct permissions. The least-privileged role required to deploy Managed DevOps infrastructure is:
• DevOps Infrastructure Contributor
In addition, the built-in DevOpsInfrastructure service principal must be granted access to the Virtual Network that will host the pool’s runners. This service principal is created and managed by Microsoft — you do not need to create it yourself.
Assign the following roles on the target Virtual Network:
• Reader
• Network Contributor

Resource Provider Registration
You must register the required resource providers in any subscription where you plan to deploy Managed DevOps Pools. This can be done via the Azure Portal, CLI, PowerShell, or Infrastructure as Code.
The required providers are:
• Microsoft.DevOpsInfrastructure
• Microsoft.DevCenter
To register them using Azure CLI:
az provider register --namespace 'Microsoft.DevOpsInfrastructure'
az provider register --namespace 'Microsoft.DevCenter'
Or using PowerShell
Register-AzResourceProvider -ProviderNamespace 'Microsoft.DevOpsInfrastructure'
Register-AzResourceProvider -ProviderNamespace 'Microsoft.DevCenter'
The Terraform examples later in this article also demonstrate how to register these providers programmatically.
Quotas
You must ensure that you have sufficient quota capacity for the VM SKU you plan to use for your DevOps runners. This quota is separate from the standard VM quotas within your Azure subscription.
By default, subscriptions are allocated up to 5 vCPUs for supported Managed DevOps Pool VM SKUs. In practice, this limits you to a maximum of two concurrent runners, depending on the SKU selected. For instance, if you select a Standard_D2s_v3 SKU (2 vCPUs), the default quota allows up to 2 concurrent agents. A Standard_D4s_v3 (4 vCPUs) would only allow 1 agent by default
If you require additional capacity, you’ll need to request a quota increase through the Azure Portal. When submitting the request, make sure you select Azure DevOps Managed Pool as the quota type — requesting a standard VM quota increase will not affect Managed DevOps Pools.

Azure DevCenter
Next, we need to deploy an Azure Dev Center resource. Dev Center is responsible for managing and orchestrating Managed DevOps Pools, and it also provides additional capabilities that I’ll cover in a future blog series.
From the Azure Portal, search for Dev Centers and select Create.

Choose the subscription and resource group, provide a meaningful name, and select the appropriate region.

On the Settings page, disable Microsoft Managed Networks. This ensures that your DevOps runners are injected into your Virtual Networks, allowing private access to resources secured behind private endpoints.

Azure DevCenter Project
Within Dev Center, create a Project. This project will hold the configuration for your Managed DevOps Pool.
Create a new project, give it a name and optional description, and apply any required tags. You do not need to configure Dev Box management options or catalogs for this scenario.
Subnet Delegation
Managed DevOps Pools require a dedicated subnet. This subnet:
- Must be used exclusively by the Managed DevOps Pool
- Cannot host any other Azure resources
- Must have sufficient IP address capacity to support the maximum number of concurrent pipeline agents
Select a Virtual Network that has connectivity to the resources your pipelines need to access. This can be the same VNET or a peered VNET, provided routing and DNS are correctly configured.

Delegate the subnet to the following service:
- Microsoft.DevOpsInfrastructure/pools
⚠️ Important: Validate Quotas and RBAC Before Deployment
Before creating a Managed DevOps Pool, double-check that subscription quotas and RBAC permissions are correctly configured.
The deployment experience currently provides no meaningful validation or warnings if these prerequisites are missing. If quotas are insufficient or required permissions have not been granted, the pool creation will simply fail silently or error late in the deployment, making troubleshooting unnecessarily difficult.
At a minimum, ensure:
- Your subscription has sufficient vCPU quota for the selected agent SKU and maximum runner count
- The deploying identity has the DevOps Infrastructure Contributor role
- The DevOpsInfrastructure service principal has Reader and Network Contributor access to the target Virtual Network and delegated subnet
- The required resource providers (Microsoft.DevOpsInfrastructure and Microsoft.DevCenter) are registered
Validating these upfront will save time and avoid failed deployments that offer little diagnostic feedback.
Create the Managed DevOps Pool
With the prerequisites in place, we can now create the Managed DevOps Pool.
From your Dev Center Project, navigate to Manage → Managed DevOps Pools, and select Create.

Basics
On the Basics tab:
- Select the Azure DevOps organisation where the pool will be created
- By default, all projects in the organisation can use the pool (this can be restricted later in the Security tab)
- Configure the maximum number of agents
- Select the agent SKU size (ensure your subscription quotas allow for this)
You can also select one or more agent images, including:
- Built-in Microsoft images
- Azure DevOps pipeline images
- Marketplace images
- Custom images from your Azure Compute Gallery

Scaling
On the Scaling tab, configure whether you want a standby agent available.
You can choose between manual or automatic scaling. In this example, I’ve selected automatic scaling with a bias towards cost efficiency rather than peak performance — choose the option that best fits your workload.

Networking
Select Agents injected into existing virtual network, then choose the Virtual Network and delegated subnet created earlier.
This enables your pipeline agents to communicate privately with resources secured behind private endpoints.

Security
On the Security tab, you can control how the Managed DevOps Pool is used:
- Link the pool to multiple Azure DevOps organisations
- Restrict usage to specific projects
- Configure Pool Administrators

Once complete, select Review + Create to deploy the Managed DevOps Pool.
Using the Pool
Once created, the Managed DevOps Pool will be visible within your Azure DevOps organisation under Pipeline settings.
From the Azure Portal, via Dev Center, you can view and manage the pool configuration, including scaling behaviour, VM SKU, image selection, and virtual network integration. Although the agents are injected into your virtual network, you will not see any virtual machines running within your own Azure subscriptions, as they are hosted in Microsoft-managed subscriptions.
To use the pool in a pipeline, you’ll need to update your YAML definition.
If your pipeline is currently using a Microsoft-hosted agent, it may look something like this:
pool:
vmImage: ubuntu-latest
To use your Managed DevOps Pool, update the pool definition to reference the pool name instead:
pool:
name: my-custom-pool
When Not to Use Managed DevOps Pools
While Managed DevOps Pools solve many long-standing challenges around security, networking, and operational overhead, there are still scenarios where they may not be the right fit.
You Require Full Control of the Underlying Machines
If your pipelines rely on direct access to the agent virtual machines — for example, for live troubleshooting, custom diagnostics, or deep OS-level configuration — Managed DevOps Pools may feel restrictive. At present, you cannot log onto or directly inspect the underlying machines, as they are hosted in Microsoft-managed subscriptions.
In these cases, traditional self-hosted runners may still be more appropriate.
⸻
You Depend on Dynamic Post-Provisioning Scripts
Managed DevOps Pools do not support VM Scale Set extensions or ad-hoc post-deployment scripting. If your current setup relies on dynamically installing tools or modifying the agent at runtime outside of the pipeline itself, you’ll need to rethink this approach.
Managed Pools work best when you:
• Pre-build tooling into custom images, or
• Install dependencies as part of the pipeline execution
If neither of these approaches fits your operating model, self-hosted agents may still be required.
⸻
You Have Highly Specialized or Unsupported Workloads
If your pipelines require:
• Niche or legacy tooling
• Unsupported operating systems
• Kernel-level drivers or hardware-specific dependencies
…then Managed DevOps Pools may not support your requirements today. These scenarios often need tightly controlled, bespoke environments.
⸻
You Need Absolute Cost Control at Very Small Scale
For very small teams or low-frequency pipeline usage, Managed DevOps Pools may not provide immediate cost advantages over Microsoft-hosted agents. If you don’t need private network access and are happy with public endpoints, Microsoft-hosted runners may remain the simplest and most cost-effective option.
⸻
You Operate in Highly Regulated or Air-Gapped Environments
In environments with strict regulatory or compliance requirements — particularly where air-gapped or fully isolated infrastructure is mandated — the lack of direct visibility and control over the underlying infrastructure may be a blocker.
Until more governance and audit capabilities are exposed, self-hosted runners can offer a clearer compliance story in these scenarios.
The Terraform Code
We can also use Terraform to deploy the end to end solution, including DevCenter, the Managed DevOps Pool and the RBAC controls. I’ve created a terraform module you can download and re-use as required.
I’ve uploaded the code to GitHub – https://github.com/deniscooper/managed_devops_pool.git
Here is an example of how to call the module.
module "devops" {
source = "git::ssh://git@ssh.dev.azure.com/v3/myorg/tfmodules/tf_module_ado_managed_pool"
providers = {
azuredevops = azuredevops
}
resource_group_name = "my-resource-group"
location = "uscentral"
tags = var.tags
#Devcenter parameters
create_devcenter = true
devcenter_name = "devcenter"
#Project and pool parameters
project_name = "projectname"
pool_name = "poolname"
azure_devops_organization_name = "my-ado-org"
vnet_id = data.azurerm_virtual_network.vnet.id #id of the vnet to use
subnet_id = data.azurerm_subnet.subnet.id #is of the subnet
}
data "azurerm_virtual_network" "vnet" {
name = "vnet-name"
resource_group_name = "vnet-rg-name"
}
data "azurerm_subnet" "subnet" {
name = "subnet-name"
virtual_network_name = data.azurerm_virtual_network.vnet.name
resource_group_name = "vnet-rg-name"
}
Summary
We’ve now built a reliable, enterprise-ready DevOps runner platform that can securely access Azure resources at the data plane without exposing them publicly — and without relying on fragile or timing-sensitive workarounds.
Managed DevOps Pools allow us to fully lock down resources behind private endpoints while still using Azure DevOps pipelines, removing the operational burden of self-hosted runners and eliminating the need for “hacky” firewall exceptions. The result is a cleaner, more secure, and far more maintainable DevOps architecture.