{"id":258,"date":"2019-03-24T20:04:35","date_gmt":"2019-03-24T20:04:35","guid":{"rendered":"http:\/\/tachi-it.com\/?p=258"},"modified":"2019-03-24T20:04:36","modified_gmt":"2019-03-24T20:04:36","slug":"infrastructure-as-code-mit-azure-devops","status":"publish","type":"post","link":"https:\/\/tachi-it.com\/infrastructure-as-code-mit-azure-devops\/","title":{"rendered":"Infrastructure as Code mit Azure DevOps"},"content":{"rendered":"\n
Im letzten Beitrag<\/a> haben wir kurz vorgestellt, wie wir Infrastruktur in Azure via Azure ARM und Azure CLI bereitstellen k\u00f6nnen. Dieses Vorgehen wird als Infrastructure as Code (IaC<\/a>) bezeichnet. <\/p>\n\n\n\n Dabei mussten wir feststellen, dass trotz einheitlicher Skripte bestimmte Parameter mehrfach angegeben werden m\u00fcssen. Gerade die Eingabe des SQL Admin Passworts ist umst\u00e4ndlich, fehleranf\u00e4llig und f\u00fcr den Produktivbetrieb letztendlich ungeeignet. Dar\u00fcber hinaus ben\u00f6tigt man zum Ausf\u00fchren der Skripte die erforderlichen Rechte.<\/p>\n\n\n\n Aus diesem Grund nutzen wir Azure DevOps<\/a>, fr\u00fcher bekannt als Visual Studio Online (VSO) oder auch Visual Studio Team Service (VSTS). Azure DevOps hat neben verschiedenen Pipeline Arten ….<\/p>\n\n\n\n Wir werden f\u00fcr unser Beispiel einzig die Release Pipelines<\/a> verwenden. Diese Art beruht urspr\u00fcnglich auf vorangegangenen Build Pipelines und deren Artefakte. Vereinfacht werden wir direkt auf das entsprechende GitHub<\/a> zugreifen und dessen Skripte nutzen.<\/p>\n\n\n\n Daf\u00fcr Links die Option Artifacts –> Add<\/em> w\u00e4hlen, dann das entsprechende GitHub Repository w\u00e4hlen. Weiter muss ein Default Branch gew\u00e4hlt werden. Dieser ist wichtig, um in nachfolgenden Schritten durch das Repository navigieren zu k\u00f6nnen, kann jedoch f\u00fcr jedes Release ge\u00e4ndert werden.<\/p>\n\n\n\n Ebenfalls ist es m\u00f6glich, einen Continuous Deployment (CD)<\/em> Trigger zu installieren. Ob es f\u00fcr IaC sinnvoll ist, ist immer abh\u00e4ngig von den konkreten Anforderrungen an die IaC Skripte. Es kann durchaus Sinn ergeben, dass w\u00e4hrend der Entwicklung der Skripte, regelm\u00e4\u00dfiges Ausf\u00fchren zur besseren Qualit\u00e4t beitragen kann, um Fehler fr\u00fchzeitig aufdecken zu k\u00f6nnen.<\/p>\n\n\n\n Zuerst m\u00fcssen wir eine Stage anlegen und uns f\u00fcr einen Namen entscheiden. Wir legen eine leere (empyte) Stage an und w\u00e4hlen die Bezeichnung dev<\/strong>. Da wir planen Shell Skripte auszuf\u00fchren, ist es aktuell notwendig, aus dem Agent Pool einen Linux Agent zu w\u00e4hlen. Andernfalls kann es zu seltsamen Fehlermeldungen kommen, wie z.B. das File deploy.sh<\/em> kann nicht gefunden werden.<\/p>\n\n\n Nachdem die Stage erstellt wurde, m\u00fcssen wir einen entsprechenden Azure CLI Task hinzuf\u00fcgen. Dabei ist es unwichtig, ob wir Azure CLI commands direkt absetzen oder Skripte ausf\u00fchren wollen.<\/p>\n\n\n Bevor wir den Task fertig konfigurieren k\u00f6nnen, sollten wir kurz \u00fcberlegen, welche Parameter f\u00fcr die Ausf\u00fchrung des Skripts deploy-sql.sh<\/em> im Ordner SQLAzure<\/em> notwendig sind. Da wir versuchen m\u00f6glichst viel \u00fcber die Pipeline bestimmen zu k\u00f6nnen, ben\u00f6tigen wir daf\u00fcr nachfolgende Parameter.<\/p>\n\n\n\n Die Bezeichnungen in den Klammern entsprechen den Variablenbezeichnungen, die wir unter der Rubrik bzw. dem Reiter Variables<\/em> konfigurieren und bei Ausf\u00fchrung unserer Skripte verwenden k\u00f6nnen (siehe Abbildung unten). Da ebenfalls die WebApp deployt werden soll, sind die Bezeichnungen ebenfalls dort zu sehen. <\/p>\n\n\n\n Ebenso kann durch die Checkbox am rechten \u00e4u\u00dferen Rand definiert werden, ob der Wert je Release \u00e4nderbar ist oder nicht. Das ist nat\u00fcrlich sinnvoll, da wir dann nicht f\u00fcr jedes Feature eine eigene Stage anlegen m\u00fcssen. Weiter kann das SQL Password als Secret hinterlegt werden.<\/p>\n\n\n\n Nachdem wir nun alle notwenigen Variablen definiert und mit Default Werten versehen haben, springen wir zur\u00fcck zu unserem Azure CLI Task. Die Option Script Location<\/em> m\u00fcssen wir auf Script Path<\/em> stellen, danach kann unter dem Feld Script Path<\/em> ein Skript gew\u00e4hlt werden (andernfalls wird ein Inlinescript verlangt). F\u00fcr unser Sql Beispiel, w\u00e4hlen wir das Skript deploy-sql.sh<\/em> im Ordner SQLAzure<\/em>. Abschlie\u00dfend m\u00fcssen wir unter Arguments<\/em> folgende Zeile hinzuf\u00fcgen.<\/p>\n\n\n\n Anzumerken ist, dass das Working Directory ebenfalls auf den Ordner SQLAzure<\/em> gestellt ist. Die komplette Konfiguration des Tasks sollte dann wie folgt aussehen. <\/p>\n\n\n\n Auf Grundlage des Setups k\u00f6nnten wir ein neues Release anlegen, die Variablen, falls gew\u00fcnscht, \u00e4ndern und das Deployment starten. Wir werden jedoch erst das Setup f\u00fcr die WebApp fertig stellen, bevor wir deployen.<\/p>\n\n\n\n Das Setup f\u00fcr die WebApp erfolgt analog zum Setup der SQL Azure Datenbank. Wir m\u00fcssen einen weiteren Azure CLI Task anlegen, die notwendigen Variablen definieren, das WebApp Shell Skript w\u00e4hlen und die Skript Argumente hinzuf\u00fcgen. Die notwendigen Variablen sind nachfolgend aufgelistet.<\/p>\n\n\n\n Nachdem wir das Skript deploy-webapp.sh<\/em> im Ordner WebApp<\/em> gew\u00e4hlt haben, m\u00fcssen noch die notwendigen Argumente \u00fcbergeben werden. Die Zeile sollte dann wie folgt aussehen. <\/p>\n\n\n\n Verzichten wir auf die \u00dcbergabe von bestimmten Argumenten, werden die definierten Default Werte verwendet. Da wir die SQL Credentials schon f\u00fcr das Erzeugen der Datenbank verwendet haben, k\u00f6nnen wir diese hier ebenfalls f\u00fcr die \u00dcbergabe des Connectionsstrings verwenden.<\/p>\n\n\n\n Nachdem beide Tasks angelegt wurden, k\u00f6nnen wir ein neues Release erstellen und deployen. Konnte der Vorgang erfolgreich abgeschlossen werden, k\u00f6nnen wir im Azure Portal beide Resource Groups<\/em> genauer betrachten. Wichtig hierbei ist, dass der hinterlegte Connectionstring in der WebApp mit den Credentials der Datenbank \u00fcbereinstimmt. <\/p>\n\n\n\n Bisher haben wir mit dem eben erstellten Release lediglich die gleichen Ressourcen angelegt, die wir auch ohne die Verwendung von Azure DevOPs <\/em>erzeugen konnten. Weiter oben wurde kurz erw\u00e4hnt, dass Variablen ebenfalls je Release ge\u00e4ndert werden k\u00f6nnen. M\u00f6chten wir f\u00fcr die Entwicklung eines neuen Features Feature XYZ<\/em> eine neue Infrastruktur anlegen, ben\u00f6tigen wir genau zwei Dinge: eine neue Datenbank und eine weitere WebApp. Alle anderen Ressourcen und Resource Groups<\/em> k\u00f6nnen und sollten weiter bestehen bleiben. <\/p>\n\n\n\n Wir m\u00fcssen somit lediglich ein neues Release erstellen und die notwendigen Ressourcen erzeugen. Bevor ein Deployment startet, k\u00f6nnen wir die Variablen \u00e4ndern, die pro Release als \u00e4nderbar definiert wurden. F\u00fcr unser FeatureXYZ<\/em> k\u00f6nnte es wie folgt aussehen. <\/p>\n\n\n\n Starten wir nun das Deployment, sollten wir nach erfolgreichem Abschluss die neuen Ressourcen innerhalb der Resource Groups<\/em> sehen. Ebenfalls muss der Connectionstring, welcher in den App Settings der neuen App ProcyWebAppXYZ<\/em> hinterlegt ist, zu der eben erstellten Datenbank passen. <\/p>\n\n\n\n Ben\u00f6tigen wir ein ganz neues Environment z.B. f\u00fcr einen Kunden, der Feature testen m\u00f6chte, dann kann das erreicht werden, indem wir die aktuelle Stage klonen. Nachdem wird die Stage geklont haben, vergeben wir einen neuen Namen (new-customer)<\/em>. Weiter m\u00f6chten wir nicht, dass das Deployment automatisch mit einem neuen Release beginnt. Daf\u00fcr m\u00fcssen wir die Pre-Deployment condition<\/em> auf manuell stellen.<\/p>\n\n\n\n F\u00fcr ein neues Environment m\u00fcssen wir mindestens die nachfolgenden Variablen neu definieren, da diese eindeutig sein m\u00fcssen. Da wir das nicht f\u00fcr jedes Release machen m\u00f6chten, werden wir das \u00fcber die Variablendefinition \u00e4ndern. <\/p>\n\n\n\n Daf\u00fcr springen wir zum Tab Variables<\/em> und legen die oben aufgelisteten Variablen erneut an. F\u00fcr jede Variable m\u00fcssen wir den Scope (rechts) \u00e4ndern, setzen diesen auf new-customer<\/em>. <\/p>\n\n\n\n Dadurch wird erreicht, dass beim Deployen der Stage new-customer<\/em> immer genau die definierten Werte genommen werden. Das Ergebnis sollte dann wie folgt aussehen.<\/p>\n\n\n\n Bei der F\u00fclle an Variablen kann dieser Weg schnell un\u00fcbersichtlich werden. Dann w\u00e4re es wahrscheinlich besser, einfach einen Prefix bzw. Suffix oder \u00e4hnliches zu definieren, der je Environment also Scope Variable gesetzt wird.<\/p>\n\n\n\n Erzeugen wir nun ein neues Release und deployen die new-customer<\/em> Stage, sollten wir zwei neue Resource Groups<\/em> inkl. neuen Ressourcen finden. Auch hierbei sollte der Connectionstring in den App Settings gepr\u00fcft werden. Die URL muss zum neu erstellen Datenbankserver passen.<\/p>\n\n\n\n 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. <\/p>\n\n\n\n Mit einer leeren Datenbank, l\u00e4sst sich nat\u00fcrlich nur schwer etwas pr\u00e4sentieren bzw. reviewen. Die Software kann zwar schnell in der Web App deployed werden, die Datenbank w\u00fcrde nicht zur App passen. In bestimmten F\u00e4llen muss man auf ein Backup zur\u00fcck greifen. Wir verwenden daf\u00fcr Migrationsskripte, die ausgef\u00fchrt werden m\u00fcssen und anschlie\u00dfend noch ein paar Beispieldatens\u00e4tze anlegen. <\/p>\n\n\n\n F\u00fcr die Ausf\u00fchrung dieser Skripte, w\u00fcrden wir wieder den Connectionstring ben\u00f6tigen, was aktuell nur durch Kopieren m\u00f6glich w\u00e4re. Wir verwenden f\u00fcr solche Szenarien einen oder mehrere Azure Key Vaults, deren Secrets dann als Variable Group<\/a><\/em>’s<\/a> in Azue Dev Ops eingebunden werden. Dadurch muss das Passwort nur einmal angegeben und kann in verschiedenen Piplines verwendet werden.<\/p>\n\n\n\n In diesem Beitrag konnten wir auf Grundlage unserer Deployment Skripte den Automatisierungsgrad dank Azure DevOps weiter erh\u00f6hen. Wir konnten sowohl neue Services f\u00fcr die Feature Entwicklung als auch komplett neue Umgebungen erzeugen. Solche Umgebungen k\u00f6nnten beispielsweise f\u00fcr Lasttests, indem die produktionsnahen Sku’s gew\u00e4hlt werden, dienen. Ebenfalls k\u00f6nnen sie zu Pr\u00e4sentationszwecken verwendet werden. <\/p>\n","protected":false},"excerpt":{"rendered":" Im letzten Beitrag haben wir kurz vorgestellt, wie wir Infrastruktur in Azure via Azure ARM und Azure CLI bereitstellen k\u00f6nnen. Dieses Vorgehen wird als Infrastructure as Code (IaC) bezeichnet. Dabei mussten wir feststellen, dass trotz einheitlicher Skripte bestimmte Parameter mehrfach angegeben werden m\u00fcssen. Gerade die Eingabe des SQL Admin Passworts ist umst\u00e4ndlich, fehleranf\u00e4llig und f\u00fcr … Erstellen der Release Pipeline<\/h2>\n\n\n\n
\r\n<\/div>\r\n\r\n<\/div><\/div><\/div>
\r\n<\/div>\r\n\r\n<\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div>\n\n\n
\r\n<\/div>\r\n\r\n<\/div><\/div><\/div>
\r\n<\/div>\r\n\r\n<\/div><\/div><\/div>
\r\n<\/div>\r\n\r\n<\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div><\/div>\n\n\n
\r\n<\/div>\r\n\r\n<\/div><\/div><\/div>
\r\n<\/div>\r\n\r\n<\/div><\/div><\/div>
\r\n<\/div>\r\n\r\n<\/div><\/div><\/div><\/div><\/div>\n\n\n
Setup for SQL Azure Deployment<\/h2>\n\n\n\n
\r\n<\/div>\r\n\r\n<\/div><\/div><\/div><\/div><\/div>\n\n\n
<\/figure>\n\n\n\n
-e $(sqlenv) -r $(ResourceGroupSql) -d $(SqlDataBaseName) -p $(SqlPassword) -s $(SqlDataBaseServerName)<\/pre>\n\n\n\n
\r\n<\/div>\r\n\r\n<\/div><\/div><\/div><\/div><\/div>\n\n\n
Setup f\u00fcr Azure WebApp Deployment<\/h2>\n\n\n\n
-r $(ResourceGroupWebApp) -d $(SqlDataBaseName) -p $(SqlPassword) \n-f $(AppServicePlan) -a $(WebAppName) -u $(SqlDataBaseServerName)<\/pre>\n\n\n\n
Release deployen<\/h2>\n\n\n\n
\r\n<\/div>\r\n\r\n<\/div><\/div>
\r\n<\/div>\r\n\r\n<\/div><\/div><\/div>
\r\n<\/div>\r\n\r\n<\/div><\/div><\/div><\/div><\/div>\n\n\n
Variablen \u00e4ndern<\/h3>\n\n\n\n
\r\n<\/div>\r\n\r\n<\/div><\/div><\/div><\/div><\/div>\n\n\n
\r\n<\/div>\r\n\r\n<\/div><\/div><\/div>
\r\n<\/div>\r\n\r\n<\/div><\/div><\/div><\/div>
\r\n<\/div>\r\n\r\n<\/div><\/div><\/div><\/div><\/div>\n\n\n
Weiteres Environment <\/h3>\n\n\n\n
\r\n<\/div>\r\n\r\n<\/div><\/div><\/div>
\r\n<\/div>\r\n\r\n<\/div><\/div><\/div>
\r\n<\/div>\r\n\r\n<\/div><\/div><\/div><\/div><\/div>\n\n\n
\r\n<\/div>\r\n\r\n<\/div><\/div><\/div><\/div><\/div>\n\n\n
\r\n<\/div>\r\n\r\n<\/div><\/div><\/div><\/div><\/div>\n\n\n
\r\n<\/div>\r\n\r\n<\/div><\/div><\/div>
\r\n<\/div>\r\n\r\n<\/div><\/div><\/div>
\r\n<\/div>\r\n\r\n<\/div><\/div><\/div><\/div>
\r\n<\/div>\r\n\r\n<\/div><\/div><\/div>
\r\n<\/div>\r\n\r\n<\/div><\/div><\/div><\/div><\/div>\n\n\n
Anmerkung<\/h3>\n\n\n\n
Zusammenfassung<\/h2>\n\n\n\n
Weiterlesen<\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[17,104,20,111],"tags":[109,107],"_links":{"self":[{"href":"https:\/\/tachi-it.com\/wp-json\/wp\/v2\/posts\/258"}],"collection":[{"href":"https:\/\/tachi-it.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tachi-it.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tachi-it.com\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/tachi-it.com\/wp-json\/wp\/v2\/comments?post=258"}],"version-history":[{"count":59,"href":"https:\/\/tachi-it.com\/wp-json\/wp\/v2\/posts\/258\/revisions"}],"predecessor-version":[{"id":351,"href":"https:\/\/tachi-it.com\/wp-json\/wp\/v2\/posts\/258\/revisions\/351"}],"wp:attachment":[{"href":"https:\/\/tachi-it.com\/wp-json\/wp\/v2\/media?parent=258"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tachi-it.com\/wp-json\/wp\/v2\/categories?post=258"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tachi-it.com\/wp-json\/wp\/v2\/tags?post=258"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}