Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Lesedauer 30 Min.

Infrastructure-as-Code mit Terraform

Terraform erstellt und verwaltet Infrastrukturen. Die Software legt besonderen Wert auf die Automatisierung aller DevOps-Tätigkeiten mittels Infrastruktur-als-Code (IaC).
Terraform, ein DevOps-Tool der Firma HashiCorp, unterstützt die Definition, Erstellung, Änderung und Verwaltung von Services und Ressourcen für eine Vielzahl von Infrastruktur-Lösungen. Dazu zählen alle gängigen Cloud-Plattformen wie Alibaba Cloud, AWS (Amazon Web Services), Google Cloud Platform (GCP), Microsoft Azure oder die Oracle Cloud Platform/Infrastructure (OCP/OCI). Dabei deckt Terraform sämtliche Teilbereiche des Cloud-Computings von Infrastructure-as-a-Service (IaaS), Platform-as-a-Service (PaaS) bis Software-as-a-Service (SaaS) ab.Ergänzend bindet Terraform auch Container-Lösungen (wie Docker oder Kubernetes (K8s)) und Software-Installations/Konfigurations-Werkzeuge (wie Ansible, Chef oder Puppet) ein. Seitens des Kunden spielt es grundsätzlich keine Rolle, ob er die gewünschten Services in der Public-Cloud oder als Vor-Ort-Service (On-Promise) auf eigenen Rechenzentren nutzt (Bild 1).
HashiCorp deckt mit Terraformund der Einbindung weiterer Standardtools eine große Bandbreite an Infrastruktur-Elementen/Services sowohl in der Cloud als auch On-Promise (Vor-Ort) beim Kunden ab(Bild 1) © Simon
Auch die Verbindung mehrerer Cloud-Dienste zu einer gemeinsamen Cloud, einer sogenannten Multi-Cloud, stellt für Terraform kein Hindernis dar. So unterstützt Terraform das cloud-init-System den De-Facto-Standard für Linux-basierte Betriebssysteme mit allen dazu verfügbaren Ausführungsumgebungen in der Public und Private Cloud.

Terraform erschließt Infrastruktur-als-Code-Paradigma der DevOps-Welt

Infrastructure-as-Code (Infrastruktur-als-Code), als Akronym: IaC, rückt analog der Programmierung einer Anwendung den Quellcode, das heißt die Codierung einer Infrastruktur für die DevOps-Tätigkeiten in den Mittelpunkt. Vergleichbar einer automatischen Installation zusammen mit der Einstellung/Konfiguration einer Anwendung oder eines Betriebssystems durch Werkzeuge (wie Ansible, Chef oder Puppet) erfolgt bei Terraform mit IaC die Bereitstellung von Infrastruktur mittels Spezifikation/Quellcode.Das Paradigma IaC ermöglicht DevOps-Mitarbeitern, alle manuellen Aufgaben zu automatisieren. Dabei erfolgt die Spezifikation der Bereitstellung, Änderung/Wartung und Verwaltung von Infrastruktur bei IaC durch die Programmierung von Code. Anstatt über die Oberfläche/GUI der zugrundeliegenden Systemsoftware eine Infrastruktur anzulegen, erledigt diese Tätigkeit kein DevOps-Mitarbeiter mehr von Hand, vielmehr führt ein individuelles Programm/Script sie automatisch durch.

Terraform-Software besitzt Idempotenz

Terraform bietet seinen Anwendern eine wichtige software-technische Eigenschaft die Idempotenz. Eine Software gilt als idempotent, wenn ihre mehrfache Ausführung immer zum selben Ergebnis führt, gänzlich unabhängig davon wie oft man das Programm wiederholt.
Eine Software wie Terraform liest den zugehörigen Quellcode, auch Konfigurationsdatei genannt, und erzeugt daraus die passende Infrastruktur. IaC überträgt die Praktiken der Software-Entwicklung von Anwendungen (die Codierung/Spezifikation) auf die Bereitstellung von Infrastruktur in den DevOps-Bereich. IaC automatisiert die Bereitstellung, Änderung, Wartung und Verwaltung von Systemen, deren Komponenten und ihre Konfiguration.Analog einem Compiler bei der Programmierung erzeugt Terraform aus dem Quellcode die spezifizierte Infrastruktur. Somit macht Terraform (das IaC-Tool) alle Arbeiten im DevOps-Bereich eindeutig, konsistent und wiederholbar. Bringt man Entwickler und DevOps-Mitarbeiter bereits zu einem frühen Zeitpunkt der Anwendungsspezifikation zusammen, so führt dies zu einer effizienteren und produktiveren Zusammenarbeit im Team.Eine Codierung der Infrastruktur (IaC) im DevOps-Bereich erhöht die Kernkompetenzen des gesamten Entwicklungsteams, vor allem verbessern sich Agilität und Disziplin, verbunden mit Klarheit und Wiederholbarkeit. Aus einer frühzeitigen Zusammenarbeit und dem Einsatz von IaC resultiert auch eine Kostenersparnis – sogar in den späteren Phasen der Produktlebenszeit. Gleichzeitig erhöht sich die Geschwindigkeit, mit der das gesamte Entwicklungsteam Änderungen in den Anwendungen sicher bereitstellen kann.

Terraform realisiert IaC mittels deklarativer Programmierung

Wie bei der Programmierung von Quellcode gibt es für IaC zwei verschieden Möglichkeiten der Realisierung: den imperativen und den deklarativen Ansatz. Beim imperativen Ansatz definiert der DevOps-Mitarbeiter, wie die Infrastruktur konfiguriert sein soll. Dabei beschreibt er durch welche Aktivitäten, das heißt auf welche Art und Weise, das eingesetzte DevOps-Tool die gewünschten Infrastruktur-Elemente/Services erzeugt.Damit entspricht der imperative Ansatz einer verfahrensorientierten Beschreibung – anhand verschiedener Befehle des DevOps-Tools entsteht eine bestimmte Konfiguration der Infrastruktur. Die Befehle müssen dabei nacheinander in der angegebenen Reihenfolge ausgeführt werden, um zum jeweiligen Ergebnis zu kommen. Die einzelnen Arbeitsschritte bauen in der Regel aufeinander auf.Eine derart verfahrensorientierte Beschreibung bildet nicht nur die Grundlage für die Bereitstellung, sondern ist im Falle von Änderungen in der Infrastruktur auch für diese erforderlich. Dadurch fallen zusätzliche mentale Arbeiten beim DevOps-Mitarbeiter an, da dieser die Befehle spezifiziert. Der DevOps-Mitarbeiter selbst muss sich im Falle eines imperativen IaC-Tools überlegen, durch welche Schritte er diese Änderungen erreicht.Dazu muss der DevOps-Mitarbeiter die passenden Befehle des IaC-Tools kennen, um diese Änderungen anhand von einzelne Verarbeitungsschritten umzusetzen. Auch für Änderungen muss die Reihenfolge der Befehle eingehalten und strikt befolgt werden. Damit entspricht der imperative Ansatz immer einer an für sich starren Handlungsanweisung, die den Weg zum Ziel beschreibt.

CLI-Vervollständigung für Bash oder Zsh einrichten

Für die beiden Unix-Shells Bash oder Zsh bietet HashiCorp eine automatische Vervollständigung der Terraform CLI-Befehle im Terminalfenster an. Generell muss die automatische Vervollständigung für die gewünschte Shell über die Tabulatortaste mit der Befehlsfolge autoload -Uz und compinit aktiviert sein. Ebenfalls muss das Konfigurationsfile der Shell dem Benutzer zugänglich sein. Der Unix touch-Befehl erstellt diese im aktuellen Benutzerverzeichnis: touch ~/.bashrc oder touch ~/.zshrc.
Im Unterschied dazu definiert beim deklarativen Ansatz der DevOps-Mitarbeiter primär das Ziel, das heißt den gewünschten Zustand der Infrastruktur. Durch welche Aktionen das IaC-Tool den Zielzustand erreicht – sprich welcher Weg dafür einzuschlagen ist – bleibt außen vor. Damit legt der deklarative Ansatz seinen Schwerpunkt auf der Beschreibung des Ergebnisses. Der DevOps-Mitarbeiter spezifiziert die notwendige Infrastruktur mit den geforderten Eigenschaften. Die IaC-Software erstellt die gewünschten Elemente und Services der Infrastruktur automatisch, indem es die notwendigen Schritte zum Ziel selbständig ermittelt. Analog verfährt der deklarative Ansatz mit Änderungen: Der DevOps-Mitarbeiter formuliert das benötigte Ergebnis in dem er den Sollzustand beschreibt. Anschließend ermittelt das IaC-Tool selbst aus dem vorhandenen Anfangszustand der Infrastruktur, die erforderlichen Schritte, um den gewünschten Endzustand zu erreichen.

HashiCorp liefert Terraform in verschiedenen Editions aus

Obwohl Terraform in Version 1 erst Mitte letzten Jahres allgemein verfügbar wurde, hat HashiCorp mit Hochdruck an verschiedenen Editions des DevOps-Tools gearbeitet. Was allerdings nicht bedeutet, dass Terraform erst seit kurzem bei den Anwendern im produktiven Betrieb ist. Vielmehr arbeitet HashiCorp seit 2014 mit einer Reihe ausgewählter Anwender eng zusammen. Diese stammen aus den Bereichen Handel (Starbucks), Finanzen (TMX Group, Deutsche Börse Group) und der Industrie (Cruise). Ergänzend hat das Software-Haus Terraform als Open-Source-Tool nahezu zeitgleich über ein GitHub-Repository zur Verfügung gestellt.Durch diesen breiten Einsatz stellt HashiCorp nach wie vor sicher, dass Anforderungen für den produktiven Betrieb von Terraform sowohl seitens der Industrie als auch aus der Open-Source-Welt kontinuierlich in die Software einfließen. Durch dieses Release-Management besitzt Version 1.0 alle wesentlichen Anforderungen für die Praxis. Inzwischen kann man Terraform als ein reifes und stabiles Produkt ansehen, das bereits in großer Anzahl bei Anwendern im produktiven Betrieb ist. Die Produkt-Homepage von Terraform bietet einen Überblick zu ausgewählten Anwendern aus der betrieblichen Praxis (Bild 2).
Die Produkt-Homepagevon Terraform listet ausgewählte Anwender mit ihren zentralen Aussagen auf(Bild 2) © Simon
Stand heute liefert HashiCorp Terraform in verschiedenen Produkt-Editions mit unterschiedlichem Funktionsumfang aus. Neben der Open-Source-Software (Terraform OSS/Terraform CLI) gibt es drei Terraform Cloud-Versionen (Free, Business, Team & Governance) als SaaS und ergänzend eine Enterprise-Edition:Auf der Homepage von Terraform öffnet ein Klick auf die Schaltfläche Download CLI die Webseite mit Anleitungen zur Installation des Terraform-CLIs. Dort befinden sich Links zum Herunterladen der Binärversion des Programms für macOS, Windows sowie FreeBSD, OpenBSD und Solaris. Für die Linux-Derivate: Ubuntu/Debian, CentOS/RHEL, Fedora oder Amazon Linux erfolgt die Installation über das jeweilige Code-Repository mit dem zugehörigen Package-Manager.Ergänzend unterstützt HashiCorp für macOS auch eine Installation über die Homebrew-Paketverwaltung. Für Windows findet man im Repository des Chocolatey-Paketmanagers die OSS-Version von Terraform. Allerdings stammt dieses Terraform-Paket nicht von HashiCorp sondern von der Chocolatey-Community. Das von HashiCorp bereitgestellte GitHub-Repository ermöglicht eine Installation über den Quellcode; jedoch benötigt man dazu Go-Kenntnisse. Zusätzlich macht der untere Bereich der Download-Seite über die Auswahlliste Releases die aktuelle erzeugte oder alternativ die letzte als stabil freigegebene Version verfügbar.Nach dem Entpacken der ZIP-Datei mit der Binärversion des Terraform-CLIs empfiehlt es sich, das Programm in ein neues Verzeichnis zum Beispiel tf-home zu kopieren. Anschließend macht man diesen Ordner über den Pfad des Betriebssystems zugänglich. Öffnet man danach ein Terminalfenster und gibt den Befehl terraform version ein, so gibt das Terraform-CLI die Versionsnummer des neu installierten Programms aus (Bild 3). Über ein Terminalfenster stehen alle CLI-Befehle von Terraform OSS zur Verfügung.
Ein Terminalfenstermacht das Terraform-CLI zugänglich; das CLI besitzt ein eigenes Hilfesystem, das sich durch das help-Kommando öffnet(Bild 3) © Simon
Das Hilfesystem des Terraform-CLIs erreicht man mittels des Befehls terraform -help. Erläuterungen zu den verfügbaren Befehlen erhält man über dieses Hilfesystem, so gibt terraform -help init eine Hilfestellung für den Einsatz des init-Befehls aus. Das Hilfesystem zeigt den Aufbau, das heißt die Syntax jedes CLI-Befehls auf und erläutert alle für den gewünschten Befehl verfügbaren Optionen, Unterkommandos und Argumente. Neben init gehören validate, plan, apply und destroy zu den wichtigsten CLI-Befehlen. Das Terraform-Hilfesystem besitzt einen strukturierten Aufbau und hat diese zentralen CLI-Befehle in die Gruppe Main commands aufgenommen.

VS Code/VSCodium mit Terraform-Features anreichern

Als Programmierumgebung für Terraform empfiehlt sich der Einsatz von Visual Studio Code/VSCodium, da diese IDE die Terraform-Language über eine Extension unterstützt. Bei der Terraform-Language handelt es sich um einen auf Terraform zugeschnittenen Dialekt der HashiCorp Configuration Language (HCL – siehe Teil 1). Öffnet man in VS Code/VSCodium den Marketplace und gibt dort als Suchkriterium Terraform an, so erscheinen eine Fülle von Extensions. Die von HashiCorp entwickelte Extension trägt die Bezeichnung HashiCorp Terraform; sie bietet Syntax-Highlighting und automatische Code-Vervollständigung.Im offiziellen Marketplace von VS Code befinden sich die meisten Extensions für Terraform. So findet man dort zusätzlich eine von Microsoft für Azure entwickelte Extension Azure Terraform, die derzeit nur als Preview verfügbar ist. Zusätzlich gibt es noch Extension wie Terraform doc snipptes oder Advanced Terraform Snippets Generator. Für die zuletzt genannte Extension findet aktuell keine Weiterentwicklung mehr statt, daher sollte man primär die anderen nutzen.Möchte man möglichst unabhängig von der eingesetzten Cloud-Plattform programmieren, empfiehlt sich der Einsatz von der HashiCorp Terraform-Extension. Als Vorteil erweist sich Terraform doc snipptes, da diese Extension direkt die Ressourcen der HashiCorp-Dokumentation nutzt und die Quellcode-Beispiele von dort bezieht. In einer weiteren Extension Terraform (forked) von Frederic Lavigne findet man eine Schnittstelle für die IBM Cloud.Für die Vervollständigung von Terraform-Moduln eignet sich Terraform Completer. Dabei handelt es sich um einen Fork von Terraform Autocomplete. Diese Extension eignet sich besonders im Umfeld von AWS. Terraform Autocomplete bietet eine Autovervollständigung von AWS Resource Types, Resource Arguments und für Variable.

Hochverfügbarkeit in der Cloud sicherstellen

Zielsetzung jeder Cloud-Plattform ist es, für ihre Anwender Hochverfügbarkeit sicherzustellen. Für eine Umsetzung der Hochverfügbarkeit gibt es verschiedene Strategien – als die beiden wichtigsten sind Verteilung, Redundanz und Resilienz anzusehen. Verteilung erhöht die Verfügbarkeit der Systeme, da die gesamte Last mehrere Komponenten übernehmen. Wodurch die Fehleranfälligkeit insgesamt sinkt. Reine Redundanz führt immer zu einer Vervielfachung der Kosten für die jeweilige Cloud-Plattform. Mit Resilienz will man diesem Kostenmultiplikator entgegenwirken: Bei Störungen sollen nur die wichtigsten Service-Dienstleistungen aufrechterhalten bleiben. Dazu benötigt die Cloud-Plattform für den betroffenen Dienst gewisse Recovery-Fähigkeiten.
Markiert man im HCL-Quelltext einen AWS-Ausdruck so erreicht man über das Kontextmenü die zugehörige Terraform-Dokumentation auf der HashiCorp-Website. Eine für Sicherheitsaspekte sehr interessante Extension stellt tfsec dar, die Aqua Security entwickelt. Dabei handelt es sich um einen statischen Quellcode-Scanner, der Terraform-Code nach möglichen Sicherheitsproblemen durchforstet.Aktuell entwickelt die Virtusa Corp. das Produkt Terraform Studio als eine Extension für VS Code/VSCodium. Das Software-Haus positioniert die Neuentwicklung als Low-Code-IDE mit dem Ziel, die Lernkurve für Anwender zu verkürzen. Dabei ist Terraform Studio eingebettet in die SDLC Suite von Virtusa, die den gesamten Software-Development-Life-Cycle abdeckt.Verschiedene Dashboards geben einen Überblick zum aktuellen Ressourcenbedarf in der Cloud und den damit entstandenen Kosten. Der mit SDLC verbundene Prozess legt seinen Schwerpunkt auf eine Verbesserung von Produktivität, Effizienz und Agilität. Neben Terraform Cloud bietet Terraform Studio eine Integration mit TFSEC, Sentinel, Jenkis, Atlantis, checkov und ServiceNow an.

Einführung in die Arbeitsweise und Grundbegriffe von Terraform

Aus Architektursicht setzt sich Terraform aus zwei wesentlichen Komponenten zusammen: dem Terraform Core und den Providern (Bild 4). Hauptaufgabe von Terraform Core besteht aus dem Lesen einer oder mehrerer Terraform-Konfigurationsdateien und deren Abgleich mit der Terraform-State-Datei. Eine Terraform-Konfigurationsdatei (*.tf) beschreibt die vom DevOps-Mitarbeiter benötigten Ressourcen/Services (Infrastruktur-Elemente). Die Terraform-State-Datei (*.tfstate) enthält aktuelle Informationen zum Zustand aller bereits im Projekt erzeugten Infrastruktur-Elemente. Terraform realisiert die Zugriffe auf diese beide Dateien mit der Programmiersprache Go (Golang).
Terraform liest Konfigurationsdateien(*.tf) und erzeugt ausgehend von der aktuellen Terraform-State-Datei (*.tfstate) die spezifizierte Infrastruktur in der gewünschten Cloud-Umgebung(Bild 4) © Simon
Die zweite aus Architektursicht vorhandene Hauptkomponente stellen die Provider dar, deren Realisierung der Terraform-Software bestimmte Technologien zugänglich macht. Bei diesen Technologien handelt es sich um Cloud-Provider (wie AWS, Azure, GCP, OCI) oder andere IaaS-Plattformen. Terraform kann aber auch PaaS-Tools wie (Docker, Kubernetes), Konfigurationswerkzeuge (wie Ansible, Chef, Puppet) oder auch SaaS (sprich Systemsoftware) einbeziehen. Die Kommunikation zwischen Terraform und den Providern erfolgt über RPCs (Remote Procedure Calls). Einem Terraform-Projekt sind alle von ihm benötigten Provider bereitzustellen, dies erfolgt in einem speziellen Ablageordner. Das Main-Kommando terraform init übernimmt diese Aufgabe. Bei der erstmaligen Ausführung des init-Befehls erzeugt die Terraform-Software ihr Arbeitsverzeichnis im Projektordner, legt alle Informationsdateien an und lädt alle für das Projekt bereits bekannte Provider aus der Terraform Registry (einer zentralen Ablage) herunter.Hat der DevOps-Mitarbeiter in den Konfigurationsdateien Änderungen vorgenommen, so muss dieser das terraform init-Kommando erneut ausführen. Sollte ein DevOps-Mitarbeiter sich nicht an diese Konvention halten, bemerkt Terraform das Fehlen der Reinitialisierung des Arbeitsverzeichnisses im Projektordner und weist ihn darauf hin, den init-Befehl nochmals durchzuführen.Ergebnis des eingangs beschriebenen Abgleichs von Terraform zwischen den Konfigurationsdateien und der State-Datei stellt ein sogenannter Plan, auch Ausführungsplan genannt, dar. Um den Plan zu generieren, steht in Terraform als Main-Kommando terraform plan zur Verfügung. Der so erzeugte Plan legt alle Aktionen oder Operationen fest und gibt diese aus, die Terraform ausgehend vom aktuellen Zustand benötigt, um den festgelegten Zielzustand für die Infrastruktur zu erreichen.Um den Plan auszuführen, das heißt den Soll-Zustand in das gewünschte Ergebnis in der Infrastruktur zu überführen, greift man auf das Main-Kommando: terraform apply zurück. Erst das apply-Kommando führt tatsächlich die notwendigen Änderungen durch, das heißt passt alle bereits vorhandenen Objekte an die spezifizierte Infrastruktur an. Erst danach befindet sich die Infrastruktur in ihrem gewünschten Ziel- oder Endzustand. Während der Entwicklung, sprich Codierung einer HCL-Datei, unterstützt der Befehl terraform validate den Programmierer. Dieser CLI-Befehl prüft alle Konfigurationsdateien in einem Ordner auf syntaktische Korrektheit und interne Konsistenz.Um alle vorhandenen Elemente aus einer Infrastruktur zu löschen, kommt der Befehl terraform destroy zum Einsatz. Möchte man vorher alle potentiell gelöschten Objekte überprüfen, so zeigt der Befehl terraform plan -destroy diese an, ohne den eigentlichen Löschvorgang auszuführen. Nur eine derartige Vorgehensweise verhindert ein versehentliches Löschen benötigter Objekte der Infrastruktur. Im Notfall muss im Projekt terraform apply erneut ausgeführt werden, um den ursprünglichen Zustand wieder zu erreichen.

Syntax-Elemente der Terraform Language orientieren sich an der HCL

Die Programmiersprache von Terraform, Terraform Language genannt, basiert auf einem Dialekt der HashiCrop Configuration Language (HCL, siehe Teil 1). Derzeit gibt es von HashiCorp noch keine grafische Benutzungsschnittstelle (GUI), die HCL-Quellcode für Terraform generiert. Daher arbeitet der DevOps-Mitarbeiter in einer Programmierumgebung, welche die Syntax der Terraform Language unterstützt; beispielsweise VS Code/VSCodium. HCL ist für Programmierer leichter lesbar als JSON oder YAML und verfügt über mächtigere Konstrukte – vergleichbar einer höheren Programmiersprache (angelehnt an die Go-Syntax).Alle für die Infrastruktur erforderlichen Objekte deklariert der DevOps-Mitarbeiter in HCL. Der Quellcode, den Terraform verarbeitet, befindet sich in einer oder mehrerer Konfigurationsdateien mit der Endung .tf. Dieser Quellcode in einer Konfigurationsdatei setzt sich aus einem, in der Regel aber aus mehreren Blöcken zusammen (Bild 5). ein Block entspricht bildlich gesprochen einem Behälter, der die Konfiguration eines Terraform-Objekts aufnimmt beziehungsweise dessen Deklaration enthält.
Die Terraform Languagebasiert auf der HCL (HashiCorp Configuration Language). Die Syntax dieser Sprache ist leicht zu lesen und zu schreiben(Bild 5) © Simon
Jeder Block orientiert sich an folgender Syntax: block_typ "label" "label_name". Wobei ein block_typ dem Terraform-Objekt entspricht, das der jeweilige Block im nachfolgenden Quellcode näher spezifiziert. Das auf den Objekttyp/Blocktyp folgende label beschreibt ein für die Infrastruktur benötigtes Element. Dieses Infrastruktur-Element (label) erhält für die spätere Referenzierung (Wiederverwendung) im HCL-Code einen optionalen Namen label_name.Anschließend folgt in geschwungenen Klammern { } eine Folge von Schlüssel-Wert-Paaren: schluessel = "wert". Jedes Schlüssel-Wert-Paar legt ein Attribut für den durch label spezifizierten Objekt/Blocktyp des Infrastruktur-Elements fest. Wobei schluessel eine Zeichenkette (String) darstellt und der Wert der konkreten Ausprägung einem der in Terraform definierten Datentypen entspricht. Innerhalb eines Blocks können sich weitere sogenannte geschachtelte (nested) Blöcke befinden. Ein geschachtelter Block beginnt mit seinem Namen, es folgen geschwungene Klammern { } mit weiteren Schlüssel-Wert-Paaren (Key-Value-Pairs).Die Konfigurationsdatei main.tf besteht aus zwei Terraform-Language/HCL-Blöcken: einem Provider- und einem Ressource-Block:

<u># Datei: main.tf</u>
<u># Den Provider fuer das Azure-Projekt</u>
<u># mit source und version festlegen</u>
<u>terraform {</u>
<u>  </u><u>required_providers {</u>
<u>    azurerm = {</u>
<u>      source  = <span class="hljs-string">"hashicorp/azurerm"</span></u>
<u>      version = <span class="hljs-string">"=2.46.0"</span></u>
<u>    </u><u>}</u>
<u>  }</u>
<u>}</u>
# Bereitstellen eines Microsoft Azure Provider
provider <span class="hljs-string">"azurerm"</span> {
  # Einen Terraform-Azure-Provider einrichten
  features {}
  # Azure Subscription-ID einlesen
  subscription_id = local.azSubID
}
# Eine Resource Group <span class="hljs-keyword">in</span> Azure einrichten
resource <span class="hljs-string">"azurerm_resource_group"</span> <span class="hljs-string">"apache_rg"</span> {
  name     = <span class="hljs-string">"rg-${var.hostname}"</span>
  location = var.location
}
 
 
Der zuerst genannte Provider-Block richtet für das über den Schlüssel subscription_id identifizierte Azure-Konto den Terraform-Azure-Provider ein. Alternativ hätte man diesen Microsoft Azure Provider für Terraform auch über einen Azure Service Principal einrichten können. Allerdings hätte dieser Azure Service Principal die benötigten Zugriffsrechte (siehe Teil 3) besitzen müssen, das heißt die Rolle (--role="Contributor") für das über --scopes="/subscriptions/ <subscription ID>" identifizierte Azure-Konto.Der zweite HCL-Block vom Typ resource richtet eine Resource Group in Azure mit dem Namen rg-apache in der Location West Europe ein. Als beschreibendes Tag erhält die neue Resource Group das Feld project zugeordnet, das Terraform mit dem Wert Webserver-apache versieht. Stößt der DevOp-Mitarbeiter im Verzeichnis der main.tf-Datei die Initialisierung des Projekts mit dem Befehl terraform init an, so lädt Terraform dazu das benötigte HashiCorp-Azure-Plugin
herunter.
Der Befehl terraform applyführt den Execution Plan aus, zeigt vorher aber noch die geplanten Änderungen an, die der DevOps-Mitarbeiter mit yes bestätigen muss(Bild 6) © Simon
Der anschließende Befehl terraform plan benötigt bereits eine aktive Anmeldung bei Azure; er erzeugt einen Ausführungsplan und zeigt dem DevOps-Mitarbeiter alle anstehenden Änderungen an. Die tatsächlichen Anlagen oder Änderungen der Infrastruktur-Elemente in Azure führt erst der Befehl terraform apply im angemeldeten Konto durch (Bild 6).

Terraform stellt Komponenten für die Programmierung bereit

Terraform besitzt verschiedene Objekttypen, die ein DevOps-Mitarbeiter für die Spezifikation der Elemente einer Infrastruktur einsetzen kann; zu den wichtigsten zählen: Provider, Resources und Data Sources. Zusätzlich gibt es noch das Terraform-Objekt, es legt den aus der Terraform Registry für das Projekt einzusetzenden Provider fest. Ein Provider liefert Terraform alle benötigten Informationen, um die Verbindung zur gewünschten Zielplattform (IaaS, PaaS, SaaS) aufzubauen und diese zu nutzen.Das Beispielprojekt setzt die Version 2.46.0 des Azure Provider ein, nutzt im Provider-Block den azurerm-Provider und teilt Terraform mit, welcher Azure-Account dafür zu verwenden ist. Bei Objekten vom Typ Resources handelt es sich um Deklarationen über die in der Infrastruktur anzulegenden Elemente. Deren Spezifikationen bilden die Basis, um konkrete Ausprägungen der Objekte in der genannten Zielplattform zu erzeugen.Aus Sicht der Codierung treten in einem Projekt am häufigsten Resource-Blöcke auf, die alle seitens der DevOps-Mitarbeiter zu programmieren sind. Jeder Resource-Block bezieht sich auf einen Provider und stellt ihm zusätzliche Informationen bereit, um das spezifizierte Infrastruktur-Element/Service auf der Zielplattform generieren zu können. Diese Zusatzinformation legt fest, wie Terraform die Deklarationen einer Ressource verarbeitet und auf die Konfiguration anwendet. Das bedeutet der Resource-Block führt die eigentliche Konfiguration des Infrastruktur-Elements/Service durch.Im Beispielprojekt hat der DevOps-Mitarbeiter eine azurerm_resource_group spezifiziert, die Terraform über die aufgebaute Verbindung im Azure-Konto beim Befehl terraform apply anlegt. Sollte das betroffene Objekt bereits auf der Zielplattform vorhanden sein, dann führt Terraform eine für die Spezifikation passende Änderung durch. Eine Azure Resource Group kann von mehreren Projekten verwendet werden.

Strategien für die Hochverfügbarkeit in der Cloud

Gängige Cloud-Plattformen implementieren Hochverfügbarkeit durch eine Mischung von Verteilung, Redundanz und Resilienz. Durch die Einführung von Regions nimmt man eine Verteilung von Services innerhalb der Cloud-Plattform vor; schafft aber aus der Gesamtsicht des Systems Redundanzen. Innerhalb einer Region baut man Availability Zones auf, um durch weitere Redundanzen bestimmte Dienste ausfallsicher bereitzustellen. Erhalten diese Dienste geeignete Recovery-Features, so führt diese Hochverfügbarkeit nicht zu einer Vervielfachung der Kosten. Der Einsatz von Lastverteilung durch Load Balancer unterstützt die Anwender, ihre Verarbeitung selbst effizienter zu gestalten.
Data Sources dienen Terraform als Basis, um weitere Information von einem Provider zu erhalten. Möchte Terraform ein bestimmtes Element anlegen, so benötigt eventuell die Software zusätzliche Informationen. Über eine Data Source frägt Terraform den betroffenen Provider ab, um die erforderlichen Informationen zu erhalten. Ähnlich wie Resource-Blöcke entsprechen auch Data Sources einem Block, dem data-Block, der mit einem Provider verknüpft ist.So kann der DevOps-Mitarbeiter über eine Data Source beispielsweise eine Abfrage spezifizieren, die sicherstellt, dass ein benötigtes Infrastruktur-Element auf der Zielplattform in einer bestimmten Region auch tatsächlich zur Verfügung steht. Im konkreten Fallbeispiel kann es sich bei der Data Source um eine Liste verfügbarer Availability Zones in einer Region handeln.

HashiCorp reichert Terraform Language um Sprachkonstrukte an

Die Syntax der Terraform Language basiert auf der HCL-Spezifikation, die für Menschen leicht verstehbar, damit schnell änderbar und mit Software einfach zu verarbeiten ist (siehe Teil 1). HashiCorp bezeichnet die Syntax der Terraform Language als Configuration Syntax; sie besitzt das .tf-Dateiformat. Für Aufbau und Gestaltung von .tf-Dateien kennt Terraform einen von HashiCorp vorgegebenen Programmierstil – der Befehl terraform fmt überführt in Terraform Language programmierte Dateien in diese Darstellung. Somit stellt man innerhalb eines Teams eine einheitliche Programmierung sicher. Alternativ unterstützt Terraform auch eine JSON-kompatible Syntax, die als Dateiendung .tf.json erhält.Ein Block enthält neben Label und weiteren (geschachtelten) Blöcken Argumente/Parameter, Expressions und Werte. Ein Argument weist oder bindet einen Wert an einen speziellen Namen; dies erfolgt innerhalb eines Blocks. Expressions oder Ausdrücke stellen Literale oder referenzierte Werte für Argumente dar. Dabei unterstützt die Terraform Language auch komplexe Ausdrücke, Berechnungen, Fallunterscheidungen – die man mittels Operatoren, Funktionsaufrufen oder Variablen bilden kann. Die Sprache kennt auch for-Ausdrücke, um die betroffene Datenstrukturen mittels Schleife zu verarbeiten.Was Datentypen betrifft, kennt die Terraform Language primitive Typen wie Zeichenketten/Strings, Zahlen, die booleschen Werte true und false – aber auch komplexe Typen wie Listen (Tupel) oder Maps. Dazu kommen Literale, die als Ausdruck einen bestimmten konstanten Wert ergeben. Eine Liste/Feld/Array (Tupel) entspricht einer durch Komma getrennte Folge von Werten, die sich innerhalb von eckigen [ ]-Klammern befinden. Eine Map, manchmal auch einfach Objekt genannt, nimmt in geschwungenen Klammern { } Schlüssel/Wert-Paare auf. Während man die Elemente einer Liste über Indices anspricht, greift man auf ein Element einer Map mit dem Ausdruck local.MapName["SchlüsselName"] zu.

Wiederverwendung von Konfigurationen mittels Moduln

Moduln dienen der Wiederverwendung von Konfigurationen in Terraform – jede Konfiguration besteht aus mindestens einem Modul – dem Root-Modul. Ein Root-Modul besteht aus Ressourcen, die innerhalb von .tf-Dateien definiert sind und sich in der Root/Wurzelverzeichnis eines Projektordners befinden. Ein Modul kann man als einen Behälter für verschiedene Ressourcen auffassen, die man zusammen benutzen möchte. Ein Modul stellt quasi eine Sammlung von .tf- und/oder .tf.json-Dateien dar, die sich alle in einem gemeinsamen Ordner befinden. Ein Modul, das ein anderes Modul aufruft, das heißt benutzt, nennt man Child/Kind-Modul.Neben der Wiederverwendung zielen Moduln auf die Organisation der Konfigurationen in logisch zusammengehörende Komponenten ab. Eine derart organisierte Komponente erleichtert es den DevOps-Mitarbeiter Änderungen vorzunehmen – da zusammenhängende Informationen sich auf die lokalen Kontexte beschränken.Ein Modul erhöht die Konsistenz innerhalb einer Vielzahl vorhandener Konfigurationen. Komplexe Konfigurationen präsentieren sich auf leicht verständlichere Art und Weise. Somit senkt sich auch die Bearbeitungszeit und die Zahl möglicher Fehlerquellen reduziert sich.Zusätzlich zur Ablage von Moduln im lokalen Dateisystem, unterstützt Terraform das Laden von Moduln aus einer öffentlichen oder privaten Registry. Eine Registry dient als Ablageort für Moduln, um diese anderen zugänglich zu machen und der Wiederverwendung zuzuführen. HashiCorp stellt über die Terraform Registry (Bild 7) eine große Sammlung von öffentlich zugänglichen Moduln bereit, die man zur Konfiguration gängiger Infrastruktur-Elemente nutzen kann. Auch die Terraform Cloud und Terraform Enterprise enthalten eine private Modul-Registry, um Moduln mit anderen innerhalb einer Entwicklungsorganisation zu teilen.
Über die Homepage der Terraform Registrymacht HashiCorp eine Vielzahl von Provider und Moduln öffentlich zugänglich(Bild 7) © Simon
Der Aufruf eines Moduls – ein Modul-Call – erfolgt über einen module-Block. Das direkt auf das Schlüsselwort module folgende Label entspricht dessen lokalem Namen, über den andere Moduln eine Instanz des Moduls ansprechen können. Innerhalb des Block-Body (den zwei geschwungenen Klammern { }) befindet sich ein source-Argument und weitere Eingabevariable. Das source-Argument verweist auf den Quellcode des Moduls, so dass Terraform auf ihn zugreifen und ihn nutzen kann.Dabei unterstützt Terraform eine große Anzahl verschiedenster Ablagemöglichkeiten für den Quellcode: lokale Verzeichnisse, Terraform Registries, Repositories, URLs, Cloud-Speicher (Buckets) oder Unterverzeichnisse von Modul-Packages. Der terraform init-Befehls kopiert Quellcode von Remotesites in das lokale Projektverzeichnis und macht ihn so Terraform zugänglich.

Terraform kennt Variable für Input und Output sowie zusätzlich Konstanten

Innerhalb von Blöcken kann man Variable und Konstante, die HashiCorp auch lokale Variable nennt, verwenden. Um Eingabewerte als Parameter an ein Terraform-Modul zu übergeben, definiert man Input-Variable, kurz Variable – diese lassen sich mit Eingabeparametern einer Funktion vergleichen. Über diese Input-Variable passt der DevOps-Mitarbeiter den Quellcode einer Terraform-Konfiguration an eigene Bedürfnisse an. Somit erhält diese automatisch eine flexiblere Verarbeitungsweise, da man an Input-Variable unterschiedliche Werte übergeben kann.Die Deklaration einer Input-Variable findet innerhalb eines HCL-Blocks durch das variable-Schlüsselwort statt, es folgt ihr Name und in geschwungenen Klammern { } weitere optionale Angaben zu ihrem Datentyp (type), einem Standardwert (default):, einer Beschreibung für die Dokumentation (description) oder einer Werteprüfung mittels eines validation-Blocks:

locals {
  azSubID = <span class="hljs-string">"8003XXX0-fxxx-45dd-898b-0a15szr3a6ac"</span>
  adressSpace = [<span class="hljs-string">"10.0.0.0/24"</span>]
  subnet_prefix = [<span class="hljs-string">"10.0.0.0/24"</span>]
}
variable <span class="hljs-string">"hostname"</span> {
  <span class="hljs-keyword">description</span> = <span class="hljs-string">"Name des virtuellen Host"</span>
  <span class="hljs-keyword">default</span>     = <span class="hljs-string">"apache"</span>
}
<span class="hljs-comment">/*</span>
<span class="hljs-comment">variable "vm-username" {</span>
<span class="hljs-comment">  description = "Der Name des Benutzers fuer den VM-Zugriff"</span>
<span class="hljs-comment">  default     = "webserver-admin"</span>
<span class="hljs-comment">}</span>
<span class="hljs-comment">*/</span>
variable <span class="hljs-string">"location"</span> {
  <span class="hljs-keyword">description</span> = <span class="hljs-string">"Region in der das Virtuelle Netzwerk angelegt wird."</span>
  <span class="hljs-keyword">default</span>     = <span class="hljs-string">"West Europe"</span>
}
<span class="hljs-comment">/*</span>
<span class="hljs-comment">variable "vm-kennwort" {</span>
<span class="hljs-comment">  description = "Das Kennwort um fuer den VM-Zugriff"</span>
<span class="hljs-comment">}</span>
<span class="hljs-comment">*/</span>
variable <span class="hljs-string">"admin-username"</span> {
  <span class="hljs-keyword">description</span> = <span class="hljs-string">"Benutzername des Administrators der VM"</span>
  <span class="hljs-keyword">default</span>     = <span class="hljs-string">"adminuser"</span>
}
# yum repolist all
 
variable <span class="hljs-string">"admin-password"</span> {
  <span class="hljs-keyword">description</span> = <span class="hljs-string">"Kennwort des Administrators der VM"</span>
  <span class="hljs-keyword">default</span>     = <span class="hljs-string">"Ende5678"</span>
}
variable <span class="hljs-string">"ssh_key"</span> {
  <span class="hljs-keyword">description</span> = <span class="hljs-string">"Pfad auf die Public-Key-Datei, fuer den SSH-Zugriff auf die VM."</span>
  <span class="hljs-keyword">default</span>     = <span class="hljs-string">"~/.ssh/id_rsa.pub"</span>
}
 
 
Eine besondere Option stellt sensitive = true dar; sie bewirkt, dass Terraform niemals den dazugehörigen Wert anzeigt. Die Variable unterliegt einer Sicherheitsfunktion, damit eignet sich die sensitive = true-Option besonders für Kennwörter/Passwörter.Neben Input-Variablen kennt Terraform auch Output-Variable, diese bezeichnet HashiCorp auch als Output Values oder kurz: Outputs. Output Values reichen in Anlehnung an Rückgabewerte von Funktionen Werte an andere Terraform-Konfigurationen weiter. Die Deklaration einer Output-Variable erfolgt über einen output-Block; dieser entspricht dem Aufbau eines variable-Blocks. Die Werte einer Output-Variable stehen erst nach Abarbeitung eines Ausführungsplans mittels terraform apply und nicht schon durch den Befehl terraform plan zur Verfügung.Für Konstante verwendet HashiCorp den Begriff Local Values; deren Wert steht nur lokal im jeweiligen Terraform-Projekt temporär zur Verfügung. Die Deklaration eines Local Value findet innerhalb eines locals-Block statt. Man verwendet den Plural, da die Bezeichnung locals verdeutlichen soll, dass man innerhalb eines locals-Block gleich mehrere Konstanten auf einmal definieren kann.Der Zugriff auf den Wert einer Konstante erfolgt über eine local.<Name>-Referenz. Achtung: Im Unterschied zum Block kommt hier der Singular zum Einsatz. Wobei der Wert einer Konstante nur dem Modul zur Verfügung steht, innerhalb dessen die Deklaration erfolgt. Wesentlicher Vorteil einer Konstante (Local Value) liegt an dem Sachverhalt, dass man ihren Wert an einer zentralen Stelle (ihrer Deklaration) leicht für alle Referenzen ändern kann.

Provider bilden Schnittstelle zu den eingesetzten Infrastrukturen/Services

Bei einem Provider handelt es sich um ein Plugin über das Terraform alle auf einer Infrastruktur benötigen Ressourcen verwaltet. Zu jedem von Terraform unterstützten Service oder Infrastruktur-Plattform existiert ein Provider, dieser macht dem DevOps-Mitarbeiter alle verfügbaren Ressourcen/Services zugänglich. Derzeit gibt es fast 150 offiziell von HashiCorp unterstützte Provider, zusätzlich nochmals so viele seitens der Community. Ein Provider realisiert den kompletten Lebenszyklus einer Ressource, das heißt ein Provider übernimmt die Verantwortung für deren Erstellung, Lesen, Aktualisieren und Löschen.Für diese CRUD (Create/Read/Update/Delete)-Aktionen führt Terraform API-Calls per RPC (Remote Procedure Call) aus, da sich die Schnittstelle des APIs in der Regel auf dem Remote-System (Service/Ressource) befindet. Der Befehl terraform init macht in einem Projekt-Ordner die benötigten Provider-Plugins durch Herunterladen aus einer Registry in einem .terraform-Unterverzeichnis verfügbar.Zusätzlich existiert auf den zugehörigen Infrastruktur-Plattformen ein passendes API. Terraform Core führt über das Provider-Plugin mittels eines APIs eine Remote-Kommunikation mit dieser Infrastruktur-Plattform/Service aus. Die Implementierung der Provider-Plugins erfolgt in der Programmiersprache Go.Setzt der Anwender eine Vielzahl von Providern in unterschiedlichen Projekten ein, so spart man durch Einrichten eines Plugin-Cache die erforderlichen Hardware-Ressourcen. Dazu richtet der Anwender über eine CLI-Konfigurationsdatei einen Verweis auf plugin_cache_dir ein. In Windows befindet sich dazu eine terraform.rc-Datei im %APPDATA-Verzeichnis des Benutzers. Für alle anderen Betriebssysteme legt man eine (verdeckte) .terraformrc-Datei direkt im Home-Verzeichnis des Benutzers an. Um für diese CLI-Konfigurationsdatei auch andere beliebige Verzeichnisse zu verwenden, stellt HasiCorp die Umgebungsvariable TF_CLI_CONFIG_FILE bereit.Alle für einen Provider verfügbaren Informationen für das Arbeiten mit ihm macht die Terraform Registry zugänglich. Wählt man auf deren Homepage gezielt den Provider aus, so öffnet der Link Documentation im Webbrowser links einen Fensterausschnitt mit allen für diesen Providern verfügbaren Attributen. Für den Zugriff auf die Realisierung einer bestimmten Version eines Providers steht im provider-Block das Schlüsselwort version bereit.Auch die Authentifizierung des Benutzers in der Infrastruktur-Plattform erfolgt im provider-Block über bestimmte Schlüssel/Wert-Paare. Dabei sollte man die Kennwörter ebenso Benutzernamen und die Referenz auf Endpoints nicht im Klartext angeben, sondern über Umgebungsvariable oder mittels Input-Variable von Terraform realisieren. Zusätzlich muss darauf geachtet werden, das provider-Blöcke nur innerhalb des Root-Moduls deklariert werden, da diese an Child-Moduln vererbt werden.

In einer Konfiguration berechnen oder referenzieren Expressions Werte

Terraform besitzt eine eigene Syntax, um Ausdrücke (Expressions) innerhalb HCL zu programmieren. Innerhalb dieser Expression stehen alle genannten Datentypen zur Verfügung. Zusätzlich kann man Literal Expressions, die einen bestimmten konstanten Wert präsentieren einsetzen. Innerhalb von Hochkommata stehen Zeichenketten deren Escape-Sequences eine Formatierung vornehmen. Für die Datenübergabe an Unix-Shells kennt Terraform HEREDOC-Strings (siehe Teil 1). String-Templates beginnen innerhalb einer Zeichenkette mit einer Interpolation ${ }; in der man Variablen referenzieren kann.Befinden sich in einer Konfiguration mit Namen versehene (benannte) Ressourcen wie Variable, so lassen sich diese auch innerhalb von Expressions nutzen. Eine derartige Ressource spricht man über die Notation <Resource_Typ>.<Name> an. Auf den Wert einer Input-Variable greift man in einer Expression durch die Notation var.<Name> zu. Eine lokale Variable spricht der Ausdruck local.<Name> an. Ergibt ein module-Block einen Wert oder eine Werte-Liste, so referenziert module.<Module_Name> diesen Ausdruck; dasselbe gilt für Data Sources (data.<Data_Type>.<Name>). Für Pfadangaben stehen path.cwd (Pfad auf das aktuelle Arbeitsverzeichnis), path.module (Terraform-Modul-Path) oder path.root (Pfad auf das Root-Modul).

Orientierung an gängigen Programmiersprachen

Seitens der Arithmetik für mathematische und logische Operatoren orientiert sich die Syntax für Expressions an gängigen Programmiersprachen. If-Anweisungen bilden konditionale Anweisungen ab: Diese beginnen mit einer Abfrage (condition ?) und bestehen aus einem true-Zweig und einem false-Zweig, die beide durch einen Doppelpunkt (:) verbunden sind. Ergänzend zu diesen bedingten Anweisungen kennt Terraform noch for-Expression, Type- und Version-Constraints. Mittels Type-Constraints prüft man seitens des Benutzers eingegeben Werte; während Version-Contraints in Anlehnung an Bundler oder NPM sich auf Versionen beziehen.Eine in Expressions verfügbare for-Anweisung beginnt in eckigen [ ] oder runden { } Klammern und dient der Bildung neuer Datenstrukturen. Über das Schlüsselwort for definiert man eine Variable, die sich innerhalb einer Datenstruktur var.list befindet und diese durchläuft. Abschließend deklariert ein Doppelpunkt (:), welches Ergebnis die Expression ergeben soll, beispielsweise: [for x in var.list : upper(x)]. Während [ ] ein Tupel mit Werten zurückliefert, erzeugt { } ein Objekt – wobei in diesem Fall ein zusätzlicher Pfeil-Operator benötigt wird: {for x in var.list : x => upper(x)}.Eine mächtige Anweisung stellt die splat-Expression dar: Diese enthält das Wildcard-Zeichen * mit dem Terraform durch eine Datenstruktur iteriert. Als Ergebnis liefert Terraform eine Liste mit den zugehörigen Werten zurück. So erzeugt die nachfolgende splat-Expression eine Output-Variable mit den IDs aller AWS-Instanzen, die sich auf das Label demo beziehen:

<span class="hljs-attribute">output</span> <span class="hljs-string">"instance_id"</span> {
  <span class="hljs-attribute">value</span> = aws_instance.demo[*].id
}
 
 
Terraform verfügt über ein besonderes Tool – die Terraform Console. Dabei handelt es sich um einen Interpreter zur Auswertung von Expressions oder zur Erkundung des Projektzustands. Insofern eignet sich die Terraform Console auch zur Entwicklung beziehungsweise Programmierung von Konfigurationen oder zur Fehlersuche. Das heißt über die Konsole kann man Informationen zum Zustand eines Projekts oder den Ausprägungen eines bestimmten Infrastruktur-Elements abfragen.
Die Terraform Consoleermittelt Rückgabewerte von Expressions oder hilft bei der Erkundung von Infrastruktur-Elementen(Bild 8) © Simon
Die Terraform Console verändert weder den Zustand eines Projekts, noch dessen Konfigurations-Objekte/Infrastruktur-Elemente oder die damit verbundenen Ressourcen. Insofern muss man beim Einsatz der Terraform Console keinerlei Besonderheiten beachten oder spezielle Vorkehrungen treffen. Zugriff auf die Terraform Console erhält man über den Befehl terraform console; danach erscheint im Terminalfenster ein >-Prompt (Bild 8), um Expressions einzugeben und deren Rückgabewerte zu ermitteln oder Informationen über Infrastruktur-Elemente auszugeben.

Mit Built-In-Funktionen konkrete Werte bestimmen oder berechnen

Als Programmiersprache besitzt die Terraform Language einen großen Vorrat an Built-In-Funktionen, die man als Funktionsaufrufe innerhalb von Expressions verwenden kann. Die Sprache selbst erlaubt es derzeit aber nicht, eigene Funktionen zu definieren. Der Aufruf einer Funktion entspricht den Konventionen der Programmiersprache Go: Anfangs steht der Funktionsname, danach folgen in runden Klammern die durch Komma getrennten Argumente, auch Funktionsparameter genannt.Im Wesentlichen kommen die Built-In-Funktionen zum Einsatz, um neue Werte aufgrund aktueller Laufzeitwerte zu bestimmen, die vor der Ausführung der Terraform-Konfiguration noch völlig unbekannt sind. Die Built-In-Funktionen in Terraform kann man in verschiedene Klassen unterteilen:Reine Infrastruktur-Elemente, die letztendlich Hardware-Ressourcen entsprechen, bilden lediglich die Grundlage für das Deployment von Anwendungen. Erst wenn die Infrastruktur-Elemente/Services über Anwendungen verfügen, die passgenau auf Endbenutzer abgestimmt sind, erhalten diese ihr volles Nutzenpotenzial. Alle Tätigkeiten, die mit Installation oder Management von Anwendungen oder Systemsoftware verbunden sind, bezeichnet man im Zusammenhang mit Infrastruktur-Elementen als Konfiguration.Im Umfeld von Terraform stehen für die Konfiguration die bereits besprochenen Provider wie Ansible, Chef oder Puppet zur Verfügung. Leider verwenden deren Hersteller für die Konfiguration häufig auch die Bezeichnung Provisioning – woraus eine gewisse Irritation entsteht. In der Begriffswelt von Terraform steht der Begriff Provisioning nur für die Ausführung spezieller Aktionen, um bestimmte nachgelagerte Aufgaben vorzubereiten. Daher bedeutet Provisioning bei Terraform keine Konfiguration von Software.

Deklaration eines Provisioners

Diese auch bei HashiCrop früher vorliegende Auffassung hat sich im Laufe der Zeit verändert: HashiCorp verfolgt mit Terraform derzeit einen pragmatischen Ansatz mit dem Bewusstsein und der Aufforderung, vor dem Einsatz eines Provisioners zuerst nach anderen Lösungen zu suchen. Provisioner sollten erst eingesetzt werden, wenn man keine anderen Alternativen findet. Die Deklaration eines Provisioners erfolgt über einen eigenen provisioner-Block, der sich innerhalb eines resource-Blocks befindet.Normalerweise führt Terraform einen provisioner-Block aus, sobald die zugehörige Ressource erzeugt wurde. Damit entsprechen Provisioner softwaretechnisch betrachtet einem Bootstrapping für das Gesamtsystem. Diese gängige besser gesagt Default-Provisioner-Klasse bezeichnet HashiCorp als Creation-Time-Provisioner. Sollte das Bootstrapping nicht erfolgreich durchführbar sein, so markiert Terraform die zugehörigen Ressourcen als tainted (verunreinigt). Anhand dieser Markierung erkennt Terraform, dass die Ressource entweder gelöscht oder vollständig neu aufgebaut werden muss.Die Vorgehensweise des Tainting ist erforderlich, da Terraform nicht den genauen Zustand der Ressource nach erfolglosem Bootstrapping kennt. Ergänzend zum standardmäßigen Create-Time- stellt Terraform noch einen Destroy-Time-Provisioner zur Verfügung. Dessen Realisierung übernimmt ein spezielles Schlüssel/Wert-Paar im provisioner-Block, das sich direkt an diesen anschließt: when = destroy. Diese Provisioner-Klasse kommt nur zur Ausführung, wenn Terraform die zugehörige Ressource löscht. Wichtig zu wissen ist, dass Terraform einen Destroy-Provisioner vor dem Löschen der betroffenen Ressource ausführt.Bleibt dessen Ausführung erfolglos, so merkt sich Terraform diesen Sachverhalt und führt den Destroy-Provisioner beim nächsten terraform apply-Befehl erneut aus. Grundsätzlich brechen erfolglose Provisioner einen terraform-apply-Befehl ab. Dieses Standardverhalten umgeht der on_failure-Schlüssel mit dem Wert continue im provisioner-Block, das heißt terraform apply erzeugt oder löscht die zugehörigen Ressourcen. Aus den genannten Gründen muss man beide Provisionertypen vorsichtig einsetzen, da Terraform nach dem Löschen der zugehörigen Ressource keine Kenntnis mehr über den Provisioner und dessen tatsächlichen Zustand besitzt. Eventuell muss ein spezielles Verfahren abhängig vom konkret erwarteten Systemzustand implementiert werden.

Apache-Webserver in Microsoft Azure auf Linux-Basis (RHEL) einrichten

Das Beispielprojekt benötigt einen Azure-Account, eine Installation des CLI von Azure und der Terraform OSS-Edition. Als Programmierumgebung kommt VS Code/VSCodium mit der Extension HashiCorp Terraform zum Einsatz. Ausgangspunkt stellt das Beispielprojekt mit der Spezifikation der gewünschten Version für den Terraform-Azure-Provider, einem provider- und dem resource-Block dar.Innerhalb der resource group apache_rg mit dem Namen rg-apache in der Location West Europe befindet sich ein Virtuelles Netzwerk apache_vnet:

# Ein virtuelles Netzwerk fuer die VM einrichten
resource <span class="hljs-string">"azurerm_virtual_network"</span> <span class="hljs-string">"apache_vnet"</span> {
  name                = <span class="hljs-string">"vnet-${var.hostname}"</span>
  <span class="hljs-keyword">address_space </span>      = local.<span class="hljs-keyword">adressSpace</span>
<span class="hljs-keyword"> </span> location            = azurerm_resource_group.apache_rg.location
  resource_group_name = azurerm_resource_group.apache_rg.name
}
# Ein <span class="hljs-keyword">Subnetz </span>fuer das virtuelle Netzwerk einrichten
resource <span class="hljs-string">"azurerm_subnet"</span> <span class="hljs-string">"apache_subnet"</span> {
  name                 = <span class="hljs-string">"subnet-${var.hostname}"</span>
  resource_group_name  = azurerm_resource_group.apache_rg.name
  virtual_network_name = azurerm_virtual_network.apache_vnet.name
  <span class="hljs-keyword">address_prefixes </span>    = local.<span class="hljs-keyword">subnet_prefix</span>
<span class="hljs-keyword">}</span>
<span class="hljs-keyword"># </span>Eine Public-<span class="hljs-built_in">IP</span>-<span class="hljs-keyword">Adresse </span>anlegen
resource <span class="hljs-string">"azurerm_public_ip"</span> <span class="hljs-string">"apache_pip"</span> {
  name                = <span class="hljs-string">"ip-${var.hostname}"</span>
  location            = azurerm_resource_group.apache_rg.location
  resource_group_name = azurerm_resource_group.apache_rg.name
  allocation_method   = <span class="hljs-string">"Dynamic"</span>
  domain_name_label   = var.hostname
}
 
 
Für den Aufbau des Virtuellen Netzwerks benötigt Azure ein Subnetz und eine Public-IP-Adresse, um auf Apache über das Internet zugreifen zu können.Das Subnetz reserviert eine Bandbreite von IP-Adressen über eine NIC (Network Interface Card) in Azure:
# Ein Network-Interface (NIC) einrichten
resource <span class="hljs-string">"azurerm_network_interface"</span> <span class="hljs-string">"apache_nic"</span> {
  <span class="hljs-keyword">name</span>                = <span class="hljs-string">"nic-${var.hostname}"</span>
  resource_group_name = azurerm_resource_group.apache_rg.<span class="hljs-keyword">name</span>
  location            = azurerm_resource_group.apache_rg.location
 
  ip_configuration {
    <span class="hljs-keyword">name</span>                          = <span class="hljs-string">"meineNicConfiguration"</span>
    subnet_id                     = azurerm_subnet.apache_subnet.id
    private_ip_address_allocation = <span class="hljs-string">"Dynamic"</span>
    public_ip_address_id          = azurerm_public_ip.apache_pip.id
  }
}
#Eine Netzwerk-Security-Gruppe einrichten
resource <span class="hljs-string">"azurerm_network_security_group"</span> <span class="hljs-string">"apache_sg"</span> {
  <span class="hljs-keyword">name</span>                = <span class="hljs-string">"sg-${var.hostname}"</span>
  location            = azurerm_resource_group.apache_rg.location
  resource_group_name = azurerm_resource_group.apache_rg.<span class="hljs-keyword">name</span>
}
 
# Network-Interface mit Rules der Security-Group verbinden
resource <span class="hljs-string">"azurerm_subnet_network_security_group_association"</span> <span class="hljs-string">"apache_sga"</span> {
  subnet_id                 = azurerm_subnet.apache_subnet.id
  network_security_group_id = azurerm_network_security_group.apache_sg.id
}
 
 
Security realisiert Azure über eine sogenannte Network Security Group, diese enthält eine Reihe von Network Security Rules in sogenannten Inbound Port Rules.Terraform bildet die Port Rules über den resource-Block azurerm_network_security_rule. Das Beispielprojekt richtet zwei solche Port-Rules ein: einen für HTTP-Zugriffe aus dem Internet auf den Apache-Webserver und einen für SSH-Verbindungen:

resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"http"</span> {
    <span class="hljs-attr">name</span>                       = <span class="hljs-string">"http"</span>
    <span class="hljs-attr">priority</span>                   = <span class="hljs-number">200</span>
    <span class="hljs-attr">direction</span>                  = <span class="hljs-string">"Inbound"</span>
    <span class="hljs-attr">access</span>                     = <span class="hljs-string">"Allow"</span>
    <span class="hljs-attr">protocol</span>                   = <span class="hljs-string">"TCP"</span>
    <span class="hljs-attr">source_port_range</span>          = <span class="hljs-string">"*"</span>
    <span class="hljs-attr">destination_port_range</span>     = <span class="hljs-string">"80"</span>
    <span class="hljs-attr">source_address_prefix</span>      = <span class="hljs-string">"*"</span>
    <span class="hljs-attr">destination_address_prefix</span> = <span class="hljs-string">"*"</span>
    <span class="hljs-attr">resource_group_name</span>         = azurerm_resource_group.apache_rg.name
    <span class="hljs-attr">network_security_group_name</span> = azurerm_network_security_group.apache_sg.name
  }
resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"ssh"</span> {
    <span class="hljs-attr">name</span>                       = <span class="hljs-string">"ssh"</span>
    <span class="hljs-attr">priority</span>                   = <span class="hljs-number">100</span>
    <span class="hljs-attr">direction</span>                  = <span class="hljs-string">"Inbound"</span>
    <span class="hljs-attr">access</span>                     = <span class="hljs-string">"Allow"</span>
    <span class="hljs-attr">protocol</span>                   = <span class="hljs-string">"TCP"</span>
    <span class="hljs-attr">source_port_range</span>          = <span class="hljs-string">"*"</span>
    <span class="hljs-attr">destination_port_range</span>     = <span class="hljs-string">"22"</span>
    <span class="hljs-attr">source_address_prefix</span>      = <span class="hljs-string">"*"</span>
    <span class="hljs-attr">destination_address_prefix</span> = <span class="hljs-string">"*"</span>
    <span class="hljs-attr">resource_group_name</span>         = azurerm_resource_group.apache_rg.name
    <span class="hljs-attr">network_security_group_name</span> = azurerm_network_security_group.apache_sg.name
  }
 
 
Dazu kommen wie üblich für HTTP-Zugriffe der Port 80 und für SSH der Port 22 zum Einsatz (Bild 9).
Azure enthält Bausteinefür den schrittweisen Aufbau einer Infrastruktur in der Cloud(Bild 9) © Simon
Der letzte Schritt legt die Linux-VM (vm-apache) mit deren Ressourcen an und baut die Verbindungen zu den bereits in Azure eingerichteten Infrastruktur-Elementen auf:

# Eine virtuelle Linux-Maschine einrichten
resource <span class="hljs-string">"azurerm_virtual_machine"</span> <span class="hljs-string">"apache_vm"</span> {
  name                            = <span class="hljs-string">"vm-<span class="hljs-subst">${var.hostname}</span>"</span>
  resource_group_name             = azurerm_resource_group.apache_rg.name
  location                        = azurerm_resource_group.apache_rg.location
  vm_size                         = <span class="hljs-string">"Standard_A1_v2"</span>
  network_interface_ids           = [<span class="hljs-string">"<span class="hljs-subst">${azurerm_network_interface.apache_nic.id}</span>"</span>]
  delete_os_disk_on_termination   = <span class="hljs-string">"true"</span>
  storage_image_reference {
      publisher = <span class="hljs-string">"RedHat"</span>
      offer     = <span class="hljs-string">"RHEL"</span>
      sku       = <span class="hljs-string">"7.3"</span>
      version   = <span class="hljs-string">"latest"</span>
  }
  storage_os_disk {
    name              = <span class="hljs-string">"osdisk-<span class="hljs-subst">${var.hostname}</span>"</span>
    managed_disk_type = <span class="hljs-string">"Standard_LRS"</span>
    caching           = <span class="hljs-string">"ReadWrite"</span>
    create_option     = <span class="hljs-string">"FromImage"</span>
  }
  os_profile {
    computer_name  = <span class="hljs-keyword">var</span>.hostname
    admin_username = <span class="hljs-keyword">var</span>.admin-username
    admin_password = <span class="hljs-keyword">var</span>.admin-password
  }
 
  os_profile_linux_config {
      # disable_password_authentication = <span class="hljs-keyword">true</span>
      disable_password_authentication = <span class="hljs-keyword">false</span>
  }
}
 
 
Dazu benötigt man Informationen seitens Azure, welche vm_size für die Location westeurope zur Verfügung stehen. Der CLI von Azure az vm list-skus --location westeurope --all --output table gibt eine Tabelle aus; anhand derer man nach verfügbaren Werten für vm_size in den beiden Feldern Name und Restrictions sucht. Der HCL-Block storage_image_reference spezifiziert das von Azure bereitgestellte und für das Projekt zu verwendende Image des RHEL-Systems. Ein weiterer HCL-Block (storage_os_disk) richtet in Azure ein Speicherbereich für die Linux-VM mit geeigneten Parametern ein.Der os_profile-Block spezifiziert mit welchem Benutzername und Kennwort der Administrator sich an einer laufenden Linux-VM anmelden kann. Der os_profile_linux_config-Block ermöglicht es, zwischen einer automatischen SSH-Verbindung mit Provisioning und einer manuellen über das Internet umzuschalten. Anhand einer SSH-Verbindung erledigt das Projekt die Konfiguration der VM in mehreren Schritten:

# Die Public-IP-Adresse der Linux-VM (vm-apache) ermitteln
az vm show -d -g rg-apache -n vm-apache --query publicIps -o tsv
 
# Mit der Linux-VM (vm-apache) ueber SSH verbinden
# dabei die zuvor ermittelte Public-IP-Adresse 
# zum Beispiel <span class="hljs-number">20.105</span><span class="hljs-number">.149</span><span class="hljs-number">.210</span> fuer publicIpAddress einsetzen
ssh adminuser@publicIpAddress
 
# Ein Update ueber die Microsoft-Repositories durchfuehren
sudo yum update -y --disablerepo=<span class="hljs-string">'*'</span> --enablerepo=<span class="hljs-string">'*microsoft*'</span>
 
# Apache-Webserver (httpd) installieren
sudo yum -y install httpd
 
# Eine html-Datei erstellen 
echo <span class="hljs-string">'&lt;h2&gt;&lt;center&gt;Apache-Webserver bereitgestellt&lt;/center&gt;&lt;/h2&gt;'</span> &gt; index.html
echo <span class="hljs-string">'&lt;h2&gt;&lt;center&gt;und gestartet!&lt;/center&gt;&lt;/h2&gt;'</span> &gt;&gt; index.html
 
# Die erzeugte HTML-Datei ins Webdirectory von Apache verschieben
sudo mv index.html /var/www/html/
 
# Linux-Firewall fuer HTTP-Zugriffe freischalten
sudo firewall-cmd --zone=public --add-service=http
 
# Security-Kontexte für SELinux im html-Directory von Apache setzen
restorecon -r /var/www/html
 
# Apache-Webserver auf virtueller Linux-Maschine starten
sudo apachectl start
 
 
Der erste Schritt ermittelt über das Azure-CLI die öffentlich IP-Adresse der Linux-VM. Danach baut der ssh-Befehl über den mit Terraform definierten adminuser eine Verbindung auf.
Über die öffentliche IP-Adresseder Linux-VM zeigt der Webbrowser die index.html-Datei an(Bild 10) © Simon
Es folgt ein Update mit den Microsoft-Repositories und eine Installation von Apache (httpd) durch den yum-Package-Manger. Danach erzeugt man eine index.html-Datei für Testzwecke und verschiebt diese für externe Zugriffe in das HTML-Verzeichnis von Apache. Anschließend muss man die Firewall für HTTP-Zugriffe aus dem Internet freischalten und die in RHEL vorhandenen SELinux-Security-Kontexte für das html-Directory von Apache setzen. Der Befehl sudo apachectl start startet den Apache-Webserver, anschließend zeigt ein Webbrowser über die öffentliche IP-Adresse den Inhalt der index.html-Datei an (Bild 10).

Weitere Provisioner für spezielle Einsatzzwecke verfügbar

Neben den beiden Klassen Default-Creation- und dem Destroy-Time-Provisioner existiert noch eine weitere: Der Provisioner-Without-a-Resource. Dessen Deklaration in HCL erfolgt über einen resource-Block, der mit einem null_resource-Provider verknüpft ist. Damit zielt der Provisioner-Without-a-Resource auf die Erledigung von Aufgaben ab, die man keiner Ressource direkt zuordnen kann.Zusätzlich zu allen über einen resource-Block verfügbaren HCL-Konstrukten, besitzt der Provisioner-Without-a-Resource ein besonderes Argument/Identifier triggers. Dieses eignet sich speziell dazu, durch Interpolationen Werte über Attribute oder Variable zu ermitteln, um die zu ihm gehörenden provisioner-Blöcke erneut ausführen zu können. Insofern übernimmt der Provisioner-Without-a-Resource für Terraform eine gewisse Gedächtnisfunktion.Seitens der provisioner-Blöcke kennt Terraform in der aktuellen Version die folgenden drei gängigen Provisioner-Typen – diese orientieren sich an potenziellen Einsatzszenarien:Terraform kennt eine Infrastruktur-Umgebung und eine Zielumgebung. Während die Infrastruktur die Plattform oder Basis der Zielumgebung entspricht, handelt es sich beim eigentlichen Ziel, um die seitens der Endanwender gewünschte Laufzeitumgebung. Oder anders ausgedrückt: Terraform erzeugt auf der verwendeten Infrastruktur-Plattform (zum Beispiel Azure oder Docker) eine bestimmte Ziel/Laufzeitumgebung mit einer gewünschten Anwendung(slandschaft).Die Infrastruktur-Umgebung legt der über die Konfigurationsdatei von Terraform festgelegte provider-Block fest. Bei der Definition des provider-Blocks benötigt man die Deklaration einer Verbindung für die Anmeldung/Login in dessen Infrastruktur.Dabei erfolgt die eigentliche Anmeldung/Login über den Aufbau einer Verbindung, die eine Authentifizierung durchführt. Für diese greift Terraform auf die im provider-Block bereitgestellten Keys/IDs der Infrastruktur-Plattform zurück. Dies verdeutlicht, dass Terraform für den Verbindungsaufbau Administrationsrechte in der Infrastruktur-Umgebung erhält. Ergänzend kennt Terraform eine weitere Form von Verbindung, die HashiCorp über einen HCL-connection-Block realisiert. Ein connection-Block deklariert eine gewünschte Verbindung in eine Zielumgebung und baut diese auf, sobald es der von Terraform generierte Ausführungsplan erfordert. Innerhalb HCL kann ein connection-Block zum einen mit einem resource- zum anderen mit einem provisioner-Block verknüpft sein.Wobei ein connection-Block, der sich in einem provisioner-Block befindet, immer die Verbindungsdefinitionen eines resource-Blocks überschreibt. Somit lässt sich ein abgestuftes Sicherheitskonzept innerhalb einer Infrastruktur-Plattform realisieren. Als Kommunikationsprotokoll steht für den Verbindungsaufbau entweder SSH oder WinRM zur Verfügung – dazu steht das type-Argument ssh oder winrm zur Verfügung. Als weitere Argumente kommen user, password und host zum Einsatz.Da in der Regel SSH der am häufigsten eingesetzten Kommunikationsform entspricht, hat HashiCorp in Terraform eine Prüfung der Host-Keys von SSH ausgeschaltet. Zusätzlich können spezielle Infrastruktur-Elemente einen eigenen Mechanismus zum Verbindungsaufbau besitzen. Im Beispiel der virtuellen Linux-Umgebung kann man über einen os_profile_linux_config-Block sowohl eine normale Authentifizierung als auch eine über SSH-Login nutzen:

os_profile_linux_config {
      # Authentifizierung fuer SSH-Key aktivieren
      # disable_password_authentication = <span class="hljs-literal">true</span>
 
      # Authentifizierung fuer User/Kennwort einschalten
      # entspricht der Standardeinstellung
      disable_password_authentication = <span class="hljs-literal">false</span>
 
/* Definition des ssh_keys-Blocks fuer SSH-Verbindung
      ssh_keys {
        # Einzig erlaubte Pfad aufgrund einer Einschraenkung
        # <span class="hljs-keyword">in</span> Microsoft Azure
        path = <span class="hljs-string">"/home/${var.admin-username}/.ssh/authorized_keys"</span>
        # Inhalt der Datei uebergeben – ssh_key siehe Listing <span class="hljs-number">2</span>
<span class="hljs-number">1</span>
        key_data = <span class="hljs-string">"${file("</span>${var.ssh_key}<span class="hljs-string">")}"</span>
      }
*/
}
 
 
Beim CDKTF (Cloud Development Kit for Terraform) handelt es sich um eine Terraform-Schnittstelle die sich derzeit in Entwicklung befindet. HashiCorp will mit dem CDKTF für den DevOps-Bereich weitere Programmiersprachen erschließen. Damit entfällt für den Einsatz von Terraform das Erlernen von HCL. Es lassen sich bereits im Projektteam vorhandene Kenntnisse zu den Programmiersprachen direkt nutzen.

Das CDKTF erschließt Terraform für weitere Programmiersprachen

Aktuell realisiert HashiCorp das CDKTF für die Programmiersprachen C#, Go, Java, Python und TypeScript. Als zentrale Anforderung für CDKTF gilt für HashiCorp die Interoperabilität mit bereits vorhandenen Terraform-Providern. Neben der OSS-Edition von Terraform wird das CDKTF auch für die Terraform Cloud-Editions und für Terraform Enterprise zur Verfügung stehen.Bereits jetzt macht HashiCorp CDKTF als eigenständiges Produkt in einem npm-Package über das Internet verfügbar. Für das Arbeiten mit CDKTF in Testprojekten benötigt man eine Installation von Terraform und des JavaScript-Runtime-Systems Node.js. Die stabile Version des cdktf-cli-Packages richtet der Befehl npm install --global cdktf-cli@latest ein.Das Hilfesystem des CLI von CDKTF zeigt der Befehl: cdktf help über ein Terminalfenster (Eingabeaufforderung) an. Die einzelnen Kommandos des CLI erkundet man über cdktf <subcommand> --help. Die Auswahl der gewünschten Programmiersprache (zum Beispiel Python) erfolgt bei der Neuanlage eines Projekts über die template-Option: cdktf init --template=python --local. Für Python benötigt man zusätzlich eine Installation des Pipenv-Package-Managers.Die seitens des CDKTF-Projekts erforderlichen Terraform-Provider deklariert man in der Datei cdktf.json, die sich im Projektverzeichnis befindet. Anschließend lädt der Befehl cdktf get die gewünschten Terraform-Provider herunter und generiert die benötigten Python-Moduln für das Projekt. Die Deklaration der gewünschten Infrastruktur-Elemente für den in cdktf.json gewählten Provider erfolgt in main.py dem Python-Hauptprogramm. Dazu benutzt man die heruntergeladenen Python-Moduln, die Terraform-Provider und deren Objekte, die das CDKTF als eigenes Framework cdktf für Python bereitstellt.Die Bereitstellung der Infrastruktur in der gewählten IaaS-Plattform erledigt der Befehl cdktf deploy. Dieser Befehl führt im Hintergrund terraform apply aus. Entsprechend löscht cdktf destroy alle bereitgestellten Infrastruktur-Elemente. Um durchgängig mit Terraform-Befehlen zu arbeiten, setzt man das synthesize-Kommando ein: cdktf synth. Dieses Subkommando erzeugt den Ordner cdktf.out über den man anschließend alle Terraform-Befehle ausführen kann.

Links zum Thema

<b>◼ <a href="https://www.hashicorp.com/products/terraform" rel="noopener" target="_blank">Produkt-Homepage von HashiCorp Terraform</a></b>

Neueste Beiträge

DWX hakt nach: Wie stellt man Daten besonders lesbar dar?
Dass das Design von Websites maßgeblich für die Lesbarkeit der Inhalte verantwortlich ist, ist klar. Das gleiche gilt aber auch für die Aufbereitung von Daten für Berichte. Worauf besonders zu achten ist, erklären Dr. Ina Humpert und Dr. Julia Norget.
3 Minuten
27. Jun 2025
DWX hakt nach: Wie gestaltet man intuitive User Experiences?
DWX hakt nach: Wie gestaltet man intuitive User Experiences? Intuitive Bedienbarkeit klingt gut – doch wie gelingt sie in der Praxis? UX-Expertin Vicky Pirker verrät auf der Developer Week, worauf es wirklich ankommt. Hier gibt sie vorab einen Einblick in ihre Session.
4 Minuten
27. Jun 2025
„Sieh die KI als Juniorentwickler“
CTO Christian Weyer fühlt sich jung wie schon lange nicht mehr. Woran das liegt und warum er keine Angst um seinen Job hat, erzählt er im dotnetpro-Interview.
15 Minuten
27. Jun 2025
Miscellaneous

Das könnte Dich auch interessieren

UIs für Linux - Bedienoberflächen entwickeln mithilfe von C#, .NET und Avalonia
Es gibt viele UI-Frameworks für .NET, doch nur sehr wenige davon unterstützen Linux. Avalonia schafft als etabliertes Open-Source-Projekt Abhilfe.
16 Minuten
16. Jun 2025
Mythos Motivation - Teamentwicklung
Entwickler bringen Arbeitsfreude und Engagement meist schon von Haus aus mit. Diesen inneren Antrieb zu erhalten sollte für Führungskräfte im Fokus stehen.
13 Minuten
19. Jan 2017
Evolutionäres Prototyping von Business-Apps - Low Code/No Code und KI mit Power Apps
Microsoft baut Power Apps zunehmend mit Features aus, um die Low-Code-/No-Code-Welt mit der KI und der professionellen Programmierung zu verbinden.
19 Minuten
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige