Zurück zum Katalog
Season 26 10 Episoden 40 min 2026

Azure Pipelines

Ausgabe 2026. Machen Sie sich mit Azure DevOps (ADO) vertraut und lernen Sie, wie man gut strukturierte Pipelines aufbaut. Erfahren Sie Best Practices, verwalten Sie Variables und Secrets und erhalten Sie praktische Ratschläge für den Einsatz in der Enterprise-Softwareentwicklung.

CI/CD DevOps
Azure Pipelines
Aktuelle Wiedergabe
Click play to start
0:00
0:00
1
YAML vs. Classic Pipelines
Wir stellen Azure DevOps Pipelines vor und untersuchen den entscheidenden Wechsel von klassischen UI-basierten Pipelines zu Pipeline-as-Code mit YAML. Sie erfahren, warum die Speicherung von Pipeline-Konfigurationen zusammen mit Ihrem Anwendungscode der Branchenstandard für Enterprise-Software ist.
3m 43s
2
Anatomie einer Pipeline: Stages, Jobs und Steps
Tauchen Sie in die strukturelle Hierarchie von Azure Pipelines ein. Sie lernen, wie Sie Ihren CI/CD-Prozess mit Stages logisch organisieren, Workloads mit Jobs verteilen und exakte Befehle mit Steps ausführen.
4m 25s
3
Ausführungskontext: Agents und Demands
Entdecken Sie, wie Azure Pipelines Ihren Code mithilfe von Agents ausführen. Wir behandeln die Unterschiede zwischen Microsoft-hosted und Self-hosted Agents und zeigen, wie Sie Demands nutzen, um Jobs an die richtige Infrastruktur weiterzuleiten.
4m 04s
4
Automatisierung des Workflows mit Triggers
Lernen Sie, wie Ihre Pipelines automatisch auf Ereignisse reagieren. Wir untersuchen Continuous Integration (CI) Triggers, Pull Request (PR) Triggers und Scheduled Triggers, um komplexe Release-Zyklen zu orchestrieren.
3m 40s
5
State Management: Variables und Variable Groups
Meistern Sie die Kunst, State und Konfigurationen durch Ihre Pipelines zu leiten. Diese Episode schlüsselt vordefinierte System-Variables, benutzerdefinierte Pipeline-Variables auf und zeigt, wie Konfigurationen projektübergreifend mit Variable Groups geteilt werden.
3m 47s
6
Sicherung von Secrets mit Azure Key Vault
Hören Sie auf, sensible Anmeldeinformationen in Ihrem CI/CD-Tool zu speichern. Wir erklären, wie Sie Azure Key Vault in Azure Pipelines integrieren, um Passwörter, API-Keys und Connection Strings zur Laufzeit dynamisch abzurufen.
4m 13s
7
Dynamische Steuerung: Conditions und Expressions
Lernen Sie, wie Sie Ihre Pipelines intelligent und reaktiv machen. Wir tauchen in benutzerdefinierte Conditions und Expressions ein, um dynamisch zu steuern, welche Jobs und Steps basierend auf Variablenwerten und vorherigen Job-Ergebnissen ausgeführt werden.
3m 33s
8
Enterprise-Wiederverwendbarkeit: YAML Templates
Skalieren Sie Ihre Pipeline-Architektur über Dutzende von Repositories hinweg mithilfe von YAML Templates. Lernen Sie den Unterschied zwischen 'Includes' und 'Extends' kennen und erfahren Sie, wie Sie Sicherheitsrichtlinien unternehmensweit durchsetzen.
4m 20s
9
Gezielte Deployments mit Environments
Heben Sie Ihre Pipeline vom bloßen 'Ausführen von Code' auf die Verwaltung tatsächlicher Deployments. Wir behandeln den Deployment Job-Typ, Environments und Deployment-Strategien wie runOnce und Canary.
4m 25s
10
Enterprise Gates: Approvals und Checks
Setzen Sie Leitplanken für Ihre automatisierten Deployments. In dieser letzten Episode untersuchen wir, wie Sie Approvals, Branch Control und Exclusive Locks für Ihre Environments konfigurieren, um die Production-Umgebung zu schützen.
3m 54s

Episoden

1

YAML vs. Classic Pipelines

3m 43s

Wir stellen Azure DevOps Pipelines vor und untersuchen den entscheidenden Wechsel von klassischen UI-basierten Pipelines zu Pipeline-as-Code mit YAML. Sie erfahren, warum die Speicherung von Pipeline-Konfigurationen zusammen mit Ihrem Anwendungscode der Branchenstandard für Enterprise-Software ist.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. Azure Pipelines, Folge 1 von 10. Du klickst dich durch ein Web-Interface, verschiebst ein paar Boxen, und dein Build läuft perfekt. Dann ändert jemand eine Einstellung, der Build schlägt fehl, und du hast absolut keine History darüber, wer was warum gemacht hat. Das ist die Falle bei visuellen Editoren, und genau deshalb ist der Wechsel von der Classic UI zu YAML-Pipelines das Fundament von moderner CI/CD. Azure Pipelines bietet dir zwei verschiedene Wege, dein Software-Delivery zu automatisieren. Die ältere Methode ist das Classic Interface. Es basiert auf einem grafischen Web-Portal, in dem du Tasks über Formulare und Dropdown-Menüs konfigurierst. Das fühlt sich am Anfang sehr zugänglich an. Aber wenn dein System wächst, wird der Classic-Ansatz zu einem echten Risiko. Die Konfiguration liegt in der Azure DevOps-Datenbank, komplett getrennt von deinem eigentlichen Source Code. YAML-Pipelines ersetzen das durch das Konzept von Pipeline-as-Code. Anstatt Settings in einem Web-Portal zu konfigurieren, definierst du deinen kompletten Build- und Release-Prozess in einer einfachen Textdatei. Du committest diese YAML-Datei direkt in dein Repository und behältst sie so direkt neben dem Application Code, den sie builden soll. Manche Entwickler zögern beim Umstieg, weil sie befürchten, dass eine textbasierte Konfiguration die Features im Vergleich zum visuellen Editor einschränken könnte. Das ist nicht der Fall. YAML-Pipelines bieten volle Feature-Parität für Continuous Integration und Continuous Delivery. Microsoft betrachtet die Classic UI als Legacy und fokussiert neue Entwicklungen auf YAML. Es ist der Enterprise-Standard für Wiederholbarkeit und Auditing. Stell dir ein Team vor, das eine komplexe Drag-and-Drop-Build-Definition in eine Repository-Datei migriert. Im alten System bedeutete das Testen eines neuen Build Steps, die gemeinsame Pipeline-Konfiguration zu bearbeiten, was potenziell den Build für den Rest des Teams kaputt machen konnte. Mit YAML wird die Pipeline genauso versioniert wie deine Application. Wenn du einen Feature Branch erstellst, um eine Core Dependency upzugraden, kannst du die YAML-Datei in genau demselben Branch anpassen. Die aktualisierte Build-Logik gilt nur für deinen isolierten Branch. Der Rest des Teams nutzt weiterhin die Pipeline des Main Branches ohne Unterbrechung. Hier ist die entscheidende Erkenntnis. Weil deine Pipeline jetzt einfach nur eine Textdatei in Git ist, fällt sie unter deinen Standard-Code-Review-Prozess. Wenn du dich von der UI verabschiedest, kann niemand mehr heimlich ein Deployment Target ändern oder einen Testing Step überspringen. Jede Änderung an der Pipeline erfordert einen Commit. Sie erfordert einen Pull Request. Sie erfordert ein Approval von einem Kollegen. Du bekommst einen permanenten, unbestreitbaren Audit Trail für jede Änderung. Außerdem ist dein Pipeline State für immer an deinen Application State gekoppelt. Wenn du einen Rollback auf ein Release von vor sechs Monaten machen musst, ist die YAML-Datei von genau diesem Zeitpunkt in deiner Git History gesichert. Du hast garantiert die richtigen Build Instructions für diese spezifische ältere Version des Codes. Beim Umstieg auf YAML geht es darum, deinen Delivery-Mechanismus mit genau derselben Engineering-Sorgfalt zu behandeln wie die Software, die er ausliefert. Wenn du diese Episoden hilfreich findest und die Show unterstützen möchtest, kannst du auf Patreon nach DevStoriesEU suchen. Das war's für diese Folge. Danke fürs Zuhören, und keep building!
2

Anatomie einer Pipeline: Stages, Jobs und Steps

4m 25s

Tauchen Sie in die strukturelle Hierarchie von Azure Pipelines ein. Sie lernen, wie Sie Ihren CI/CD-Prozess mit Stages logisch organisieren, Workloads mit Jobs verteilen und exakte Befehle mit Steps ausführen.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. Azure Pipelines, Folge 2 von 10. Der Unterschied zwischen einer Stage und einem Job bestimmt genau, wo deine Pipeline fehlschlägt, wenn du die Agent Allocation falsch verstehst. Machst du das falsch, könnten deine Build Artifacts plötzlich fehlen, genau dann, wenn es Zeit ist, deine Tests auszuführen. Die Anatomie einer Pipeline – genauer gesagt Stages, Jobs und Steps – ist die Struktur, die das löst. Eine Pipeline ist ein hierarchisches System. Diese Struktur erzwingt strikt, wie die Arbeit aufgeteilt wird, wo sie läuft und in welcher Reihenfolge. Die Hierarchie ist drei Ebenen tief. Stages enthalten Jobs, und Jobs enthalten Steps. Eine Stage fungiert als logische Grenze. Sie gruppiert zusammengehörige Arbeit. Bei einer Enterprise-Anwendung nutzt du Stages, um die Hauptphasen deines Software Delivery Cycles zu trennen. Du könntest eine Build-Stage definieren, gefolgt von einer separaten Testing-Stage. Standardmäßig laufen Stages sequenziell. Eine Stage muss komplett abgeschlossen sein, bevor die nächste beginnt. Sie dienen als organisatorische Grenzen für deinen gesamten Prozess. Hier ist die wichtigste Erkenntnis. Engineers verwechseln häufig Stages und Jobs und behandeln sie austauschbar. Sie erfüllen aber nicht denselben Zweck. Eine Stage ist ein rein logischer Container. Ein Job ist eine physische Execution Boundary. Ein Job definiert die tatsächliche Umgebung, in der dein Code läuft. Jeder Job wird einem Agent zugewiesen, also der Maschine oder dem Container, der deine Tasks ausführt. Alle Operationen innerhalb eines einzelnen Jobs werden auf genau diesem einen Agent ausgeführt. Weil Jobs die Unit of Execution sind, verhalten sie sich anders als Stages. Wenn du drei Jobs in einer einzigen Stage platzierst, versucht Azure Pipelines, diese drei Jobs parallel auf drei verschiedenen Agents auszuführen, vorausgesetzt, du hast die nötige Kapazität. Das bedeutet, dass Jobs kein lokales File System oder Memory teilen. Wenn Job A deinen Application Code kompiliert, kann Job B nicht einfach auf die kompilierten Binaries vom lokalen Laufwerk zugreifen. Job B läuft auf einer komplett anderen Maschine. Wenn du zwei Prozesse hast, die sequenziell denselben lokalen Speicherplatz nutzen müssen, müssen sie im selben Job platziert werden. Innerhalb eines Jobs definierst du Steps. Ein Step ist der kleinste Baustein einer Pipeline. Er ist eine konkrete Anweisung, normalerweise ein Task oder ein Script. Weil Steps innerhalb eines bestimmten Jobs leben, laufen sie alle sequenziell auf exakt demselben Agent. Step eins könnte ein Task sein, um deinen Source Code herunterzuladen. Step zwei könnte ein Bash-Script sein, das deinen Compiler ausführt. Da sie dieselbe Execution Environment teilen, hat Step zwei sofortigen, direkten Zugriff auf die Dateien, die Step eins gerade heruntergeladen hat. Wende das mal auf die Strukturierung einer Enterprise-Pipeline an, die eine Application baut, Unit Tests ausführt und das Ergebnis verpackt. Du erstellst eine einzige Stage namens Continuous Integration. Innerhalb dieser Stage definierst du zwei separate Jobs, um die Ausführung zu beschleunigen. Job eins übernimmt den primären Build. Seine Steps checken den Code aus, führen den Compiler aus und verpacken das Binary. Job zwei übernimmt die Static Code Analysis und das Standalone Unit Testing. Weil das separate Jobs innerhalb derselben Stage sind, laufen sie gleichzeitig auf separaten Agents. Sie blockieren sich nicht gegenseitig, aber sie teilen sich auch kein File System. Wenn Job zwei das von Job eins erstellte Binary braucht, musst du Job eins explizit anweisen, das Artifact in den Pipeline Storage zu publishen, und Job zwei anweisen, es herunterzuladen. Die Struktur bestimmt die Möglichkeiten in deiner Continuous Integration Architektur. Nutze Stages, um deinen Workflow logisch zu organisieren, aber verlass dich auf Jobs, um genau zu steuern, wo und wie deine Ausführung über Maschinen hinweg skaliert. Danke fürs Zuhören. Hoffentlich hast du etwas Neues gelernt.
3

Ausführungskontext: Agents und Demands

4m 04s

Entdecken Sie, wie Azure Pipelines Ihren Code mithilfe von Agents ausführen. Wir behandeln die Unterschiede zwischen Microsoft-hosted und Self-hosted Agents und zeigen, wie Sie Demands nutzen, um Jobs an die richtige Infrastruktur weiterzuleiten.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. Azure Pipelines, Folge 3 von 10. Jede Pipeline braucht Rechenleistung, aber die Wahl des falschen Execution Contexts kann deine Build-Zeiten unbemerkt verdoppeln. Du schreibst vielleicht perfekten Pipeline-Code, aber wenn dein Build bei jedem einzelnen Run Gigabytes an Dependencies komplett neu herunterladen muss, verschwendet dein Team Stunden mit Warten. Genau wegen dieses Flaschenhalses musst du den Execution Context verstehen, insbesondere Agents und Demands. Um Code zu builden oder Software in Azure DevOps zu deployen, brauchst du einen Agent. Ein Agent ist einfach eine installierbare Software, die sich mit deiner Azure DevOps-Organisation verbindet, auf Arbeit wartet und Jobs nacheinander ausführt. Jeder Agent läuft auf einer Host-Maschine, und du wählst generell zwischen zwei Arten von Hosts. Du kannst Microsoft-hosted Agents nutzen, oder du nutzt Self-hosted Agents. Microsoft-hosted Agents sind aus Bequemlichkeit die Standardwahl. Du fragst nach einer Maschine, und Microsoft stellt dir eine aus ihrem Cloud-Pool zur Verfügung. Du musst nie das Betriebssystem patchen oder die Agent-Software upgraden. Aber diese Bequemlichkeit hat einen Haken, über den viele Teams stolpern. Hier ist die wichtigste Erkenntnis: Ein Microsoft-hosted Agent gibt dir für jeden einzelnen Pipeline-Run eine brandneue, komplett frische virtuelle Maschine. Sie merkt sich deinen letzten Build nicht. Wenn dein Job fertig ist, wird diese Maschine dauerhaft zerstört. Wenn dein Build einen gigabyte-großen Package-Cache braucht, wird er ihn bei jedem Run von null an über das Netzwerk herunterladen, es sei denn, du fügst explizite Pipeline-Caching-Steps hinzu, um diese Daten zu speichern und wiederherzustellen. Wenn du vermeiden willst, jedes Mal alles herunterzuladen, oder wenn dein Build hinter eine strenge Firmen-Firewall greifen muss, nutzt du Self-hosted Agents. Du installierst die Agent-Software auf deiner eigenen Infrastruktur. Das kann ein Server in deinem lokalen Rechenzentrum sein oder eine persistente virtuelle Maschine in deinem eigenen Cloud-Tenant. Weil die Maschine zwischen den Runs erhalten bleibt, bleiben deine Package-Caches, heruntergeladenen Software Development Kits und inkrementellen Build-Dateien genau dort, wo du sie gelassen hast. Das beschleunigt die Execution Times drastisch. Wenn du einen modernen Mittelweg suchst, kannst du dir Managed DevOps Pools ansehen. Diese fungieren als Scale-Set-Alternative, bei der du Custom Base Images und Größen definierst, und Azure übernimmt das automatische Provisioning und Scaling. Damit hätten wir abgedeckt, was Agents sind. Aber woher weiß Azure DevOps nun, welchen Agent es für einen bestimmten Job nutzen soll? Das basiert auf einem System von Capabilities und Demands. Jeder Self-hosted Agent meldet eine Liste von Capabilities an den Server zurück. Viele davon werden von der Agent-Software automatisch entdeckt, wie der Betriebssystemtyp oder der Pfad zu installierten Tools wie Node oder Python. Du kannst auch Custom User Capabilities definieren, um vielleicht eine bestimmte Maschine so zu kennzeichnen, dass sie eine bestimmte Grafikkarte hat. In deiner Pipeline-Definition schreibst du Demands, die zu diesen Capabilities passen. Ein Demand garantiert, dass dein Job nur an einen Agent geroutet wird, der exakt die Capability besitzt, die du anfragst. Stell dir ein Szenario vor, in dem du eine iOS-Anwendung kompilierst. Das Kompilieren für iOS erfordert Xcode, was zwingend Apple-Hardware voraussetzt. Angenommen, du hast einen einzigen Pool von zwanzig Self-hosted Agents, aber nur zwei davon sind Mac Minis. Du fügst deinem Pipeline-Job einfach einen Demand für die macOS-Capability hinzu. Wenn Azure DevOps den Run evaluiert, filtert es alle Windows- und Linux-Maschinen im Pool heraus und routet deinen iOS-Build direkt zu einem der verfügbaren Mac Minis. Deine Wahl des Agents diktiert deine gesamte Pipeline-Architektur. Mit Microsoft-hosted bist du raus aus der Serverwartung, musst dafür aber den State selbst managen, während Self-hosted den Infrastruktur-Aufwand gegen rohe Geschwindigkeit und persistente Caches eintauscht. Das war's für diese Folge. Danke fürs Zuhören und keep building!
4

Automatisierung des Workflows mit Triggers

3m 40s

Lernen Sie, wie Ihre Pipelines automatisch auf Ereignisse reagieren. Wir untersuchen Continuous Integration (CI) Triggers, Pull Request (PR) Triggers und Scheduled Triggers, um komplexe Release-Zyklen zu orchestrieren.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. Azure Pipelines, Folge 4 von 10. Du denkst vielleicht, deine Pipeline wird nur ausgeführt, wenn jemand einen Commit pusht. Aber in einem ausgereiften System sind Pipelines viel proaktiver und führen verschiedene Arten der Validierung zu völlig unterschiedlichen Zeitpunkten aus. Wir sprechen über die Automatisierung des Workflows mit Triggern. Trigger definieren genau, welche Events die Ausführung deiner Pipeline auslösen. Der häufigste Typ ist der Continuous Integration, oder CI-Trigger. Ein CI-Trigger feuert automatisch, sobald Code in einen bestimmten Branch gepusht wird. In deiner Pipeline-Datei definierst du das mit einem einfachen Trigger-Block. Du kannst ihm sagen, dass er auf den Main-Branch hören soll, aber bestimmte Dateipfade, wie zum Beispiel Dokumentationsordner, ignorieren soll. Das verhindert, dass deine Pipeline Zeit damit verschwendet, die Anwendung zu bauen, wenn jemand nur einen Tippfehler in einer Textdatei gefixt hat. Das kümmert sich um Code, der bereits in einen Branch wandert. Aber normalerweise willst du Fehler abfangen, bevor der Merge passiert. Das ist der Job von Pull Request Triggern. Ein PR-Trigger wird ausgelöst, wenn ein Pull Request geöffnet wird, oder wenn neue Commits in diesen bestehenden Pull Request gepusht werden. Sein Hauptzweck ist es, den Target-Branch zu schützen, indem der eingehende Code validiert wird. Hier ist die wichtigste Erkenntnis. Es gibt eine typische Falle, in die Entwickler bei PR-Triggern tappen. Wenn ein Pull Request eine Pipeline triggert, wertet Azure Pipelines die Konfiguration anhand der YAML-Datei im Source-Branch aus, nicht im Target-Branch. Die Logik für die Validierung kommt aus dem Feature-Branch selbst. Wenn du Änderungen an der Pipeline-Konfiguration in deinem Feature-Branch machst, gelten diese Änderungen für den PR-Run. PR-Trigger müssen schnell sein. Wenn du einen PR-Trigger konfigurierst, solltest du ihn für schnelle Operationen nutzen, wie Unit Tests und Code Linting. Du willst, dass Entwickler ihr Feedback in Minuten bekommen. Aber manche Operationen, wie tiefe statische Analyse oder aufwendige Security Scans, dauern viel länger. Ein zweistündiger Security Scan wird dein Team lahmlegen, wenn du ihn an einen PR-Trigger hängst. Das bringt uns zum dritten Typ: Scheduled Triggers. Anstatt auf Code-Bewegungen zu reagieren, führen Scheduled Triggers Pipelines basierend auf der Uhrzeit aus. Sie verwenden die Standard-Cron-Syntax, um bestimmte Tage und Uhrzeiten für den Pipeline-Run zu definieren. Du definierst einen Schedules-Block in deiner YAML-Datei, gibst die Cron Expression an und listest die Branches auf, die du bauen willst. Du kannst diese Trigger kombinieren, um einen effizienten Workflow zu bauen. Tagsüber führen deine PR-Trigger schnelle Unit Tests auf Feature-Branches aus, um die Entwicklung am Laufen zu halten. Währenddessen konfigurierst du einen Scheduled Trigger, der jeden Tag um Mitternacht diesen zweistündigen Security Scan auf dem Main-Branch anstößt. Du kannst den Scheduled Trigger sogar so einstellen, dass er nur ausgeführt wird, wenn seit der letzten Nacht neue Commits gemerged wurden. Das überspringt den Run an einem ruhigen Wochenende komplett und spart Compute-Kosten. Wenn du diese Trigger zusammen nutzt, kannst du schnelles Entwickler-Feedback von tiefer Systemvalidierung entkoppeln. Trigger sind nicht nur Startknöpfe; sie sind die architektonischen Kontrollen, die entscheiden, wie und wann deine Compute-Ressourcen verbraucht werden. Danke fürs Zuhören – bis zum nächsten Mal.
5

State Management: Variables und Variable Groups

3m 47s

Meistern Sie die Kunst, State und Konfigurationen durch Ihre Pipelines zu leiten. Diese Episode schlüsselt vordefinierte System-Variables, benutzerdefinierte Pipeline-Variables auf und zeigt, wie Konfigurationen projektübergreifend mit Variable Groups geteilt werden.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. Azure Pipelines, Folge 5 von 10. Environment-Namen direkt in deinen YAML-Files zu hardcoden, ist Technical Debt, die nur darauf wartet, bei deiner nächsten Infrastructure Migration zu explodieren. Wenn sich diese Namen ändern, willst du nicht Dutzende von Repositories durchsuchen müssen, um sie zu updaten. State Management mit Variables und Variable Groups löst dieses Problem, indem es deine Configuration zentralisiert. Die Pipeline weiß out of the box bereits eine Menge über ihren Execution Context. Azure stellt vordefinierte System Variables bereit, um deiner Pipeline ohne manuelles Setup Environmental Awareness zu geben. Wenn du zum Beispiel wissen musst, welcher Branch den Run getriggert hat, nutzt du Build dot SourceBranch. Wenn du die eindeutige ID des aktuellen Runs brauchst, nutzt du Build dot BuildId. Diese werden automatisch befüllt und sind direkt bereit zum Auslesen. Wenn du Custom Configuration brauchst, definierst du Inline Variables. Diese packst du direkt in dein YAML-File unter den Variables-Block. Das ist perfekt für Werte, die spezifisch für eine einzelne Pipeline sind, wie zum Beispiel ein Build Configuration Flag oder ein lokaler File Path. So bleibt die Logik self-contained. Inline Variables stoßen jedoch an ihre Grenzen, wenn du skalierst. Stell dir ein Szenario mit drei separaten Microservice-Pipelines vor. Sie alle müssen den Namen des Target Deployment Environments und ein Set von shared API Endpoint URLs kennen. Wenn du diese inline definierst, wiederholst du dich über drei Repositories hinweg. Wenn sich ein Endpoint ändert, musst du drei Pull Requests machen. Die Lösung ist eine Library Variable Group. Das ist ein zentralisierter Key-Value Store innerhalb der Azure DevOps Library. Du erstellst die Gruppe einmalig im User Interface, befüllst sie mit deinen Environment-Namen und API Endpoints und referenzierst diese Gruppe dann über ihren Namen im Variables-Block aller drei Microservice-Pipelines. Wenn der Tag der Migration kommt, updatest du die Variable Group im DevOps-Portal, und jede Pipeline nutzt bei ihrem nächsten Run sofort die neuen Werte. So setzt du das Don't Repeat Yourself-Prinzip durch und behältst deine shared Configuration an genau einem Ort. Hier ist der entscheidende Punkt. Wie du eine Variable aufrufst, bestimmt, wann Azure sie evaluiert. Es gibt zwei primäre Syntaxen, und wenn du sie vermischst, geht deine Pipeline kaputt. Die erste ist die Macro Syntax, geschrieben als Dollarzeichen, gefolgt von runden Klammern, die den Variablennamen umschließen. Macro Syntax wird zur Runtime evaluiert. Azure ersetzt die Variable genau vor der Ausführung des spezifischen Tasks, der sie nutzt. Die zweite ist die Template Expression Syntax, geschrieben als Dollarzeichen, gefolgt von doppelten geschweiften Klammern, die das Wort variables dot und deinen Variablennamen enthalten. Template Expressions werden zur Compile Time evaluiert, noch bevor die Pipeline überhaupt zu laufen beginnt. Wenn du versuchst, eine Variable zu nutzen, um zu entscheiden, ob eine Stage überhaupt laufen soll, oder um einen Loop über eine Liste von Jobs zu definieren, musst du die Template Expression Syntax verwenden. Die Pipeline braucht diesen Wert im Voraus, um den Execution Graph zu bauen. Wenn ein Wert während des Pipeline Runs dynamisch durch ein Script generiert wird, musst du die Macro Syntax verwenden, weil der Wert zur Compile Time schlichtweg nicht existiert. Meistere den Unterschied zwischen Compile Time Template Expressions und Runtime Macros, und du wirst die frustrierendsten Variable Parsing Errors in deiner Pipeline-Entwicklung eliminieren. Das war's für diese Folge. Bis zum nächsten Mal!
6

Sicherung von Secrets mit Azure Key Vault

4m 13s

Hören Sie auf, sensible Anmeldeinformationen in Ihrem CI/CD-Tool zu speichern. Wir erklären, wie Sie Azure Key Vault in Azure Pipelines integrieren, um Passwörter, API-Keys und Connection Strings zur Laufzeit dynamisch abzurufen.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. Azure Pipelines, Folge 6 von 10. Ein Secret in deinen Pipeline-Logs zu maskieren, bedeutet nicht, dass es sicher ist, wenn du es direkt in deine CI/CD-Konfiguration eingetippt hast. Wenn ein Datenbankadministrator dieses Passwort rotiert, schlagen deine Deployments sofort fehl, bis jemand die Pipeline manuell updatet. Um das zu beheben, rufst du Credentials dynamisch über den Azure Key Vault ab. Oft definiert man Secret-Variablen direkt in der Azure DevOps UI. Du klickst auf das Schloss-Icon, der Text verschwindet, und du gehst davon aus, dass du sicher bist. Secrets in der Pipeline UI zu speichern, führt zu einer fragmentierten Security Posture. Wenn eine Enterprise-Policy vorschreibt, Credentials alle dreißig Tage zu rotieren, ist das Suchen und Updaten jeder Pipeline-Variable in Dutzenden von Projekten ein operativer Albtraum. Außerdem verlierst du das zentrale Auditing darüber, wer oder was auf diese Daten zugegriffen hat. Der Secure-by-Design-Ansatz ist, Secrets dynamisch zur Runtime aus einem Enterprise Key Vault abzurufen. Die Pipeline besitzt das Secret niemals. Sie leiht es sich nur genau dann, wenn es gebraucht wird. Um das umzusetzen, nutzt du den Azure Key Vault Pipeline Task. Zuerst braucht die Pipeline die Berechtigung, in deinen Vault zu schauen. Du konfigurierst eine Azure Resource Manager Service Connection in deinem DevOps-Projekt. Diese Verbindung basiert entweder auf einem Service Principal oder einer Managed Identity. Anschließend gewährst du dieser Identität expliziten Read-Access auf die Secrets in deinem Key Vault, und zwar über Azure Role-Based Access Control oder Vault Access Policies. Hier ist der entscheidende Punkt: Die Pipeline läuft als diese Identität und beweist Azure, wer sie ist, bevor sie sensible Daten abruft. Das passt zu einem Zero-Trust-Modell. Es existieren keine permanenten Credentials in deinem Code-Repository oder deinen Pipeline-Definitions. Stell dir eine Pipeline vor, die kurz vor dem Deployment eines Backend-Services einen hochsensiblen Database Connection String abruft. In deiner Pipeline-Definition fügst du direkt vor dem Deployment-Step den Azure Key Vault Task Version zwei hinzu. Du übergibst drei wesentliche Inputs an diesen Task. Erstens gibst du den Namen deiner Azure Subscription Service Connection an. Zweitens den Namen deines eigentlichen Key Vaults. Und drittens definierst du eine Liste der spezifischen Secret-Namen, die du downloaden möchtest. Du kannst zwar einen Wildcard-Asterisk übergeben, um alles herunterzuladen, aber das Least-Privilege-Prinzip besagt, dass du nur genau das anfragst, was du auch brauchst. Du forderst also gezielt ein Secret namens production-database-connection an. Wenn die Pipeline diesen Step erreicht, ruft sie den Vault auf. Wenn die Identität die Berechtigung hat, gibt der Vault das Secret zurück. Der Task nimmt diesen sicheren Wert und wandelt ihn automatisch in eine Standard-Pipeline-Variable um. Der Name der neuen Pipeline-Variable entspricht exakt dem Namen des Secrets im Vault. Dein nächster Deployment-Step kann nun auf diese Variable referenzieren, um die Datenbank zu konfigurieren, genau wie bei jedem Standard-Text-Input. Azure Pipelines registriert diesen heruntergeladenen Wert im Hintergrund außerdem als Secret-Variable. Falls ein Skript den Connection String versehentlich in der Konsole ausgibt, fängt das System das ab und ersetzt den tatsächlichen Text in den Logs durch Sternchen. Dynamisches Fetching stellt sicher, dass deine Pipeline automatisch beim allernächsten Run den neuen String verwendet, wenn das Datenbankteam das primäre Passwort im Vault updatet – ganz ohne manuelles Eingreifen. Dein Continuous Delivery System sollte wie ein sicherer Kurier agieren, der den verschlossenen Aktenkoffer kurz vor der Delivery abholt, anstatt eine dauerhafte Kopie im Depot zu lagern. Das war’s für diese Folge. Danke fürs Zuhören und keep building!
7

Dynamische Steuerung: Conditions und Expressions

3m 33s

Lernen Sie, wie Sie Ihre Pipelines intelligent und reaktiv machen. Wir tauchen in benutzerdefinierte Conditions und Expressions ein, um dynamisch zu steuern, welche Jobs und Steps basierend auf Variablenwerten und vorherigen Job-Ergebnissen ausgeführt werden.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. Azure Pipelines, Folge 7 von 10. Die zuverlässigsten Enterprise-Pipelines laufen nicht einfach blind von oben nach unten. Sie reagieren dynamisch, wenn etwas schiefgeht, bauen nach einem Absturz die Infrastruktur ab oder überspringen bei einem kleinen Hotfix aufwendige Test Suites. Um diese Art von Resilience aufzubauen, brauchst du Dynamic Control: Conditions und Expressions. Jeder Job und Step in Azure Pipelines hat eine Condition, egal ob du sie explizit schreibst oder nicht. Standardmäßig wendet das System eine integrierte Condition namens succeeded an. Das bedeutet, dass ein Step nur ausgeführt wird, wenn alle seine vorherigen Dependencies fehlerfrei abgeschlossen wurden. Schlägt Step eins fehl, wird Step zwei automatisch übersprungen. Oft wird missverstanden, wann diese Checks stattfinden. Eine Condition wertet aus, ob ein Step laufen soll, und zwar komplett, bevor der Step oder Job überhaupt beginnt. Ein Step kann niemals eine Condition nutzen, um seinen eigenen internen Output auszuwerten. Die Condition ist der Gatekeeper, der am Eingang steht. Sie schaut sich den State der Pipeline bis zu genau diesem Moment an und entscheidet, ob der Step starten darf. Manchmal brauchst du explizit einen Job, der läuft, wenn ein vorheriger Step fehlschlägt. Nimm zum Beispiel einen Deployment-Job, der eine temporäre Test-Infrastruktur hochfährt. Wenn das Deployment mittendrin abstürzt, überspringt das Standardverhalten den Rest der Pipeline. Deine temporären Server bleiben online und verbrennen still und heimlich dein Cloud-Budget. Du löst das, indem du am Ende der Pipeline einen dedizierten Cleanup-Job hinzufügst, aber seine Condition von succeeded auf failed änderst. Jetzt ignoriert dieser Cleanup-Job erfolgreiche Runs komplett. Er wacht nur auf, um die temporäre Infrastruktur zu zerstören, wenn die primären Deployment-Jobs abstürzen. Du bist nicht auf grundlegende Status-Checks wie succeeded, failed oder always beschränkt. Du kannst eigene Expressions schreiben, um Variablen-States auszuwerten und granulare Routing-Entscheidungen zu treffen. Azure Pipelines verwendet für diese Expressions eine funktionale Syntax. Anstatt mathematische Symbole zu schreiben, nutzt du benannte Funktionen. Wenn du prüfen willst, ob eine Variable einem bestimmten Wert entspricht, verwendest du eine Funktion namens eq. Du öffnest Klammern, übergibst die Pipeline-Variable, die du prüfst, fügst ein Komma hinzu und gibst den erwarteten Wert an. Du kannst mehrere Checks kombinieren, indem du diese Funktionen verschachtelst. Angenommen, du hast einen Release-Job, der nur laufen soll, wenn die Pipeline den Status succeeded hat und der aktuelle Branch main ist. Du beginnst mit einer and-Funktion. Innerhalb ihrer Klammern übergibst du zwei Argumente. Das erste Argument ist die succeeded-Funktion. Das zweite Argument ist deine eq-Funktion, die die Source-Branch-Variable mit dem String für den main-Branch vergleicht. Der Release-Job wird nur getriggert, wenn beide Statements true ergeben. Mit Expressions kannst du Pipelines bauen, die sich an den Kontext des Runs anpassen. Bevor wir zum Schluss kommen: Wenn du die Show unterstützen möchtest, kannst du auf Patreon nach DevStoriesEU suchen, was wir immer sehr schätzen. Hier ist der entscheidende Punkt. Wahre Pipeline-Resilience entsteht nicht dadurch, jeden möglichen Fehler zu verhindern, sondern durch den Einsatz von Conditions, um sicherzustellen, dass dein System genau weiß, wie es mit Fehlern umgehen muss, wenn sie unweigerlich auftreten. Vielen Dank fürs Einschalten. Bis zum nächsten Mal!
8

Enterprise-Wiederverwendbarkeit: YAML Templates

4m 20s

Skalieren Sie Ihre Pipeline-Architektur über Dutzende von Repositories hinweg mithilfe von YAML Templates. Lernen Sie den Unterschied zwischen 'Includes' und 'Extends' kennen und erfahren Sie, wie Sie Sicherheitsrichtlinien unternehmensweit durchsetzen.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. Azure Pipelines, Folge 8 von 10. Du hast fünfzig Microservices und hast gerade die Build-Definition für einen davon aktualisiert. Jetzt musst du diesen YAML-Block noch neunundvierzig Mal manuell per Copy-and-Paste einfügen. Wenn du Code zwischen Pipelines hin und her kopierst, häufst du massive Technical Debt an und machst globale Security-Updates unmöglich. Die Lösung ist Enterprise Reusability: YAML Templates. Mit Templates kannst du Pipeline-Logik einmal definieren und überall wiederverwenden. In Azure Pipelines funktionieren Templates auf zwei völlig unterschiedliche Arten: Includes und Extends. Ein Include Template verhält sich genau wie eine Copy-and-Paste-Operation, die vom Pipeline-Compiler ausgeführt wird. Du nimmst eine häufig verwendete Sequenz, wie die Installation von Dependencies oder das Publishen eines Artifacts, speicherst sie als eigenständige YAML-Datei und referenzierst sie dann in deiner Main-Pipeline. Wenn die Pipeline läuft, zieht sie den Inhalt dieses externen Templates direkt in deinen aktiven Job oder deine Stage. Das ist super, um Code-Wiederholungen zu vermeiden, aber es lässt dem Entwickler immer noch die volle Kontrolle über die Pipeline-Struktur. Er entscheidet, ob, wann und wo dein Template included wird. Hier ist der entscheidende Punkt: Wenn ein zentrales Plattform-Team Regeln durchsetzen muss, nutzt es keine Includes. Es nutzt Extends. Ein Extends Template stellt die Kontrollstruktur auf den Kopf. Anstatt dass die Entwickler-Pipeline Logikbausteine reinzieht, deklariert die Entwickler-Pipeline, dass sie ein zentrales Template extended. Dieses zentrale Template diktiert die genauen Stages, Jobs und das gesamte Grundgerüst der kompletten Pipeline. Der Entwickler darf seine spezifischen Anweisungen nur in die bestimmten Slots übergeben, die das Template explizit offen lässt. Nehmen wir als Beispiel die Vorgabe eines Security-Teams. Sie verlangen, dass jeder Microservice einen Static Application Security Testing, kurz SAST, Code-Scanner ausführt, bevor irgendein Code kompiliert wird. Um das durchzusetzen, schreiben sie ein Extends Template, das einen Job mit zwei Steps definiert. Der erste Step ist der obligatorische SAST-Scanner. Der zweite Step ist ein Placeholder für Entwickler-Aktionen. Die Pipeline-Datei des Entwickler-Teams macht nichts anderes, als auf dieses zentrale Template zu zeigen und ihre spezifischen Build-Commands an diesen Placeholder zu übergeben. Das Plattform-Team garantiert, dass der Scanner jedes einzelne Mal zuerst läuft, ohne jemals fünfzig einzelne YAML-Dateien auditieren zu müssen. Um diese Commands oder andere Informationen an Templates zu übergeben, nutzt du Template Parameters. Leute verwechseln Parameters oft mit Variables, aber ihr Verhalten ist grundlegend verschieden. Variables werden zur Runtime ausgewertet und sind meistens nur lose strukturierter Text. Parameters werden zur Compile Time ausgewertet. Noch bevor die Pipeline überhaupt anfängt zu laufen, parst Azure DevOps die Templates und löst alle Parameters auf. Weil das zur Compile Time passiert, bieten Parameters strenge Safety Checks. Du kannst präzise Parameter Types definieren, wie Boolean, Number oder eine Liste von Steps. Du kannst Default Values erzwingen oder Inputs auf eine vordefinierte Liste von erlaubten Strings beschränken. Wenn ein Entwickler versucht, einen Text-String an einen Boolean Parameter zu übergeben, weigert sich die Pipeline zu kompilieren. Dieses Strong Typing verhindert Runtime Failures und stellt sicher, dass sich das Template exakt so verhält, wie das zentrale Team es beabsichtigt hat. Indem die Pipeline-Struktur gezwungen wird, vor Beginn der Execution ausgewertet und type-checked zu werden, fungiert ein Extends Template als architektonische Grenze. Es trennt komplett, was Entwickler bauen, von dem, wie die Organisation es absichert. Das war's für diese Folge. Danke fürs Zuhören und keep building!
9

Gezielte Deployments mit Environments

4m 25s

Heben Sie Ihre Pipeline vom bloßen 'Ausführen von Code' auf die Verwaltung tatsächlicher Deployments. Wir behandeln den Deployment Job-Typ, Environments und Deployment-Strategien wie runOnce und Canary.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. Azure Pipelines, Folge 9 von 10. Du kannst problemlos einen Standard Pipeline Job schreiben, der ein Shell Script ausführt, um Code auf einen Server zu pushen. Tritt jedoch ein kritischer Bug auf und jemand fragt, was genau letzten Dienstag in Production gegangen ist, liefert dir dieser Standard Job nichts als Console Logs. Um einen echten Audit Trail darüber zu bekommen, was wo deployed wurde, brauchst du Targeting Deployments mit Environments. Ein Environment in Azure DevOps ist eine logische Sammlung von Ressourcen, die du mit einem Deployment targetest. Du nennst es zum Beispiel Production oder Staging, und es dient als Tracking-Anker. Anstatt einen Standard Pipeline Job zu nutzen, um deinen Code zu pushen, verwendest du ein spezielles Keyword namens Deployment Job. Wenn du einen Deployment Job mit einem Environment verknüpfst, trackt Azure DevOps automatisch die genauen Commits und Work Items, die auf dieses spezifische Target deployed werden. Du bekommst eine komplette Deployment History im User Interface, ohne zusätzliche Logging-Logik schreiben zu müssen. Es gibt einen großen Unterschied darin, wie sich Deployment Jobs im Vergleich zu Standard Jobs verhalten. Ein Standard Job lädt dein Source Code Repository automatisch herunter. Ein Deployment Job tut das nicht. Er überspringt den Checkout-Schritt standardmäßig komplett. Das bringt viele Entwickler ins Stolpern. Der Grund dafür ist simpel. Wenn du die Deployment Phase erreichst, solltest du ein vorkompiliertes Artifact oder ein Container Image deployen, das in einer früheren Build Stage erstellt wurde. Du brauchst in der Regel keinen rohen Source Code auf einem Deployment Runner. Wenn du das Source Repository tatsächlich brauchst, musst du dem Deployment Job explizit sagen, dass er es auschecken soll. Wenn du einen Deployment Job definierst, listest du keine flache Sequenz von Tasks auf. Du verpackst diese Tasks in eine Deployment Strategy. Die gängigste Strategy ist run once. Wie der Name schon sagt, führt sie deine Deployment-Schritte einfach nacheinander auf dem Environment aus. Wenn du etwas Komplexeres brauchst, kannst du die Canary Strategy verwenden. Canary lässt dich auf einen kleinen Prozentsatz deiner Server deployen, auf Errors monitoren und die neue Version dann schrittweise auf den Rest ausrollen. Das begrenzt den Schaden eines schlechten Releases. Innerhalb dieser Strategies sind deine Tasks in Lifecycle Hooks organisiert. Das erzwingt eine saubere Struktur. Zuerst hast du den pre-deploy Hook, wo du Ressourcen initialisieren oder Database Migrations ausführen kannst. Dann kommt der deploy Hook, der die neue Version deiner Application pusht. Danach kümmert sich der route-traffic Hook darum, Network Requests auf die neu deployte Version umzuleiten. Zum Schluss kannst du post-route-traffic nutzen, um Health Checks auszuführen oder alte Ressourcen aufzuräumen. Falls etwas schiefgeht, gibt es auch on-failure und on-success Hooks, um Rollbacks oder Notifications zu handhaben. Nehmen wir ein Szenario, in dem du die run once Strategy nutzt, um ein Container Image in einen Kubernetes Namespace zu pushen. In deiner Pipeline definierst du einen Deployment Job, der dein Production Environment targetet. Innerhalb der run once Strategy nutzt du den deploy Hook, um einen Task zu definieren, der dein gebautes Container Artifact nimmt und das Deployment Manifest auf den Cluster anwendet. Du musst das Repository nicht auschecken, da das Container Image bereits gebaut und in deiner Registry gespeichert ist. Wenn diese Pipeline läuft, werden die Tasks ausgeführt und Azure DevOps zeichnet die Aktion auf. Weil sie ein Environment targetet, kannst du das Interface öffnen, auf Production klicken und genau sehen, welcher Commit den Image Build getriggert hat, welcher Developer ihn geschrieben hat und dass er den Kubernetes Cluster erfolgreich erreicht hat. Hier ist die entscheidende Erkenntnis. Der Wechsel von einem Standard Job zu einem Deployment Job ändert nicht nur, wie deine Pipeline strukturiert ist. Er verwandelt deine Pipeline von einem blinden Automation Script in eine vollständig nachvollziehbare Deployment History. Danke fürs Einschalten. Bis zum nächsten Mal!
10

Enterprise Gates: Approvals und Checks

3m 54s

Setzen Sie Leitplanken für Ihre automatisierten Deployments. In dieser letzten Episode untersuchen wir, wie Sie Approvals, Branch Control und Exclusive Locks für Ihre Environments konfigurieren, um die Production-Umgebung zu schützen.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. Azure Pipelines, Folge 10 von 10. Du willst auf keinen Fall, dass eine automatisierte Pipeline an einem Freitagabend eine massive, irreversible Datenbank-Migration in die Production pusht. Eine vollautomatisierte Pipeline ist super, bis sie zum falschen Zeitpunkt oder ohne menschliche Aufsicht getriggert wird. Genau hier kommen Enterprise Gates, genauer gesagt Approvals und Checks, ins Spiel und lösen dieses Problem. Bevor wir uns die einzelnen Checks ansehen, müssen wir ein häufiges Missverständnis ausräumen. Approvals und Checks werden nicht in deiner YAML-Pipeline-Datei definiert. Sie werden in der Azure DevOps UI direkt auf der Ressource selbst konfiguriert, zum Beispiel auf einem Environment oder einer Service Connection. Diese Unterscheidung ist extrem wichtig. Wären diese Regeln in der YAML-Datei, könnte ein Entwickler die Datei in einem Feature Branch bearbeiten, die Production Checks entfernen und die Kontrollen umgehen. Dadurch, dass die Konfiguration auf der Ressource liegt, setzt der Resource Owner die Regeln durch. Das macht deine Compliance manipulationssicher. Der Pipeline-Code fordert lediglich die Nutzung der Ressource an, und die Ressource entscheidet, ob die Deployment-Bedingungen erfüllt sind. Wenden wir das mal auf ein konkretes Szenario an. Du hast ein Environment namens Production. Du möchtest Release-Sicherheit und ITIL-Compliance erzwingen, ohne deine Automatisierung zu verlieren. Als Erstes willst du Deployments am Wochenende verhindern. Auf dem Production Environment in der UI fügst du einen Business Hours Check hinzu. Du definierst das erlaubte Zeitfenster, vielleicht Montag bis Donnerstag, neun bis siebzehn Uhr in deiner lokalen Zeitzone. Wenn eine Pipeline versucht, außerhalb dieses Fensters zu deployen, pausiert sie. Sie wartet in einem Pending State, bis die Business Hours beginnen. Keine Freitagabend-Migrationen mehr. Als Nächstes brauchst du einen menschlichen Sanity Check. Du fügst einen Manual Approval Check hinzu und weist ihn einer bestimmten Gruppe zu, wie dem QA-Team. Wenn die Pipeline die Deployment Stage für Production erreicht, hält sie an. Eine E-Mail geht an das QA-Team raus. Sie überprüfen die Änderungen und können den Run im Azure DevOps Portal explizit approven oder rejecten. Erst nach dem Approval läuft die Pipeline weiter. Du kannst sogar die Evaluation Sequence erzwingen, sodass der Business Hours Check erfolgreich sein muss, bevor die Notification für das Manual Approval verschickt wird. Jetzt musst du garantieren, dass kein experimenteller Code durchrutscht. Du implementierst Branch Control. Du fügst einen Check hinzu, der besagt, dass nur der Main Branch das Production Environment targeten darf. Wenn jemand die Pipeline von einem Feature Branch aus triggert, schlägt der Check automatisch fehl. Das Deployment wird blockiert, bevor es überhaupt anläuft. Zu guter Letzt gibt es noch das Problem der Concurrent Deployments. Wenn zwei Entwickler im Abstand von zehn Minuten Code mergen, könnten am Ende zwei Pipelines gleichzeitig versuchen, dieselbe Production-Infrastruktur zu updaten. Der Exclusive Lock Check verhindert diese Race Condition. Er stellt sicher, dass immer nur ein Pipeline Run gleichzeitig auf das Environment zugreifen kann. Die zweite Pipeline wartet einfach in einer Queue, bis die erste fertig ist, was eine saubere, sequentielle Deployment History garantiert. Hier ist die wichtigste Erkenntnis: Approvals und Checks nehmen dem Pipeline-Code die Macht zu deployen und übergeben sie dem Infrastructure Owner. Das schafft eine sichere, unveränderliche Grenze um deine kritischen Systeme. Da das die letzte Folge unserer Azure Pipelines-Serie ist, empfehle ich dir dringend, dich in die offizielle Microsoft-Dokumentation einzulesen und diese Konfigurationen hands-on auszuprobieren. Wenn du Themen für unsere nächste Serie vorschlagen willst, schau auf devstories dot eu vorbei. Das war's für diese Folge. Danke fürs Zuhören und keep building!