„Managed DevOps Pools for Azure DevOps“ und wie diese unser Leben erleichtern

In diesem Beitrag stellen wir Azure DevOps Managed DevOps Pools vor und erklären, wie sie uns das Leben erleichtern. Wir vergleichen Managed DevOps Pools mit Self-Hosted Agents und zeigen, wie man diese Funktionalität vollständig mit Terraform automatisiert. Schließlich besprechen wir, wie man sie in der eigenen CI/CD-Pipeline verwendet.

Was versteht man unter Managed DevOps Pools for Azure DevOps?

Azure DevOps Agent-Pools sind eine Sammlung von Build- und Release-Agents, die verwendet werden, um CI/CD (Continuous Integration/Continuous Delivery)-Pipelines auf spezifischen Maschinen, virtuellen Maschinen oder Containern auszuführen. Die zugrunde liegende Infrastruktur für die Agent-Pools wird durch den neuen Service „Managed DevOps Pools for Azure DevOps“ von Microsoft bereitgestellt und verwaltet. Dieser Service automatisiert und vereinfacht nahezu alle Aufgaben rund um das Management der Agent-Pools und bietet den Endanwendern die größtmögliche Flexibilität in der Anpassung an die eigene Umgebung und an die Anforderungen.

Die Microsoft-gehostete Agents bieten keine Netzwerk-Integration, sodass man entweder auf das Hosting durch Microsoft verzichten oder den komplexen und wartungsintensiven Weg über Virtual Machine Scale Sets gehen musste um self-hosted Agents mit dem eigenen Netzwerk zu integrieren. Managed Pools for Azure DevOps löst diese Diskrepanz und vereint nun beides – Hosting und Management durch Microsoft sowie unzählige Anpassungsmöglichkeiten, inklusive der Anbindung an das eigene Netzwerk.

Wie funktioniert Managed DevOps Pools for Azure DevOps?

hintergrund weiss

Auf der obigen Grafik sieht man ein vereinfachtes Setup eines Managed DevOps Pools im eigenen Netzwerk. Die Pipelines aus der Azure DevOps Organisation greifen auf den Managed DevOps Pool zu. Dieser läuft in der eigenen Subscription im privaten Netzwerk. Die eigentlichen Agents sind hierbei durch Microsoft abstrahiert.

Um die Kommunikation zu ermöglichen, bedarf es eines dedizierten Service Principals namens „DevOpsInfrastructure“ (Dieser wird beim Registrieren des Resource Providers Microsoft.DevOpsInfrastructure automatisch angelegt). Der Service Principal „DevOpsInfrastructure“ benötigt auf das virtuelle Netzwerk die Berechtigungen „Network Contributor“ sowie „Reader“. Weiterhin ist es möglich die notwendigen Rechte durch die Definition einer benutzerdefinierte Rolle zu reduzieren.

Was sind die Unterschiede zu den Self-Hosted Agents und zu den VMSS Agents?

Die Unterschiede zwischen den drei Optionen lassen sich in folgender Gegenüberstellung besonders gut erkennen:

  Self-hosted Agents VMSS Agents MDP Agents
Infrastruktur Eigene Verantwortung Eigene Verantwortung Verwaltung durch Microsoft
Kontrolle Volle Kontrolle Volle Kontrolle Kein Zugriff auf die zugrundeliegende VMs
Skalierung Keine Skalierung Azure Pipelines übernehmen die Skalierung Vollautomatisierte Skalierung, sehr großer Pool an Agents
Kosten Volle (Fix)Kosten für die Infrastruktur Kosten für Compute und Parallel Jobs Kosten für Compute und Parallel Jobs
Wartung Eigene Verantwortung Eigene Verantwortung Wartung durch Microsoft

Welche Vorteile sehen wir im Einsatz von Azure Managed Pools?

Um zu erklären, warum wir Azure Managed Pools zum neuen Standard in unserem Projektgeschäft definiert haben, müssen wir etwas ausholen. Jedem Unternehmen ist es wichtig, dass auf seine privaten Ressourcen sicher zugegriffen wird. Den Entwicklerteams ist es wichtig, dass die Jobs und die Pipelines parallel und schnell laufen. Den Administratoren ist es wichtig, dass der Wartungsaufwand minimal bleibt. Den Platform Engineers ist es wichtig, dass das Setup rund um die Pipelines weitestgehend automatisiert abläuft.

Als wir die Ankündigung von Azure DevOps Managed Pools im Sommer 2024 gesehen und im späten Herbst über die allgemeine Freigabe gelesen haben, mussten wir es sofort ausprobieren – unsere Erwartungen wurden mehr als erfüllt. Wir sind nun in der Lage, vollautomatisiert via Terraform unseren Kunden Agents anzubieten, die mit wenig Aufwand sicher im Kundennetz laufen, nach Belieben skalieren können, komplett wartungsfrei sind.

Wie automatisiert man das Setup via Terraform am Beispiel von Managed Pool im eigenen Netzwerk?

Grundlegende Voraussetzungen sind eine Azure DevOps Organisation, ein Azure DevOps Projekt, eine administrativen Zugriff auf das Projekt und die Anbindung der Organisation an den Entra ID Dienst.

Zum Ausführen der Pipeline und zum Erstellen der Infrastruktur wird ein Service Principal verwendet. Der Service Principal sowie alles Notwendige für den Terraform State können mithilfe unseres Open-Source Projekt „Terraform scaffolding for Azure“angelegt werden (Weitere Informationen gibt es im zugehörigen Blogpost). Unser Terraform Scaffolding ist nach dem Prinzip der minimalen Berechtigungen (least privilege) aufgesetzt. Die meisten Anwendungsfälle sind mit den im Skript verwendeten minimalen Berechtigungen abgedeckt. Im Fall von Managed Pools muss im Entra ID dem Service Principal das Recht „Application.ReadAll“ zugewiesen werden.

Sobald die Voraussetzungen erfüllt sind (DevOps Organisation, Projekt, Service Principal, Terraform State und ein Terraform-Projekt, z.B. in VS Code), muss man als Erstes die notwendigen Resource Providers: „Microsoft.DevOpsInfrastructure“ und „Microsoft.DevCenter“ definieren.

terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "4.17.0"
    }
    azapi = {
      source  = "Azure/azapi"
      version = "2.2.0"
    }
    azurecaf = {
      source  = "aztfmod/azurecaf"
      version = "1.2.28"
    }
    azuread = {
      source  = "hashicorp/azuread"
      version = "3.1.0"
    }
  }

  backend "azurerm" {
  }
  required_version = "1.10.5"
}
provider "azuread" {
}

provider "azurerm" {
  features {}
  subscription_id                 = "XYZ"
  resource_provider_registrations = "core"
  resource_providers_to_register = [
    "Microsoft.DevOpsInfrastructure",
    "Microsoft.DevCenter"
  ]
}

provider "azapi" {
}

Beim Registrieren von Microsoft.DevOpsInfrastructure wird in EntraID eine Enterprise Application mit dem Namen „DevOpsInfrastructure” angelegt.

Da unser Managed Pool in unserem privaten Azure Netzwerk laufen sollen, müssen wir dem „DevOpsInfrastructure“ Service Principal entsprechende Rechte zuweisen. Im ersten Schritt lesen wir den Service Principal ein:

data "azuread_service_principal" "managed_pool_sp" {
  display_name = "DevOpsInfrastructure"
}
data "azurerm_client_config" "current" {}

Dann legen wir vorbereitend eine variables.tf an in der wir die notwendigen Variablen definieren:

variable "project" {
  type    = string
  default = "poc"
}
variable "location" {
  type    = string
  default = "swedencentral"
}
variable "stage" {
  type    = string
  default = "dev"
}
variable "vnet_address_space" {
  type    = list(string)
  default = ["10.0.0.0/16"]
}
variable "subnet_address_space_runner" {
  type    = list(string)
  default = ["10.0.0.0/24"]
}
variable "azure_devops_organization" {
  type    = string
  default = "your-devops-org-name"
}
variable "azure_devops_project" {
  type    = string
  default = "your-devops-project-name"
}
variable "parallelism" {
  type    = number
  default = 2
}
variable "maximumConcurrency" {
  type    = number
  default = 2
}
variable "maxAgentLifetime" {
  type    = string
  default = "7.00:00:00"
}
variable "gracePeriodTimeSpan" {
  type    = string
  default = "00:00:00"
}
variable "runner_sku" {
  type    = string
  default = "Standard_B2ms"
}
variable "runner_state" {
  type    = string
  default = "Stateful"
}

Nun erstellen wir neben der Resource Group und dem VNet ein eigenes Subnet mit Service Delegation für die Agents des Managed Pools. Dieses Subnet wird ausschließlich vom Managed Pool for Azure DevOps verwendet (für alle unterstützten Ressourcen verwenden wir das Microsoft Cloud Adoption Framework (CAF), um die konsistente und klare Benennung der Azure-Ressourcen zu gewährleisten):

resource "azurecaf_name" "rg_core" {
  name          = var.project
  resource_type = "azurerm_resource_group"
  suffixes      = [var.stage, "core"]
}

resource "azurecaf_name" "vnet" {
  name          = var.project
  resource_type = "azurerm_virtual_network"
  suffixes      = [var.stage, "core"]
}

resource "azurecaf_name" "subnet" {
  name          = var.project
  resource_type = "azurerm_subnet"
  suffixes      = [var.stage]
}

locals {
  tags = {
    environment = var.stage
    location    = var.location
    managedBy   = data.azurerm_client_config.current.client_id
  }
  devOpsInfrastructureUser_object_id = data.azuread_service_principal.managed_pool_sp.object_id
}

resource "azurerm_resource_group" "core" {
  name     = azurecaf_name.rg_core.result
  location = var.location
  tags     = local.tags
}

resource "azurerm_virtual_network" "core" {
  name                = azurecaf_name.vnet.result
  address_space       = var.vnet_address_space
  location            = azurerm_resource_group.core.location
  resource_group_name = azurerm_resource_group.core.name
  tags                = local.tags
}

resource "azurerm_subnet" "runner" {
  name                 = "${azurecaf_name.subnet.result}-runner"
  resource_group_name  = azurerm_resource_group.core.name
  virtual_network_name = azurerm_virtual_network.core.name
  address_prefixes     = var.subnet_address_space_runner
 
  delegation {
    name = "Microsoft.DevOpsInfrastructure/pools"

    service_delegation {
      actions = [
        "Microsoft.Network/virtualNetworks/subnets/join/action",
      ]
      name = "Microsoft.DevOpsInfrastructure/pools"
    }
  }
}

In diesem Schritt berechtigen wir den Service Principal “DevOpsInfrastructure“ auf unserem privaten Azure Netzwerk:

resource "azurerm_role_assignment" "vnet_network_contributor_doi_user" {
  scope                = azurerm_virtual_network.core.id
  role_definition_name = "Network Contributor"
  principal_id         = local.devOpsInfrastructureUser_object_id
}

resource "azurerm_role_assignment" "vnet_reader_doi_user" {
  scope                = azurerm_virtual_network.core.id
  role_definition_name = "Reader"
  principal_id         = local.devOpsInfrastructureUser_object_id
}

Als Nächstes müssen wir die Ressourcen DevCenter und DevCenter Projekt anlegen da diese die Grundlage für den Managed DevOps Pool darstellen:

resource "azurerm_dev_center" "core" {
  name                = "dc-${var.project}-${var.stage}"
  location            = azurerm_resource_group.core.location
  resource_group_name = azurerm_resource_group.core.name
  tags      = local.tags

}

resource "azurerm_dev_center_project" "core" {
  name                = "dcp-${var.project}-${var.stage}"
  dev_center_id       = replace(azurerm_dev_center.core.id, "devCenters", "devcenters")
  location            = azurerm_resource_group.core.location
  resource_group_name = azurerm_resource_group.core.name
  tags      = local.tags

}

Die eigentliche Terraform Ressource zum Anlegen von Managed DevOps Pool ist der Runner. Diesen müssen wir über den AZAPI Terraform Provider anlegen. Dokumentation zum Verwenden der Ressource findet man hier.

resource "azapi_resource" "runner" {
  parent_id = azurerm_resource_group.core.id
  type      = "Microsoft.DevOpsInfrastructure/pools@2024-10-19"
  name      = "mdp-${var.project}-${var.stage}"
  location  = azurerm_resource_group.core.location
  tags      = local.tags
  body = {
    properties = {
      organizationProfile = {
        organizations = [
          {
            url = "https://dev.azure.com/${var.azure_devops_organization}"
            projects = [
              "${var.azure_devops_project}"
            ],
            parallelism = var.parallelism
          }
        ],
        permissionProfile = {
          kind = "Inherit"
        }
        kind = "AzureDevOps"
      }
      devCenterProjectResourceId = azurerm_dev_center_project.core.id
      maximumConcurrency         = var.maximumConcurrency
      agentProfile = {
        maxAgentLifetime    = var.maxAgentLifetime
        gracePeriodTimeSpan = var.gracePeriodTimeSpan
        kind                = var.runner_state
        resourcePredictionsProfile = {
          predictionPreference = "Balanced"
          kind                 = "Automatic"
        }
      }

      fabricProfile = {
        sku = {
          name = var.runner_sku
        }
        images = [
          {
            aliases = [
              "ubuntu-22.04"
            ]
            buffer             = "*"
            wellKnownImageName = "ubuntu-22.04/latest"
          }
        ]
        osProfile = {
          secretsManagementSettings = {
            observedCertificates = []
            keyExportable        = false
          }
          logonType = "Service"
        }
        storageProfile = {
          osDiskStorageAccountType = "StandardSSD"
          dataDisks                = []
        }
        networkProfile = {
          subnetId = azurerm_subnet.runner.id
        }
        kind = "Vmss"
      }
      provisioningState = "Succeeded"
    }
  }
}

An dieser Stelle sind wir mit den Terraform-Aufgaben durch und wechseln ins Azure DevOps Portal. Da wir die Ressourcen über die Pipeline anlegen wollen, brauchen wir als Erstes eine Service Connection. Diese legen wir unter Verwendung des Service Principals aus dem ersten Schritt an (zur Erinnerung: die Vorgehensweise beim Anlegen des Terraform-Grundgerüsts inklusive Service Principal und Terraform State Management findet man unter folgendem Link. Natürlich würden sich die notwendigen Anpassungen im Azure DevOps Portal auch mit Terraform über den Azure DevOps Provider realisieren lassen.

Als Nächstes müssen wir dem Nutzer, in dessen Kontext die Pipeline läuft, Berechtigungen erteilen. In unserem Fall ist dies der bereits bekannte Service Principal. Dieser manuelle Schritt erfolgt vor dem Ausführen der Pipeline zum Anlegen des Managed Pools:

  1. Der Service Principal soll als „User“ zur „Organization“ hinzugefügt werden.
  2. Dem Service Principal wird anschließend die „Project Administrator“-Berechtigung auf das Projekt erteilt.
  3. Unter „Organisation settings“ → „Pipelines“ → „Agent Pools“ muss der Service Principal als Administrator hinzugefügt werden. (Die Berechtigungen können nach dem initialen Ausführen zum Anlegen des Pools wieder entzogen werden.)

Es ist alles bereit, um die Pipeline erstmalig laufen zu lassen. Die Pipeline ist eine einfache YAML-Pipeline, die unter anderem Terraform plan und Terraform apply ausführt. Da beim initialen Lauf der Managed Pool noch nicht vorhanden ist und erst angelegt werden muss, lassen wir die Pipeline mit einem Standard Microsoft Hosted Agent laufen:

pool:
  vmImage: ubuntu-latest

Das Ergebnis sehen wir direkt im Azure Portal:

Picture 2 1

Der finale Schritt ist das Austauschen des zu verwendenden Pools in der Pipeline:

pool:
   name: mdp-poc-dev

Ab jetzt läuft die Pipeline im Microsoft Managed Pool, der sich in unserem privaten Netzwerk befindet. Wir sind nun bereit, alle Features der Managed DevOps Pools für Azure DevOps voll auszunutzen.

Sobald die Agents im Pool einen Job ausgeführt haben, hat man im Azure Portal die Möglichkeit, die Ausführung des Jobs auszuwerten sowie weitere Metriken und mögliche Fehler zu analysieren. Als Einstieg benutzt man die Overview-Seite der Ressource „Managed DevOps Pool“:

Monitoring

Unter „Monitoring“ kann man sich die einzelnen Metriken genau anschauen und auch dazu Alerts erstellen.

Welche Limitierungen gibt es bei Managed DevOps Pools?

  1. Kein Fernzugriff: Was bei den Self-Hosted Agents ein Vorteil ist, könnte bei dem Einsatz von Managed DevOps Pools ein Nachteil sein – die virtuellen Maschinen sind für mögliche Anpassungen nicht erreichbar, da sie komplett in der Verantwortung von Microsoft liegen.
  2. Begrenzte Anpassungsmöglichkeiten: da die Infrastruktur von Microsoft verwaltet werden, hat man im Vergleich zu selbst gehosteten Agenten nur begrenzte Kontrolle über deren Konfiguration
  3. Regionale Verfügbarkeit: Managed DevOps Pools sind nur in bestimmten Azure-Regionen verfügbar. Vor dem Einsatz überprüfen werden, ob die gewünschte oder im Projekt vorausgesetzte Region verfügbar ist. Dafür öffnet man in der Subscription Overview “Resource Providers”, sucht nach Microsoft.DevOps, klickt auf Microsoft.DevOpsInfrastructure und kontrolliert im Tab “Location”, ob die notwendige Region auswählbar ist:
Screenshot 2025 01 31 at 08.34.03 768x406 1

4. Quotenbeschränkungen: neben der regionalen Verfügbarkeit sollte man beachten, dass es Standardquoten für die Anzahl der Agenten gibt, die man erstellen kann. Möglicherweise muss eine Erhöhung beantragt werden, wenn der Bedarf diese Grenzen überschreitet. Wie man die Erhöhung beantragt, ist hier beschrieben.

Zusammengefasst bieten Managed DevOps Pools for Azure DevOps eine zentralisierte und vereinfachte Lösung zur Verwaltung von Agenten für CI/CD-Pipelines. Diese Pools ermöglichen eine sichere und automatisierte Integration in private Netzwerke und minimieren gleichzeitig den Wartungsaufwand durch Microsofts Management der zugrunde liegenden Infrastruktur. Trotz einiger Limitierungen bezüglich Fernzugriff und Anpassungsmöglichkeiten, bieten Managed DevOps Pools eine flexible und skalierbare Lösung, die besonders in komplexen Unternehmensumgebungen von Vorteil ist.

Sie möchten mehr über Managed DevOps Pools for Azure erfahren oder diese direkt in Ihren Projekten nutzen? Sprechen Sie uns an!

Inhalt (Lesezeit: 10,9min)
Bereit für den Start?

Wir unterstützen Sie dabei, den Rollout schnell und sicher umzusetzen.

Das könnte dich auch interessieren
  • wd newsletter grafik
    white duck News – September 2026

    Die Zukunft gehört Unternehmen, die KI nicht nur testen, sondern produktiv, sicher und wirtschaftlich in ihre Prozesse integrieren. Der Weg [...]

    23. September 2026

  • DB WD Blog TokenControl SocialMediaSizes v04 Explainer BlogHeader 2560x1702px@2x scaled 1
    Token Control 5.1: Responses API, Tool-Governance und mehr Modelle

    Mit Token Control 5.1 erweitern wir die Governance für die Responses API. Im Mittelpunkt steht eine veränderte Nutzung von KI: [...]

    16. September 2026

  • DB WD Blog TokenControl SocialMediaSizes v04 Explainer BlogHeader 2560x1702px@2x scaled 1
    Microsoft Foundry Agents mit Token Control unter Kontrolle

    Microsoft Foundry hat die Einstiegshürde für KI-Agenten so weit gesenkt, dass Agenten heute nicht mehr nur in Entwicklungsteams entstehen. Prompt [...]

    13. September 2026

  • white duck team
    Unser Team kurz vorgestellt – Eva Kallrath

    Platform Engineer - Wie alles begann Mein Einstieg in die IT begann mit einem dualen Studium der Wirtschaftsinformatik, in dem [...]

    13. Juli 2026

  • wd newsletter grafik
    white duck News – September 2026

    Die Zukunft gehört Unternehmen, die KI nicht nur testen, sondern produktiv, sicher und wirtschaftlich in ihre Prozesse integrieren. Der Weg [...]

    23. September 2026

  • DB WD Blog TokenControl SocialMediaSizes v04 Explainer BlogHeader 2560x1702px@2x scaled 1
    Token Control 5.1: Responses API, Tool-Governance und mehr Modelle

    Mit Token Control 5.1 erweitern wir die Governance für die Responses API. Im Mittelpunkt steht eine veränderte Nutzung von KI: [...]

    16. September 2026