Continuous Deployment in Azure
Continuous Deployment (CD) beschreibt einen Prozess, der es erlaubt, Software möglichst agil und schnell zu entwickeln. Primär ermöglicht CD, viele kleine Schritten und kleine Features möglichst schnell zum Kunden zu liefern. Damit dieses "Delivery" möglichst effizient und fehlerfrei funktioniert, versucht man den Prozess des Testings und Delivery vollständig zu automatisieren. Ich bin kein Softwareentwickler. Im Gegenteil – für mich ist die Infrastrukturwelt interessant. Warum schreibe ich dann über Continuous Delivery?
Ganz einfach: In der IT-Infrastruktur wird es immer wichtiger, Services schnell und effizient mit möglichst wenig manuellen Fehlerquellen an den Kunden und an den Enduser zu bringen. Die Zeiten, in denen der Admin mit einer DVD in den Keller ging, um den Server zu installieren, sind lange vorbei. Heute müssen wir einen Server genau so schnell installieren können, wie wir eine Datenbank zur Verfügung stellen oder neuen Speicherplatz benötigen. Insofern macht es Sinn, sich von der Continuous Delivery-Ecke ein Stück abzuschneiden und die eigene Infrastruktur mit allen Komponenten – wie Server, Netzwerk, Software, Datenbank – ähnlich zu betrachten.
Ich will anhand einiger kurzer Beispiele zeigen, wie man mit einem Cloud-Service wie Microsoft Azure die Möglichkeit hat, auch eigene Infrastruktur sehr schnell automatisiert zu erstellen und weiterzuentwickeln. Man könnte an dieser Stelle anmerken, dass ich von DevOps spreche. Das ist nicht ganz falsch. Die Grenzen verschwimmen und eine genaue Linie zu ziehen ist schwierig. Letzten Endes stellt sich nur die Frage, welche der im Folgenden gezeigten Features und Techniken Ihren Anforderungen entsprechen und helfen, Ihren Job einfacher zu machen.
In diesem Artikel beginne ich mit einigen Grundlagen und baue auf dem Verständnis für die Basis-Technologie auf: Was ist Azure und was kann der Service auf dieser Reise für Werkzeuge und Dienste zur Verfügung stellen. Das hilft, darüberliegende Automatisierungsprodukte bei Fehlverhalten besser zu verstehen. Wenn man nicht weiß, was unter der Oberfläche passiert, ist eine Fehlermeldung meist schwer zu interpretieren.
Azure Resource Manager – Ein Überblick
Die Microsoft Azure Cloud ist heutzutage ein allseits bekannter Begriff in der IT-Welt von Microsoft. Mein kurzer Überblick handelt nicht von den vielen möglichen Features, sondern baut auf Funktionen auf, die wir für unsere Automatisierungszwecke nutzen. Ich gehe dabei auf Dienste ein, die ich in einem Beispiel nutzen werde.
Einstieg
Nach einem kurzen Einstieg in das Azure-Portal [1] folgt eine Anmeldung mit einem Login für den Zugang zu den berechtigten Azure-Abonnements.
Begrüßt wird man hier mit einem Standard-Dashboard. Bereits existierende Ressourcen werden in kleinen "Tiles" angezeigt, die jederzeit größer, kleiner oder ausgeblendet werden können. Das Dashboard kann den eigenen persönlichen Vorlieben angepasst werden.
In der linken Spalte ist neben dem NEW-Button zur Anlage neuer Ressourcen auch eine Favoritenliste aller gepinnten Ressourcen-Typen zu sehen, um die Navigation und die Suche nach bestimmten Ressourcen zu vereinfachen.
Dieses Portal ist das Interface, um manuell Änderungen oder Anpassungen vorzunehmen sowie den Status einer Ressource zu kontrollieren und um schnell etwas zu testen. Es ist definitiv kein Interface für den ambitionierten IT-Infrastruktur-Fachmann und schon gar nicht für einen PowerShell-Liebhaber oder zukünftige CD-Wege.
PowerShell everywhere
Wie erwartet gibt es für die Azure-Welt natürlich auch ein PowerShell-Interface. Wer es noch nicht kennt sollte möglichst rasch einen Blick darauf werfen [2] und einfach "Azure" als Suchbegriff eintippen. Zum Zeitpunkt der Erstellung dieses Artikels wurde ich mit 215 Modulen fündig. Für die aktuelle PowerShell-Version 5.1 ist die Installation recht einfach. Dies kann per $PSVersionTable überprüft werden (s.Abb.2). Mit dem PowerShell-Kommando Find-Module wird das korrekte Modul gesucht und per Install-Module einfach installiert.
Aber Vorsicht: nach Möglichkeit sollte immer das passende AzureRM-Modul verwendet werden. Das Kürzel RM im Namen steht hier für den Ressource Manager. Fehlt dieses Kürzel, bewegen wir uns im Classic-Modus bzw. in der Service Manager-Welt und diese wollen wir nicht mehr verwenden. Die Gründe dafür werden im nächsten Abschnitt erklärt.
Was ist der Azure Resource Manager?
Für klassische Infrastruktur erlaubt Azure Resource Manager, die Umgebung zu beschreiben und wiederholt, kontrolliert und letztendlich auch dokumentiert zu erzeugen. Durch die Methodik und Speicherung der Aufgaben in einem einfachen und lesbaren Textfile im JavaScript Object Notation-Format (JSON) ist ein einfaches Change-Management rasch umgesetzt. Eine solche Beschreibung (das JSON Textfile) kann etwa in eine Versionsverwaltung wie Git gespeichert werden und steht so für weitere (automatisierte) Anpassungen zentral zur Verfügung. Man nimmt das JSON-Dokument, speichert es in ein GIT und ist nur einen Commit von einer neuen Version und damit einem "Change" entfernt.
Ein GIT ist ein Versionierungssystem [3]. Bekannt durch GitHub [4] oder BitBucket [5], verwendet auch Microsoft in vielen Bereichen schon länger dieses System. Damit können Versionen von Files und vor allem deren Veränderung aufgezeichnet werden. Jede Änderung eines Files oder sogar einer Zeile wird damit erkannt und automatisch dokumentiert. Wie üblich hat man ein paar Worte erfunden um den Prozess etwas cooler klingen zu lassen. Kurz erklärt aber einige Ausdrücke, die ich auch immer wieder verwende.
- Repository: Der Source-Code eines Projektes zusammengefasst mit all seinen Files und Ressourcen.
- Commit : Eine getätigte Änderung eventuell mit Kommentar als aufgezeichnete Änderung in die lokale Kopie des Repositories übernehmen.
- Commit History : Die History aller Commits zeigt nun einfach alle getätigten Änderungen an unseren Files und somit eine genaue Aufzeichnung der Entwicklung.
- Branch: Ich will etwas testen und den primären Entwicklungszweig nicht stören. Ich eröffne einen neuen Branch, tätige meine Änderungen und wenn alles funktioniert wie ich mir das vorstelle, könnte ich diesen Branch mit einem Pull Request wieder zurück in den MASTER Branch mergen lassen.
- Pull Request : Der Versuch, das lokale Repository mit seinen Änderungen in das zentrale Repository zu mergen. Sofern erlaubt und ohne Kollisionen möglich, kann dies automatisch passieren.
Ein solches Setup ist für unsere Continuous Deployment-Anforderung nahezu perfekt. Viele Konzepte aus dem Agile Development werden hier in die DevOps-Schiene verschoben und für eigene Zwecke genutzt. Die Commit History gibt Auskunft über alle Details von Änderungen im Environment.
ARM als Basis zur Nutzung von Diensten
Der Azure Resource Manager verwendet eine RESTful API [6], die mit sogenannten Templates gefüttert wird. Die Engine (der Ressourcen-Manager) übernimmt dieses Template und versucht den gewünschten Zustand (Desired State) herzustellen. Dies ist nicht mit Powershell Desired State Configuration zu verwechseln, das ist ein anderes Thema.
Per ARM Template können Ressourcen wie eine virtuelle Maschine oder eine Datenbank aber nicht nur einmalig angelegt werden. Wir können diese auch anpassen. Das bedeutet, wir haben ein ARM Template, das sich verändern und entwickeln kann. Gibt es eine Änderung, können wir das Template erneut auf unsere Ressourcen-Gruppe anwenden und ARM wird alles Notwendige tun um die Ressource dem Template anzupassen. Man könnte schon fast von Continuous Deployment in einer sehr einfachen Form sprechen, aber dazu kommen wir noch.
Azure Resource Manager
Damit wir verstehen wie so ein gesamter Prozess abläuft, will ich ganz zu Anfang auf die Grundlagen eingehen. Ich erkläre kurz, was diese wichtig klingenden Begriffe wie Ressource, Template oder Deployment und deren Zusammenhang bedeuten. Anschließend bauen wir ein einfaches Beispiel, um zu verstehen, wie diese Dinge zusammenhängen.
Resources
In ARM sind alle Komponenten und Services eine oder mehrere Ressourcen. Somit ist eine Virtuelle Maschine genauso eine Ressource wie etwa auch eine Azure SQL-Datenbank. Viele größere Ressourcen können dann in kleinere Einzelteile zerlegt werden. Eine Virtuelle Maschine zum Beispiel besteht aus der VM selbst, aus einer Netzwerkkarte, einer IP-Konfiguration, einer Festplatte und optional auch Dingen wie einer öffentlichen IP-Adresse, zusätzlichen Festplatten oder Plug-Ins wie Virenscanner oder Management Agents.
Azure Resource Groups
Ressourcen in Azure können in sogenannten Ressource-Gruppen zusammengefasst werden. Diese Gruppen erlauben eine simple Strukturierung um die bessere Übersicht zu behalten. Eine Ressource-Gruppe ist grundsätzlich einfach ein Container für mehrere Ressourcen. Weiterhin können auf Ebene der Ressource-Gruppe aber auch Berechtigungen vergeben und Kosten ausgewertet werden.
Für ARM Templates haben Ressource-Gruppen eine spezielle Bedeutung. Ein Template kann nur den Inhalt einer Ressource-Gruppe beschreiben, niemals aber die Ressource-Gruppe selbst. Es ist auch nicht möglich mit einem Template eine Ressource-Gruppe selbst anzulegen. Diese muss zuerst erzeugt werden, um danach das Template innerhalb der Ressource-Gruppe auszuführen. Das klingt im ersten Moment nach einer Limitierung, macht aber in der Betrachtung von ganzen Deployments Sinn.
Templates
Ein Template beschreibt in einer definierten Form, im JSON-Format [7], eine Sammlung von Ressourcen, deren Einstellungen und eventuell auch die Abhängigkeiten untereinander. Ein einzelnes Template kann aus einem oder mehreren JSON-Files und optional einem Parameter-File bestehen.
In diesem Beispiel sind zwei Templates in Form von JSON-Files und ihren passenden Parameter-Files zu sehen. Der Inhalt besteht aus drei Bereichen. Diese Bereiche beschreiben jeweils Teile des Gesamttemplates und werden später näher beleuchtet.
Templates können in sehr einfacher Form eine einzelne Datenbank oder eine VM erzeugen, aber auch sehr komplex und viele hunderte Zeilen lang werden. So können u. a. ganze Environments mit Domain Controllern, Datenbanken, Web Services erzeugt werden.
Parameter-Files sind letztendlich auch nur JSON-Files, die es erleichtern, alle Inputparameter zur Laufzeit anzugeben. Man kann damit Sets von Deployment-Parametern definieren: ein Set für das produktive Deployment, ein Set für Entwickler, ein Set für die Qualitätssicherungsumgebung. Warum benötige ich das? Ganz einfach: Es gibt in Azure ein paar Dinge die global eindeutig sein müssen. Ein Storage-Account als Beispiel hat einen globalen DNS-Namen. Dieser wird aus dem Namen des Accounts und einer fixen DNS-Domain erzeugt. Ist der Name des Storage Accounts in der Testumgebung aber mit dem Namen in der Produktivumgebung identisch, gibt es einen Deployment-Fehler.
Listing 1:
{ "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", "contentVersion": "1.0.0.0", "parameters": { "StorageAccountName": { "value": "cwstorageaccountname" } } }
Das einfachste Beispiel eines Parameter-Files ist hier zu sehen (s. Listing 1). Es beinhaltet nur den Namen und den Wert eines einzelnen Parameters. Der Name entspricht dem Namen des dazu passenden Parameters im Template-JSON.
Deployment
Es gibt zwei verschiedene Deployment-Methoden: Inkrementell (Incremental) und Vollständig (Full). Diese beiden Methoden zu unterscheiden ist sehr wesentlich. Wähle ich den Modus "Full" so wird das ARM dafür sorgen, dass der Inhalt meiner Ressource-Gruppe am Ende des Deployments zu 100 Prozent dem Template entspricht. Wenn man in einem bestehenden Template, das bereits in einer Ressource-Gruppe deployt ist, Einstellungen ändert, dann wird die bestehende Virtuelle Maschine einfach erweitert. Wichtig dabei ist, dass existierende Ressourcen, die nicht im Template definiert sind, gelöscht werden. Das kann mitunter unbeabsichtigte und fatale Folgen haben, denen man sich bewusst sein muss.
Im Modus "Inkrementell" ist dies etwas entspannter. Wird eine passende Ressource gefunden, dann wird sie sehr wohl aktualisiert. Existieren jedoch überzählige Ressourcen, so werden sie einfach ignoriert. Nicht existente Ressourcen werden gemäß Template erzeugt. Der Modus selbst muss zu Beginn des Deployments mit einem einfachen Powershell-Parameter gewählt werden und kann bei jedem Deployment geändert werden. Es macht aber je nach Anwendung meist wenig Sinn, diesen Modus zu wechseln.
Deployment-Schema
Ein Schema beschreibt, welche Werte und Parameter innerhalb des JSON-Files möglich und erlaubt sind. Diese Schemata sind selbst in JSON definiert und müssen im Regelfall nicht selbst geändert werden. Der Vollständigkeit möchte ich hier nur referenzieren [8].
Nutzt man einen eigenen JSON-Editor der mit Schemata umgehen kann, so hilft er mit entsprechenden "Intellisense", zu zeigen welche Werte weiter möglich wären.
Der Parameters-Abschnitt
Der Parameters-Abschnitt eines Template erlaubt es diesem, über Command Line oder auch ein eigenes spezielles Eingabe-File zur Laufzeit angepasste Werte mitzugeben. Der einfachste Anwendungsfall dafür ist die Anlage der gleichen Ressource mit geändertem Namen. Dieser wird als Parameter zur Laufzeit übergeben und kann vom User eventuell auch bestimmt werden.
Der Variables-Abschnitt
Wie in Programmiersprachen ist es möglich, Variablen zu definieren, die feste Werte enthalten, aber auch durch gewisse Funktionen Parameter innerhalb eines Templates umzuwandeln bzw. zu verändern. Diese können wiederum später im Resources-Abschnitt verwendet werden.
Der Resources-Abschnitt
Die Resources stellen das Herz jedes Template dar. Hier werden die eigentlichen Ressourcen definiert und beschrieben. Zusammen mit Parametern und Variablen wird das gewünschte Endergebnis zusammengestellt. Abhängigkeiten können hier genauso definiert werden wie auch die Variablen zuvor verwendet werden, etwa um nicht nur eine sondern die gewünschte Zahl an virtuellen Maschinen zu erzeugen oder auch um einem Storage-Account einen gewünschten Namen zu geben.
ARM Template-Funktionen
Hiermit ist es möglich, innerhalb eines Deployments einfache Veränderungen durchzuführen. Diese Funktionen beschränken sich jedoch auf Basis-Anpassungen, wie das Modifizieren von Strings, Zahlen oder einfachen Schleifen. In Summe gesehen sind sie aber ein mächtiges Werkzeug, das weitere Flexibilität in ein Template einbringen kann [9].
PowerShell
Wem das Schreiben von JSON-Code etwas zu mühsam oder unübersichtlich ist, kann sich natürlich auch mit PowerShell behelfen. Es gibt für die einzelnen Ressource-Provider auch entsprechende PowerShell-Module, die über PowerShell Gallery zur Verfügung gestellt werden [10]. Gepaart mit PowerShell 5.x sind diese nur eine Code-Zeile entfernt:
Find-Module -Name Azure* | Install-Module -Force
Die genutzten PowerShell CmdLets erzeugen letztendlich im Hintergrund auch den selben ARM JSON-Code. Dieser Vorgang ist aber in einer vielleicht angenehmeren und flexibleren Form verwendbar. Wer sich mit PowerShell wohler fühlt, für den ist das natürlich eine valide Option. Das folgende Codestück zeigt, wie in wenigen Zeilen auf eine Azure Subscription verbunden wird, angemeldet und ein Azure Storage Account angelegt werden kann.
Import-Module AzureRm.Profile Import-Module AzureRm.Storage Login-AzureRmAccount Select-AzureRmSubscription -SubscriptionId 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' $account = New-AzureRmStorageAccount -ResourceGroupName 'RG-CW-Informatik01' -Name 'rgcwinformatik01' -SkuName Standard_LRS -Location WestEurope
Wichtig ist hier zu unterscheiden: Das Schreiben eines Scripts als "Deployment"-Methode ist natürlich valide. Im Kontext von Continuous Deployment geht es mir aber darum, bewusst nicht Powershell-Code zu verwenden. Warum? Nun, das ist einfach erklärt: Würde man Ressourcen per PowerShell erzeugen, kann und muss man die Existenz und Veränderung im Script selbst berücksichtigen. Man muss also darauf aufpassen, dass eine Ressource bereits angelegt ist und überprüfen, ob alle Parameter dem entsprechen.
Ein simples Beispiel wäre es, eine virtuelle Maschine per Template zu erzeugen. Habe ich nun ein Monat später die Anforderung, dass diese eine zusätzliche Festplatte bekommen soll, so kann ich in meinem ursprünglichen Template einfach eine Festplatte hinzufügen und das Template erneut deployen. ARM wird für mich überprüfen, ob die virtuelle Maschine schon vorhanden ist und nur die Festplatte hinzufügen.
Würde ich das selbe mit Powershell machen, müsste ich zuerst abfragen, ob die virtuelle Maschine bereits existiert, sie schon eine zusätzliche Festplatte hat und ob ich sie noch hinzufügen kann.
In meinem Beispiel hat Microsoft dies schon für mich getan. Es gibt ja den Azure Resource Manager, der diese Aufgabe übernimmt. Ich muss nur noch ein JSON-File erzeugen, um den gewünschten Zustand zu beschreiben. Verändere ich das JSON-File, verändert sich meine Umgebung.
Manuelles Deployment
Ein rascher Einstieg in der Nutzung von JSON-Files ist die manuelle Deployment-Methode. Damit kann man sehr schnell ohne Infrastruktur ein JSON Template-File testen und global sehen, ob es funktioniert.
Das JSON-File kann direkt im Azure-Portal importiert werden. Über den Marketplace wird das Microsoft Template Deployment ausgewählt. Anschließend kann einfach der JSON-Text in den Editor kopiert werden. Dann eventuell geforderte Parameter definieren, Create anklicken und starten. Nach erfolgtem Deployment kann man die erzeugten Ressourcen bewundern. In meinem Beispiel einen einfachen Storage-Account.
PowerShell
PowerShell ermöglicht es ebenso, Ressourcen direkt zu erzeugen, aber es hilft uns auch bei einem Deployment. Mit wenigen Zeilen Code kann eine Azure Subscription ausgewählt und darin ein Template Deployment gestartet werden. Der Beispiel-Code anbei zeigt in einfachster Form, wie man ein Deployment per PowerShell starten kann.
$TemplateFileName = 'storageaccount.json' $TemplateConfigFileName = 'storageaccount.parameter.json' # connect to azure subscription with an adminaccount $AzureCredential = Get-Credential -Message 'AzureAdmin' -UserName 'user@irgendwas.onmicrosoft.com' # select the correct subscription Select-AzureRmSubscription -SubscriptionId 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' # create a unique name for the deployment $DeploymentName = (New-Guid).Guid # define a name for the resource group if not already created $AzureResourceGroupName = 'RG-CW-Informatik01' # define the azure datacenter location to deploy $AzureLocation = 'West Europe' # create the resource group New-AzureRmResourceGroup -Name $AzureResourceGroupName -Location $AzureLocation # start the deployment. New-AzureRMResourceGroupDeployment -Name $DeploymentName ` -ResourceGroupName $AzureResourceGroupName ` -TemplateFile $TemplateFileName ` -TemplateParameterFile $TemplateConfigFileName
Dieses einfache PowerShell-Script verbindet sich auf die Azure-Cloud, wählt eine definierte Subscription aus und lädt das entsprechende JSON-File um das Deployment zu starten. Diese Methode ist sehr simpel und kann auf jedem Rechner gestartet werden, der auch die Azure PowerShell-Module und zu mindestens Powershell 3.0 installiert hat.
Den Ablauf des Deployments kann man in der entsprechenden Resource Group im Portal beobachten. Auch wenn Fehler auftreten, sind diese hier verzeichnet. Dazu den Menüpunkt "Deployments" auswählen und den dort generierten Namen eines Deployments auswählen. Neben Laufzeit und Ergebnis sind hier natürlich auch einzelne Events und eventuell aufgetretene Fehler zu finden.
Continuous Deployment
Etwas im Portal zusammen zu klicken ist schlichtweg langweilig. Vor allem ist es immer ein manueller Prozess: Ich selbst kopiere das JSON-File ins Portal. Das hat wenig mit Continuous zu tun, mehr mit viel manuellem Aufwand und vielen Fehlerquellen, die keinen wirklichen Vorteil haben.
Zu einem wirklichen Vorteil wird es erst, wenn wir einen Weg finden, das bestehende Template auf Knopfdruck zu deployen bzw. auch, dies aufgrund eines Events automatisch passieren zu lassen. Eine Veränderung des Template zum Beispiel könnte ein solcher Event sein: wenn jemand die neue Version seines Template auf ein GIT Repository [11] pusht und daraufhin auch testen möchte, ob diese neue Version funktioniert. Baut man diesen Prozess aus, sollte das Push sofort dazu führen, dass das Deployment in das Testenvironment automatisch startet und der Mitarbeiter nur noch das Ergebnis sieht.
Ein weiteres Beispiel wäre die Verbindung eines Systems, das aufgrund seiner Auslastung skaliert werden muss. Auch hier wollen wir zum Beispiel anhand einer definierten CPU-Last entscheiden, dass eine weitere virtuelle Maschine benötigt wird.
Ein "TestEnvironment" wird oft von vielen Developern benutzt. Diese wollen immer das gleiche Environment, das möglichst sauber, frisch installiert und am besten täglich neu erstellt wird. Ein idealer Anwendungsfall. Das Deployment kann automatisiert und zeitgesteuert ein- oder mehrmals pro Tag, eventuell auch auf Wunsch die gesamte Testumgebung einfach neu erstellen.
Dies sind einige simple Beispiele, die wir abdecken können. Ich will nun zeigen wie das praktisch umsetzbar ist.
Automation Account – Step 1
Ein Azure Automation Account ist eine Methode, ein Deployment zu automatisieren [12]. Ein Automation Account erlaubt es, Powershell-Scripte in einem kontrollierten Umfeld laufen zu lassen. Sogenannte Runbooks starten PowerShell-Scripte, die – wie im Beispiel zuvor – mit kleinen Anpassungen ein Deployment starten können.
Was braucht man dafür? Einerseits wird ein angepasstes Powershell-Script benötigt, das wie im Beispiel unten aussehen könnte.
$TemplateUri = 'https://raw.githubusercontent.com/chwilfing/PublicScripts/InfoAktuellDemo/InformatikAktuell-Demo/StorageAccount.json' $TemplateParameterUri = 'https://raw.githubusercontent.com/chwilfing/PublicScripts/InfoAktuellDemo/InformatikAktuell-Demo/StorageAccount.parameter.json' $RGName = 'RG-CW-informatik01' $AZConnection = 'AzureRunAsConnection' Import-Module AzureRM.Profile #Import-Module AzureAutomationAuthoringToolkit Import-Module AzureRM.Automation $DeploymentName = (New-Guid).Guid $AzRGName = $RGName $AzureLocation = 'northeurope' try { # Get the connection "AzureRunAsConnection " $servicePrincipalConnection=Get-AutomationConnection -Name $AZConnection 'Logging in to Azure...' Add-AzureRmAccount ` -ServicePrincipal ` -TenantId $servicePrincipalConnection.TenantId ` -ApplicationId $servicePrincipalConnection.ApplicationId ` -CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint } catch { if (!$servicePrincipalConnection) { $ErrorMessage = "Connection $($AzConnection) not found." throw $ErrorMessage } else{ Write-Error -Message $_.Exception throw $_.Exception } } New-AzureRmResourceGroup -Name $AzRGName -Location $AzureLocation -Force | out-null New-AzureRMResourceGroupDeployment -Name $DeploymentName ` -ResourceGroupName $AzRGName ` -TemplateUri $TemplateUri ` -TemplateParameterUri $TemplateParameterUri ` -Verbose ` -Force
Erweitert wurden folgende Teile:
- Template URI / Template Parameter URI
Damit die Azure Engine auf das JSON-File zugreifen kann, muss dieses an einer öffentlich zugänglichen Stelle erreichbar sein. Die Engine selbst authentifiziert sich nicht (bzw. kann das nicht). Meist und recht einfach sind daher Public GitHub-Repositories zu nutzen. Hat man sein eigenes GIT laufen kann man dieses natürlich verwenden. Will man seine Repositories nicht öffentlich machen, so muss man sich um Deployment-Zertifikate kümmern, um das ganze privat zu machen. Um das Deployment im ersten Schritt einfach zu halten, habe ich den einfachen Weg eines öffentlichen Repositories gewählt. - Azure Service Principal
Aus einem Automation-Account heraus hat ein Powershell-Prozess zunächst keinerlei Rechte. Er kann nur auf Ressourcen innerhalb des Automation-Accounts zugreifen. Daher müssen wir den Umweg eines Azure AD Service Principals gehen [13]. Dieses kann einfach innerhalb des Automation-Accounts angelegt werden und dann wie im Code Sample gezeigt referenziert werden, um Zugriff auf die Subscription und/oder deren Ressourcen zu bekommen. Der Powershell-Prozess selbst hat natürlich nur Zugriff auf Ressourcen auf die der Service Principal auch Zugriff hat. Schränkt man diese auf eine einzelne Ressource-Gruppe ein, so kann es nur diese modifizieren. - Error Handling
Etwas Fehlerbehandlung gehört immer dazu. In diesem Beispiel wird nur das Minimum verwendet, um Fehler eines nicht existenten Service Principals abzufangen.
Das Runbook kann hier einfach mit dem Start-Button manuell gestartet werden und deployed das Template, welches hinter unserer Template URI und der Template Parameter URI versteckt ist.
Hat man alles richtig gemacht, wird man mit dem Output des Scripts und einer "Erfolgreich"-Meldung belohnt.
Automation Account – Step 2
Hat man es bis hierher geschafft, kann man schon sehr zufrieden sein. Immerhin konnten wir bereits ein fertiges Template per Mausklick deployen. Egal ob es jetzt ein einfacher Storage-Account ist oder eine vollständige Umgebung mit mehreren Servern. Solang das JSON-Template korrekt ist, funktioniert das Deployment per Mausklick.
Aber wer automatisiert jetzt den Mausklick?
Ich habe eingangs erwähnt, dass es doch schön wäre, wenn man bei Änderung des Templates auch automatisch eine neue Version des JSON-Templates deployed bekäme. Der Grundstein ist gelegt. Was bedeutet eine neue Version in unserem Fall? Nachdem wir ein GIT als Basis für unsere JSON-Templates verwenden, wäre es doch besonders schön, wenn wir einfach ein Push auf den entsprechenden Branch nutzen könnten, um das Template Deployment anzustoßen oder?
Nichts einfacher als das.
Sogenannte Webhooks erlauben uns, ein Runbook in einem Automation-Account einfach per HTTP-Request aufzurufen [14]. Das bedeutet, wir können unser Runbook jederzeit von überall auf der Welt aufrufen. Auch etwa per Powershell vom eigenen PC aus:
Invoke-WebRequest -Method Post -Uri 'https: //s9events.azure-automation.net/webhooks?token=xxxxxxxxx
Oder natürlich auch von den meisten Code-Repositories aus. Je nach Code-Repository führen hier unterschiedliche Wege zum Ziel. Bei GitHub, das ich im Beispiel nutze, kann man in den Einstellungen des Repositories per Klick neue Webhooks anlegen und auswählen, bei welchen Events diese aufgerufen werden sollen. Der angelegte WebHook kann somit getriggered werden, wenn ein Push auf das Repository gemacht wird.
Das jeweilige Repository kann einen eigenen Branch nutzen, um das Testenvironment zu repräsentieren. Pusht man auf den entsprechenden Branch, wird die Test- oder die DEV-Environment per WebHook erzeugt. Wird ein Pull Request akzeptiert, könnte man ein entsprechend anderes Script nutzen und etwa für den Import ein anderes Parameter-File verwenden.
Zusammenfassung
Auf einen Blick ergeben alle Themen zusammengefasst folgendes Rezept, um Continuous Deployment per Azure ARM-Templates einzurichten:
- Ein JSON-Template erzeugen und in einem Repository der Wahl ablegen (etwa GIT),
- einen Automation-Account in Azure mit einem Powershell Runbook anlegen, das unser JSON-Template aus dem Repository deployed,
- einen WebHook auf dem Automation-Account für das Runbook erzeugen,
- einen WebHook aus dem Automation-Account bei Push Event im Repository aufrufen lassen,
- das JSON-Template ändern und per GIT Commit / GIT Push ins Repository pushen und dann
- das Ergebnis ansehen und bei Bedarf mehrere Iterationen durchführen, bis das gewünschte Ergebnis automatisiert bereitsteht.
Weitere Hilfe?
Templates erzeugen ist eine mühsame Sache, es lohnt sich aber. Damit man hier aber nicht Stunden über Stunden damit verbringt, Fehler in JSON-Files zu suchen, gibt es von Microsoft eine wirklich unverzichtbare Ressource an bestehenden Templates. Diese umfassen so ziemlich alle Ressourcen in allerlei Ausprägungen, die man sich vorstellen kann. Diese Vorlagen sind in einem Public GitHub Repository zu finden [15]. Jeder Besucher kann einen Blick in die Templates werfen und diese analysieren.
Aus meiner Sicht ist dies ein perfekter Startpunkt für eigene Templates. Hier sind viele brauchbare Beispiele enthalten, von der Erzeugung simpler einzelner VMs bis hin zu einer vollständigen SharePoint-Farm mit 8 Servern ist alles dabei. Starten Sie mit diesen Templates und passen Sie diese an ihre eigenen Bedürfnisse an!