Nachdem wir im Vorfeld grundlegende Aspekte von Infrastructure as Code (IaC), Dynamisierungsoptionen und die Arbeit mit Modulen beschrieben haben, gehen wir in diesem Artikel einen weiteren Schritt in Richtung Automatisierung – mit Hilfe der Implementierung innerhalb von Deployment Pipelines.
Im vorliegenden Artikel wollen wir klären, was es zur Vorbereitung der Automatisierung braucht und welche Schritte innerhalb der Pipeline konfiguriert werden müssen. Dabei geht es neben dem eigentlichen Deployment auch um Tasks zur Validierung sowie Securityscans. Der Fokus liegt an dieser Stelle auf Azure DevOps; Alternativen wie GitHub Actions funktionieren aber mit ähnlichen Prinzipien – ein Beispiel zum Start finden Sie unter [1].
Azure DevOps ist eine von Microsoft entwickelte Plattform zur Automatisierung der Softwareerstellung und -bereitstellung. IT-Teams können damit Anwendungen sicher, schnell und zuverlässig bereitstellen, indem sie CI/CD Pipelines verwenden. Obwohl Azure DevOps viele Funktionen bietet, konzentrieren wir uns hier auf für die Bereitstellung der infrastrukturspezifischen Themen. Aspekte wie Taskmanagement, Branching-Strategien und Package-Management werden hier daher nicht weiter besprochen.
Über eine automatisierte Deployment Pipeline für Azure-Ressourcen habe ich hier im Windows Developer bereits in meiner Artikelserie zu Terraform geschrieben [2], viele Punkte davon sind auch für ARM- oder Bicep-Services relevant. Bezogen auf die Wahl zwischen ARM oder Bicep als Technik gibt es eigentlich keine nennenswerten Unterschiede für die Pipeline – die Engine ist ja am Ende die gleiche. Da wir uns nun auf Azure DevOps fokussieren, braucht es für ein erfolgreiches Deployment folgende Komponenten, auf die wir im weiteren Verlauf des Artikels eingehen werden:
ein DevOps-Projekt und eine Organisation
ein (Git-basiertes) Repository
eine Service-Connection
die Auswahl eines Agent-Typen
die Pipeline-Definition
Automatisierungs-Pipelines sollen sichere Deployments anhand von Codeänderungen gewährleisten und zusätzliche Stabilitäts- sowie Sicherheitsmaßnahmen integrieren. Das gilt auch für IaC-Deployments mit Bicep oder ARM-Templates. Sobald die Pipeline korrekt erstellt und konfiguriert ist, liegt der Fokus auf der Codeentwicklung für die benötigten Ressourcen.
Mehrere Umgebungen lassen sich ohne bzw. mit wenig Codeduplikation bereitstellen. Als Prozess beschrieben, kann ein Commit im Repository beispielsweise direkt ein Development Stage Deployment auslösen und manuelle Schritte minimieren. Mit implementierten Pull-Request-Strategien und Approval Gates erreicht der Code schließlich produktive Stages, was wiederholbare Deployments unabhängig von festen Releasezyklen ermöglicht und Sicherheit bringt, unter anderem durch integrierte Prüfmechanismen. Andernfalls müsste auf einen fehleranfälligen und aufwendigen manuellen Prozess gesetzt werden.
Tauchen wir also in das Thema ein: Zunächst werden die wesentlichen Bereiche von Azure DevOps erörtert, die für diesen Prozess von Bedeutung sind sowie die entsprechenden zu beachtenden Aspekte. Im Anschluss daran folgt eine detaillierte Beschreibung der einzelnen Schritte der Pipeline.
Um Bicep-Code über eine DevOps Pipeline bereitstellen zu können, werden ähnliche Mechanismen wie bei der klassischen Softwareentwicklung angewendet. Ein Azure-DevOps-Projekt mit einem Code-Repository dient als Grundlage, die während eines Pipeline-Durchlaufs auf einer virtuellen Agent-Maschine ausgecheckt wird. Abhängig vom Pool des Agents muss möglicherweise zusätzliche Software installiert werden. Die Pipeline umfasst die zuvor erwähnten manuellen Schritte als Task und kann zusammen z. B. mit einem Service Principal, der über die erforderlichen Rechte verfügt, das Deployment ausführen.
Zu Demonstrationszwecken kann sowohl eine neue Azure-DevOps-Organisation als auch ein DevOps-Projekt innerhalb dieser Organisation kostenlos erstellt werden, sofern diese noch nicht vorhanden sind. Git als Repository-Typ für das Projekt ist gesetzt und der Work-Item-Prozess ist für die Pipelines nicht relevant. Die Service Connection wird auf Projektebene erstellt. Auf Organisationsebene können beispielsweise bei Bedarf die Agent-Pools für die Pipelines konfiguriert werden und es ist ersichtlich, wie viele parallele Jobs zur Ausführung der Pipelines zur Verfügung stehen. Für den Einstieg mit einem privaten Repository und einem öffentlichen Agent reicht der eine verfügbare Job mit 1 800 Minuten Laufzeit pro Monat aus, bevor zusätzliche Kapazitäten erworben werden müssen.
Nachdem das Projekt erstellt wurde, kann das Repository initiiert werden. Inzwischen geschieht das grundsätzlich auf Git-Basis, zumindest bei der Projekterstellung. Subversion kann auch noch angebunden werden. Im Repository finden sich sowohl die Templates für die Bicep-Ressourcen als auch die Pipeline-YAML-Datei selbst. Im Unternehmenskontext sollten ebenfalls Branching-Strategien und Pull-Request-Richtlinien festgelegt werden, was an dieser Stelle jedoch zu weit führen würde. Es gilt prinzipiell: Auch Bicep-IaC-Code sollte außerhalb eines Demo-Scopes nicht direkt in den Main-Branch committet werden.
Um Ressourcen über eine Pipeline bereitzustellen, muss über die Projekteinstellungen eine Service Connection eingerichtet werden. Hierbei handelt es sich im Wesentlichen um einen Namen für die Verbindung sowie – im klassischen Weg – einen Service Principal, der über die erforderlichen RBAC-Rechte in der Azure-Umgebung verfügt. Der Principal kann entweder bereits vorhanden sein oder durch den Assistenten im DevOps-Portal erstellt werden. Bei der Einrichtung einer Verbindung im Kontext der unternehmensinternen DevOps-Organisation kann es zu Schwierigkeiten kommen, da Anwender:innen häufig nicht über ausreichende Rechte in Entra ID für die im Hintergrund notwendigen App-Registrierungen verfügen. Zudem herrscht oft Verwirrung, da Benutzer:innen aufgrund der Einstellungen in DevOps-Projekten die Service Connections nicht sehen können, diese jedoch für Pipelines verwendet werden dürfen.
Die andere Variante, die mittlerweile von Microsoft empfohlen wird, geht hier über eine Workload Identity Federation [3]. Diese arbeitet ebenfalls über eine App Registration oder eine User-assigned Identity, die mit externen Identitätsprovidern verknüpft werden kann. Das erspart die Schlüsselrotation der App bzw. eliminiert das Risiko des Secret-Diebstahls. Der Ansatz wird aber noch nicht für alle Szenarien unterstützt. Bezüglich der Pipeline-Definition sehen wir am Ende keinen Unterscheid – Hauptsache, die Service Connection funktioniert.
Der letzte Punkt dieses Abschnitts erläutert kurz die verschiedenen Agent-Typen für Pipeline-Deployments. In Azure DevOps gibt es zwei Arten...