Kategorie: Azure CLI

Infrastructure as Code mit Azure DevOps

Im letzten Beitrag haben wir kurz vorgestellt, wie wir Infrastruktur in Azure via Azure ARM und Azure CLI bereitstellen können. Dieses Vorgehen wird als Infrastructure as Code (IaC) bezeichnet.

Dabei mussten wir feststellen, dass trotz einheitlicher Skripte bestimmte Parameter mehrfach angegeben werden müssen. Gerade die Eingabe des SQL Admin Passworts ist umständlich, fehleranfällig und für den Produktivbetrieb letztendlich ungeeignet. Darüber hinaus benötigt man zum Ausführen der Skripte die erforderlichen Rechte.

Aus diesem Grund nutzen wir Azure DevOps, früher bekannt als Visual Studio Online (VSO) oder auch Visual Studio Team Service (VSTS). Azure DevOps hat neben verschiedenen Pipeline Arten ….

Erstellen der Release Pipeline

Wir werden für unser Beispiel einzig die Release Pipelines verwenden. Diese Art beruht ursprünglich auf vorangegangenen Build Pipelines und deren Artefakte. Vereinfacht werden wir direkt auf das entsprechende GitHub zugreifen und dessen Skripte nutzen.

Dafür Links die Option Artifacts –> Add wählen, dann das entsprechende GitHub Repository wählen. Weiter muss ein Default Branch gewählt werden. Dieser ist wichtig, um in nachfolgenden Schritten durch das Repository navigieren zu können, kann jedoch für jedes Release geändert werden.

Ebenfalls ist es möglich, einen Continuous Deployment (CD) Trigger zu installieren. Ob es für IaC sinnvoll ist, ist immer abhängig von den konkreten Anforderrungen an die IaC Skripte. Es kann durchaus Sinn ergeben, dass während der Entwicklung der Skripte, regelmäßiges Ausführen zur besseren Qualität beitragen kann, um Fehler frühzeitig aufdecken zu können.

Zuerst müssen wir eine Stage anlegen und uns für einen Namen entscheiden. Wir legen eine leere (empyte) Stage an und wählen die Bezeichnung dev. Da wir planen Shell Skripte auszuführen, ist es aktuell notwendig, aus dem Agent Pool einen Linux Agent zu wählen. Andernfalls kann es zu seltsamen Fehlermeldungen kommen, wie z.B. das File deploy.sh kann nicht gefunden werden.

Setup for SQL Azure Deployment

Nachdem die Stage erstellt wurde, müssen wir einen entsprechenden Azure CLI Task hinzufügen. Dabei ist es unwichtig, ob wir Azure CLI commands direkt absetzen oder Skripte ausführen wollen.

Bevor wir den Task fertig konfigurieren können, sollten wir kurz überlegen, welche Parameter für die Ausführung des Skripts deploy-sql.sh im Ordner SQLAzure notwendig sind. Da wir versuchen möglichst viel über die Pipeline bestimmen zu können, benötigen wir dafür nachfolgende Parameter.

  • Database Server Name (SqlDataBaseServerName)
  • Database Name (SqlDataBaseName)
  • Resource Group Name (ResourceGroupSql)
  • Sql Environment (sqlenv)
  • Sql Admin Password (SqlPassword)

Die Bezeichnungen in den Klammern entsprechen den Variablenbezeichnungen, die wir unter der Rubrik bzw. dem Reiter Variables konfigurieren und bei Ausführung unserer Skripte verwenden können (siehe Abbildung unten). Da ebenfalls die WebApp deployt werden soll, sind die Bezeichnungen ebenfalls dort zu sehen.

Ebenso kann durch die Checkbox am rechten äußeren Rand definiert werden, ob der Wert je Release änderbar ist oder nicht. Das ist natürlich sinnvoll, da wir dann nicht für jedes Feature eine eigene Stage anlegen müssen. Weiter kann das SQL Password als Secret hinterlegt werden.

Nachdem wir nun alle notwenigen Variablen definiert und mit Default Werten versehen haben, springen wir zurück zu unserem Azure CLI Task. Die Option Script Location müssen wir auf Script Path stellen, danach kann unter dem Feld Script Path ein Skript gewählt werden (andernfalls wird ein Inlinescript verlangt). Für unser Sql Beispiel, wählen wir das Skript deploy-sql.sh im Ordner SQLAzure. Abschließend müssen wir unter Arguments folgende Zeile hinzufügen.

-e $(sqlenv) -r $(ResourceGroupSql) -d $(SqlDataBaseName) -p $(SqlPassword) -s $(SqlDataBaseServerName)

Anzumerken ist, dass das Working Directory ebenfalls auf den Ordner SQLAzure gestellt ist. Die komplette Konfiguration des Tasks sollte dann wie folgt aussehen.

Auf Grundlage des Setups könnten wir ein neues Release anlegen, die Variablen, falls gewünscht, ändern und das Deployment starten. Wir werden jedoch erst das Setup für die WebApp fertig stellen, bevor wir deployen.

Setup für Azure WebApp Deployment

Das Setup für die WebApp erfolgt analog zum Setup der SQL Azure Datenbank. Wir müssen einen weiteren Azure CLI Task anlegen, die notwendigen Variablen definieren, das WebApp Shell Skript wählen und die Skript Argumente hinzufügen. Die notwendigen Variablen sind nachfolgend aufgelistet.

  • App Service Plan Name (AppServicePlan)
  • Web App Name (WebAppName)
  • Web App Sku (Sku)

Nachdem wir das Skript deploy-webapp.sh im Ordner WebApp gewählt haben, müssen noch die notwendigen Argumente übergeben werden. Die Zeile sollte dann wie folgt aussehen.

-r $(ResourceGroupWebApp) -d $(SqlDataBaseName) -p $(SqlPassword) 
-f $(AppServicePlan) -a $(WebAppName) -u $(SqlDataBaseServerName)

Verzichten wir auf die Übergabe von bestimmten Argumenten, werden die definierten Default Werte verwendet. Da wir die SQL Credentials schon für das Erzeugen der Datenbank verwendet haben, können wir diese hier ebenfalls für die Übergabe des Connectionsstrings verwenden.

Release deployen

Nachdem beide Tasks angelegt wurden, können wir ein neues Release erstellen und deployen. Konnte der Vorgang erfolgreich abgeschlossen werden, können wir im Azure Portal beide Resource Groups genauer betrachten. Wichtig hierbei ist, dass der hinterlegte Connectionstring in der WebApp mit den Credentials der Datenbank übereinstimmt.

Variablen ändern

Bisher haben wir mit dem eben erstellten Release lediglich die gleichen Ressourcen angelegt, die wir auch ohne die Verwendung von Azure DevOPs erzeugen konnten. Weiter oben wurde kurz erwähnt, dass Variablen ebenfalls je Release geändert werden können. Möchten wir für die Entwicklung eines neuen Features Feature XYZ eine neue Infrastruktur anlegen, benötigen wir genau zwei Dinge: eine neue Datenbank und eine weitere WebApp. Alle anderen Ressourcen und Resource Groups können und sollten weiter bestehen bleiben.

Wir müssen somit lediglich ein neues Release erstellen und die notwendigen Ressourcen erzeugen. Bevor ein Deployment startet, können wir die Variablen ändern, die pro Release als änderbar definiert wurden. Für unser FeatureXYZ könnte es wie folgt aussehen.

Starten wir nun das Deployment, sollten wir nach erfolgreichem Abschluss die neuen Ressourcen innerhalb der Resource Groups sehen. Ebenfalls muss der Connectionstring, welcher in den App Settings der neuen App ProcyWebAppXYZ hinterlegt ist, zu der eben erstellten Datenbank passen.

Weiteres Environment

Benötigen wir ein ganz neues Environment z.B. für einen Kunden, der Feature testen möchte, dann kann das erreicht werden, indem wir die aktuelle Stage klonen. Nachdem wird die Stage geklont haben, vergeben wir einen neuen Namen (new-customer). Weiter möchten wir nicht, dass das Deployment automatisch mit einem neuen Release beginnt. Dafür müssen wir die Pre-Deployment condition auf manuell stellen.

Für ein neues Environment müssen wir mindestens die nachfolgenden Variablen neu definieren, da diese eindeutig sein müssen. Da wir das nicht für jedes Release machen möchten, werden wir das über die Variablendefinition ändern.

  • ResourceGroupSql
  • ResourceGroupWebApp
  • SqlDataBaseServerName
  • SqlPassword
  • WebAppName

Dafür springen wir zum Tab Variables und legen die oben aufgelisteten Variablen erneut an. Für jede Variable müssen wir den Scope (rechts) ändern, setzen diesen auf new-customer.

Dadurch wird erreicht, dass beim Deployen der Stage new-customer immer genau die definierten Werte genommen werden. Das Ergebnis sollte dann wie folgt aussehen.

Bei der Fülle an Variablen kann dieser Weg schnell unübersichtlich werden. Dann wäre es wahrscheinlich besser, einfach einen Prefix bzw. Suffix oder ähnliches zu definieren, der je Environment also Scope Variable gesetzt wird.

Erzeugen wir nun ein neues Release und deployen die new-customer Stage, sollten wir zwei neue Resource Groups inkl. neuen Ressourcen finden. Auch hierbei sollte der Connectionstring in den App Settings geprüft werden. Die URL muss zum neu erstellen Datenbankserver passen.

Durch das Erzeugen einer neuen Stage und die Verwendung der Scopes in der Variablendefinition kann sehr schnell ein komplett neues Environment aufgesetzt werden. Auch dieses kann als Grundlage zur Feature Entwicklung dienen.

Anmerkung

Mit einer leeren Datenbank, lässt sich natürlich nur schwer etwas präsentieren bzw. reviewen. Die Software kann zwar schnell in der Web App deployed werden, die Datenbank würde nicht zur App passen. In bestimmten Fällen muss man auf ein Backup zurück greifen. Wir verwenden dafür Migrationsskripte, die ausgeführt werden müssen und anschließend noch ein paar Beispieldatensätze anlegen.

Für die Ausführung dieser Skripte, würden wir wieder den Connectionstring benötigen, was aktuell nur durch Kopieren möglich wäre. Wir verwenden für solche Szenarien einen oder mehrere Azure Key Vaults, deren Secrets dann als Variable Group’s in Azue Dev Ops eingebunden werden. Dadurch muss das Passwort nur einmal angegeben und kann in verschiedenen Piplines verwendet werden.

Zusammenfassung

In diesem Beitrag konnten wir auf Grundlage unserer Deployment Skripte den Automatisierungsgrad dank Azure DevOps weiter erhöhen. Wir konnten sowohl neue Services für die Feature Entwicklung als auch komplett neue Umgebungen erzeugen. Solche Umgebungen könnten beispielsweise für Lasttests, indem die produktionsnahen Sku’s gewählt werden, dienen. Ebenfalls können sie zu Präsentationszwecken verwendet werden.


Azure ARM und Azure CLI in der Praxis

In diesem Beitrag möchten wir kurz aufzeigen wie einfach es ist, Enwicklungs- und Testumgebungen mit Azure ARM Templates und Azure CLI aufzusetzen. Dadurch wird es möglich, autarke Umgebungen zur Featureentwicklung zu erzeugen ohne mit parallelen Entwicklungsarbeiten zu kollidieren.

Dafür haben wir Auszüge unserer Procy Infrastruktur Skripte auf GitHub bereit gestellt. Das Procy Backend besteht aus verschiedenen Services sowie einer SQL Server Datenbank. Die Services werden in einer Azure Web App deployed und die Datenbank in einer SQL Azure Instanz angelegt. Beide Services sind Azure bereitgestellte PaaS Angebote, so dass unserer Focus auf der Verwendung und nicht der Konfiguration dieser Services liegt. Um den Beispielen folgen zu können, benötigt man einen Azure Account mit gültiger Subscription. Außerdem müssen die Azure CLI Tools installiert sein.


SQL Azure

Zum Erzeugen der SQL Azure Umgebung greifen wir auf ein Azure Resource Manager (ARM) template zurück. In solch einem Template können mehrere Ressourcen mit individuellen Einstellungen definiert werden. Unter SQL Azure sind alle Ressourcen definiert, die wir für unsere Umgebung benötigen. Der Output umfasst für uns wichtige Ressourcen wie SQL server und SQL database. Diese beiden Ressourcen werden zu einer Ressource Group hinzugefügt.

Außerdem ist es möglich, Parameter in separaten Files zu hinterlegen, wobei hier die SKU, Leistung, Name usw. geändert werden können. Diese Parameter können dann ebenfalls während des Deployments überschrieben werden. Das Deployment kann mit dem az group deployment create command gestartet werden.

Für unser spezielles Beispiel müssen wir mindestens folgendes Command absetzen, um das Deployment zu triggern. Vorher sollte man sich mit az login angemeldet sowie die resource group erstellt haben.

Erstellen einer Resource Group

az group create 
--name myResourceGroup 
--location myResourceGroupLocation

Erstellen der SQL Azure Umgebung

az group deployment create  
--resource-group myResourceGroup
--template-file azuredeploy.json
--parameters @basic.parameters.json
--parameters sqlserveradminpw=myPassword

Dabei muss zwingend die Resource Group sowie ein admin password für den SQL server angegeben werden. Außerdem ist im Beispiel nur ein Paramters File hinterlegt, welches angegeben werden muss, da keine default Werte hinterlegt sind.

Werden beide oben dargestellten Befehle für die Ressourcengruppe sql-example-basic-rg ausgeführt, sollten wir im Azure Portal die Ressourcengruppe samt beider Resourcen sehen können. Basic im rg Namen bezieht sich hierbei auf das Prameterfile bzw. die Stage, die damit erzeugt wurde.

Benötigen wir für ein Feature FeatureXYZ eine weitere Datenbank mit gleichen Zugangsdaten, dann können wir einfach einen anderen Datenbanknamen wählen und Skript erneut ausführen. Im konkreten Beispiel können wir für die Datenbank ProcyFeatureXYZ folgenden Command absetzen.

az group deployment create  
--resource-group blog-example-basic-rg  
--template-file azuredeploy.json  
--parameters @basic.parameters.json  
--parameters database_name=ProcyDBFeatureXYZ

Werfen wir wieder einen Blick auf unsere Resource Group, ist nun eine weitere Datenbank zu sehen, welche mit den gleichen Credentials verwendet werden kann. Einzig der Name der Datenbank muss im Connectionstring getauscht werden.

Das wir kein Passwort übergeben mussten, ist der Condition(al) Property im azuredeploy.json file geschuldet. Wird kein Passwort übergeben, wird das Deployment des Servers nicht beachtet, was in unserem Fall einer weiteren Datenbank auch nicht notwendig ist.

"condition": "[greater(length(parameters('sqlserveradminpw')) , 0)]",

Das Deployment der Datenbank kann auch über Azure CLI commands durchgeführt werden. Das ist ausreichend, falls sich die Menge an Konfigurationen in Grenzen hält.

az sql db create 
-g sql-example-basic-rg 
-s procydbserver 
-n ProcyDBFeatureXYZ 
--service-objective Basic 
--catalog-collation SQL_Latin1_General_CP1_CI_AS

Möchte man Auditing, Azure Advisor, Firewallsettings usw. für alle Datenbanken verwenden, empfiehlt sich die Verwendung des ARM templates wie weiter oben beschrieben. Dadurch wird sicher gestellt, dass alle Datenbanken auf den gleichen Settings basieren.


Azure Web App

Um die notwendigen Backendservices zu deployen, greifen wir auf den Azure Service Azure Web App zurück. Dafür muss ein App Service Plan ausgewählt werden, unter dem die einzelnen Apps erstellt werden können. Solange Apps zu einem App Service Plan gehören, werden die Ressourcen geteilt. Im Gegensatz zum SQL Deployment, bestimmen wir hier die Leistung anhand des übergeordneten App Service Plans und können es nicht je App konfigurieren. Dafür können verschiedene Entwicklungssysteme aufgesetzt werden, wobei nur für einen App Service Plan bezahlt werden muss.

Für unseren Zweck genügen wenige Einstellungen, so dass wir auf die Verwendung der ARM Templates verzichten und alles mit Hilfe der Azure CLI deployen können. Im Infrastructure Repository sind alle notwendigen Commands im File deploy-webapp.sh im Ordner WebApp zu finden.  

Auch hier sollten wir mit einer separaten Resource Group beginnen, einen App Service Plan und letztendlich die zugehörige App erstellen. Durch die Verwendung von Resource Gruppen erhalten wir eine einfache Kostenübersicht und sind in der Lage alle Ressourcen zu löschen.

az group create  --name webapp-example-rg --location "West Europe"

az appservice plan create --name webapp-example-plan --resource-group webapp-example-rg --sku F1

Auf Grundlage des erstellten Service Plans, kann nun die erste App angelegt werden. Da wir in diesem Beispiel keine weiteren Settings, wie Programmiersprache, Runtime usw. benötigen, genügt ebenfalls ein einfacher Azure CLI command.

az webapp create --name webapp-example-app --resource-group webapp-example-rg --plan webapp-example-plan

Nachdem absetzen der Commands sollte im Portal folgendes zu sehen sein. In der Resource Group sind sowohl die App als auch der App Service Plan als eigenständige Ressource zu sehen.

Bisher konnten wir sowohl die Infrastruktur für die Datenbank als auch das Backend provisionieren. Was aktuell fehlt, ist die Verbidnung zwischen den beiden Services. Dafür müssen wir den Connection string zur Datenbank setzen. Das können wir entweder manuell vornehmen oder auch über die Azure CLI abbilden.

az webapp config connection-string set -g webapp-example-rg -n webapp-example-app -t SQLAzure --settings ProcyDbConnectionString="yourConnectionString"

Unsere Anwendung sucht konkret in den Appsettings bzw. unten den Environment Variablen nach dem ProcyDbConnectionString. Der Bezeichner kann natürlich frei gewählt werden und muss mit der App übereinstimmen.

Staging Slots für Produktivbetrieb

Da es sich lediglich um Entwicklungs- und Testumgebungen handelt, werden keine Staging Slots verwendet. Für den Produktivbetrieb sollten jedoch wenigstens zwei Slots verwendet werden. Einen für dir produktiven Betrieb und einen für das letzte stabile System als Backup. Dadurch können Rollbacks einfach durchgeführt werden.


Orchestrierung durch Skripte

Um die ganzen Commands nicht alle manuell ausführen zu müssen, haben wir diese in ein einfaches Shell Skript gepackt. In diesem Skript wird ebenfalls der Connectionstring zusammen gebaut und gesetzt. Die Beispiele sind stark Procy (ursprüngliche App) lastig, was bedeutet, dass diese Bezeichner in den Skripten umbenannt werden müssten.

Weiter ist es notwendig, sowohl für das SQL Azure Skript als auch für das WebApp Skript die Parameter DatabaseServer, DatabaseName und SQLAdminPassword zu übergeben. Der Datanbankbenutzer (User) kann aktuell nicht geändert werden! Für lokale Zwecke könnte ein weiteres Shell Skript verwendet werden, welches die beiden Skripte nacheinander aufruft und so die komplette Infratsruktur erzeugt. Dabei wäre die Eingabe bestimmter Parameter nur einmalig notwendig. Wir verwenden dafür jedoch AzureDevOps.

Zusammenfassung

In diesem Beitrag konnten wir aufzeigen, wie einfach Services in Azure provisioniert werden können. Durch die Verwendung von Azure ARM templates sowie der Azure CLI können solche Prozesse stark automatisiert und vereinheitlicht werden.

Durch die Verwendung und Zuordnung von Resource Group’s erhalten wir zum Einen eine einfache Übersicht aller Kosten je Gruppe und zum Anderen können die Ressourcen leicht entfernt werden, sobald der Kunde beispielsweise den Test des Features abgeschlossen hat. Somit fallen keine unnötigen Kosten an.

Des Weiteren kann das Thema role-based access control (RBAC) in der Praxis sehr wichtig werden, wobei Resource Groups ein wichtige Rolle spielen. Für unser Beispiel benötigen wir es nicht zwingend, da der Zugriff auf die spezifischen Azure Resourcen nicht freigegeben wird, sondern der Fokus ausschließlich auf dem Testen der deployten Software liegt.

Im nächsten Beitrag werden wir aufzeigen, welche Rolle AzureDevOps, ehemals Visual Studio Team Services (VSTS), dabei spielen kann, eine komplett neue Infrastruktur für einen neuen Kunden aufsetzen zu lassen.