GitLab-Terraform-Integration fuer Infrastructure-as-Code-Pipelines
AI generated
CI/CD
.yml
GitLab · CI/CD · Infrastructure as Code
GitLab-Terraform-Integration
Infrastructure as Code mit Plan-Diff im Merge Request

Terraform manuell von der Kommandozeile auszufuehren funktioniert im Team schnell nicht mehr zuverlaessig. Die GitLab-eigene Terraform-Integration bildet plan und apply als Pipeline-Jobs ab, verwaltet den State zentral und zeigt Aenderungen direkt als Diff im Merge Request, bevor irgendjemand echte Infrastruktur veraendert.

17 Min. Lesezeit Terraform Infrastructure as Code State Backend Merge-Request-Widget

1. Warum Infrastructure-as-Code-Pipelines mehr brauchen als lokales Terraform

Terraform lokal auf dem eigenen Rechner auszufuehren funktioniert, solange nur eine einzige Person Infrastrukturaenderungen vornimmt. Sobald ein Team gemeinsam an derselben Infrastruktur arbeitet, entstehen typische Probleme: Der lokale State ist veraltet, zwei Personen fuehren gleichzeitig apply aus und ueberschreiben sich gegenseitig, oder niemand kann im Nachhinein nachvollziehen, wer wann welche Aenderung tatsaechlich angewendet hat.

Eine Pipeline-basierte Terraform-Ausfuehrung loest diese Probleme strukturell: Jede Aenderung durchlaeuft denselben, reproduzierbaren Prozess aus plan und apply, der State liegt zentral an einem Ort, auf den alle Teammitglieder ueber dieselbe, kontrollierte Schnittstelle zugreifen, und jede angewendete Aenderung ist über die Pipeline-Historie lueckenlos nachvollziehbar.

2. Grundlagen: GitLab-Terraform-Template einbinden

GitLab stellt ein offizielles CI-Template unter Terraform/Base.latest.gitlab-ci.yml bereit, das die grundlegenden Terraform-Jobs mit sinnvollen Defaults vordefiniert. Durch include: wird dieses Template in die eigene .gitlab-ci.yml eingebunden, ohne dass die komplette Job-Logik von Hand nachgebaut werden muss. Das Template kuemmert sich unter anderem um terraform init mit dem GitLab-Backend und um das Format der JSON-Ausgabe, die spaeter fuer das Plan-Widget im Merge Request benoetigt wird.

Zusaetzlich zum Template wird ueblicherweise ein eigenes Image benoetigt, das Terraform selbst enthaelt, etwa registry.gitlab.com/gitlab-org/terraform-images/stable, das GitLab passend zu den eigenen Templates pflegt. Die konkrete Terraform-Version laesst sich ueber den Image-Tag festlegen, was Versions-Upgrades kontrollierbar und reproduzierbar macht, statt sich auf eine zufaellig auf dem Runner installierte Version zu verlassen.


include:
  - template: Terraform/Base.latest.gitlab-ci.yml

variables:
  TF_ROOT: ${CI_PROJECT_DIR}/infrastructure
  TF_STATE_NAME: production

default:
  image:
    name: registry.gitlab.com/gitlab-org/terraform-images/stable:latest

3. terraform plan als Pipeline-Job

Der plan-Job berechnet, welche Aenderungen an der realen Infrastruktur noetig waeren, ohne sie tatsaechlich anzuwenden. Das Template erzeugt dabei automatisch sowohl eine menschenlesbare Textausgabe als auch eine maschinenlesbare JSON-Datei, die als artifacts: reports: terraform an GitLab uebergeben wird. Diese JSON-Datei ist die Grundlage fuer das spaeter im Merge Request angezeigte Plan-Widget.

In jedem Merge Request laeuft plan automatisch, sodass Reviewer bereits vor der eigentlichen Freigabe sehen, welche konkreten Ressourcen erstellt, geaendert oder geloescht wuerden. Das verhindert, dass ein Code-Review sich ausschliesslich auf die .tf-Dateien selbst stuetzt, waehrend die tatsaechliche Auswirkung auf die Infrastruktur erst beim spaeteren apply sichtbar wird.


plan:
  extends: .terraform:build
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    - if: $CI_COMMIT_BRANCH == "main"

plan-json:
  extends: .terraform:build
  script:
    - gitlab-terraform plan-json
  needs: ["plan"]
  artifacts:
    reports:
      terraform: ${TF_ROOT}/plan.json

4. terraform apply als manueller Job mit needs auf plan

Der apply-Job wendet die im plan-Job berechneten Aenderungen tatsaechlich an. Aus Sicherheitsgruenden sollte er praktisch immer when: manual nutzen und ueber needs: explizit vom vorherigen plan-Job abhaengen, damit niemals ein apply ohne einen zugehoerigen, vorher gepruften plan-Lauf ausgefuehrt werden kann. Ein protected environment fuer den apply-Job stellt zusaetzlich sicher, dass nur autorisierte Teammitglieder die tatsaechliche Anwendung freigeben duerfen.

Wichtig ist, dass apply auf dem exakt gleichen Terraform-Plan aufbaut, der im Merge Request angezeigt wurde, und nicht auf einer neu berechneten Version, die sich durch zwischenzeitliche Aenderungen an der realen Infrastruktur unterscheiden koennte. Das GitLab-Template loest das, indem der plan-Output als Artefakt an den apply-Job weitergereicht wird, statt plan innerhalb des apply-Jobs erneut auszufuehren.


apply:
  extends: .terraform:build
  environment:
    name: production
  needs: ["plan"]
  rules:
    - if: $CI_COMMIT_BRANCH == "main"
      when: manual

5. State-Backend direkt in GitLab speichern

Statt eines separat zu betreibenden Backends wie S3 oder Azure Blob Storage bietet GitLab ein eigenes, HTTP-basiertes Terraform-State-Backend, das ueber die GitLab-API angesprochen wird und den State direkt im Projekt speichert, versioniert und mit Locking absichert. Konfiguriert wird das ueber einen backend "http"-Block, dessen Adresse sich aus der GitLab-Projekt-ID und dem gewaehlten TF_STATE_NAME zusammensetzt, wie im GitLab-Template bereits vordefiniert.

Der Vorteil dieses gemanagten Backends ist, dass keine zusaetzliche Cloud-Ressource ausschliesslich fuer die Terraform-State-Verwaltung betrieben und mit eigenen Zugriffsrechten abgesichert werden muss. Die Zugriffskontrolle laeuft ueber dieselben GitLab-Projektrechte, die ohnehin fuer das Repository gelten, und der State ist ueber die GitLab-Oberflaeche unter Operate > Terraform States direkt einsehbar und bei Bedarf sperrbar.


terraform {
  backend "http" {
  }
}

# Init-Aufruf im Template setzt die Backend-Parameter automatisch:
# terraform init \
#   -backend-config=address=... \
#   -backend-config=lock_address=... \
#   -backend-config=unlock_address=... \
#   -backend-config=username=... \
#   -backend-config=password=$CI_JOB_TOKEN

6. Merge-Request-Widget mit Plan-Diff

Sobald der plan-json-Job seinen Report als artifacts: reports: terraform bereitstellt, zeigt GitLab automatisch ein eigenes Widget im Merge Request an, das die geplanten Aenderungen zusammengefasst darstellt: wie viele Ressourcen erstellt, geaendert und geloescht werden, mit der Moeglichkeit, ins Detail jeder einzelnen Ressourcenaenderung hineinzuklicken. Das erspart Reviewern, die vollstaendige, oft lange Textausgabe von terraform plan manuell im Job-Log zu durchsuchen.

Dieses Widget ist besonders wertvoll bei Aenderungen, die auf den ersten Blick harmlos wirken, tatsaechlich aber ein Neuanlegen statt eines In-Place-Updates einer Ressource ausloesen, etwa wenn ein unveraenderliches Attribut wie ein AWS-Instanztyp in einer Weise geaendert wird, die Terraform zwingt, die Ressource zu loeschen und neu zu erstellen. Ein Reviewer sieht solche destruktiven Aenderungen im Plan-Widget klar markiert, bevor der apply-Job ueberhaupt freigegeben wird.

7. Vorteile gegenueber manuellem Terraform-CLI-Einsatz

Manueller Terraform-CLI-Einsatz auf Entwicklerrechnern bringt mehrere strukturelle Schwaechen mit sich: Jede Person braucht lokalen Zugriff auf Cloud-Zugangsdaten mit potenziell weitreichenden Rechten, es gibt kein zentrales, erzwungenes Vier-Augen-Prinzip vor einem apply, und unterschiedliche lokale Terraform-Versionen zwischen Teammitgliedern koennen zu leicht abweichendem Verhalten fuehren. Die GitLab-Integration adressiert all diese Punkte strukturell, nicht nur durch Team-Disziplin und Konventionen.

Zusaetzlich profitieren Teams von der vollstaendigen Audit-Historie: Jede Pipeline dokumentiert, wer welchen Merge Request erstellt, welchen Plan gesehen und welchen apply-Job freigegeben hat, alles nachvollziehbar ueber die GitLab-Oberflaeche. Bei manuellem CLI-Einsatz existiert diese Nachvollziehbarkeit meist nur, wenn zusaetzlich manuell Protokoll gefuehrt wird, was in der Praxis selten konsequent passiert.

8. Automatisches Apply auf Staging, manuelles Apply auf Produktion

Nicht jede Terraform-Aenderung muss denselben Freigabeprozess durchlaufen. Fuer eine Staging-Umgebung, in der Fehlkonfigurationen leicht rueckgaengig gemacht werden koennen und die ohnehin regelmaessig neu aufgebaut wird, ist ein automatisches apply direkt nach erfolgreichem plan oft vertretbar und beschleunigt die Feedback-Schleife bei der Entwicklung von Terraform-Modulen erheblich.

Fuer Produktionsumgebungen bleibt ein manuelles, durch ein protected environment abgesichertes apply dagegen die richtige Wahl, da hier Fehlkonfigurationen echte Ausfaelle oder Kosten verursachen koennen. Ueber getrennte TF_STATE_NAME-Werte pro Umgebung und rules:-Bedingungen, die auf den jeweiligen Branch reagieren, lassen sich beide Freigabestufen innerhalb derselben .gitlab-ci.yml sauber abbilden, ohne Code zu duplizieren.


apply-staging:
  extends: .terraform:build
  environment:
    name: staging
  variables:
    TF_STATE_NAME: staging
  needs: ["plan-staging"]
  rules:
    - if: $CI_COMMIT_BRANCH == "develop"

apply-production:
  extends: .terraform:build
  environment:
    name: production
  variables:
    TF_STATE_NAME: production
  needs: ["plan-production"]
  rules:
    - if: $CI_COMMIT_BRANCH == "main"
      when: manual

9. Fazit: Terraform-Pipelines als Standard fuer Team-Infrastruktur

Die GitLab-eigene Terraform-Integration macht aus einem potenziell riskanten, manuellen Prozess eine nachvollziehbare, reviewte Pipeline mit klarer Trennung zwischen plan und apply. Fuer jedes Team, das Infrastruktur gemeinsam verwaltet, reduziert dieser Ansatz das Risiko unbeabsichtigter oder unkoordinierter Aenderungen erheblich.

Der Umstieg von lokalem Terraform auf die Pipeline-Variante lohnt sich bereits ab zwei Personen, die regelmaessig an derselben Infrastruktur arbeiten. Das gemanagte State-Backend und das Merge-Request-Plan-Widget sind dabei keine Komfortfunktionen am Rande, sondern die eigentlichen Kernvorteile gegenueber dem klassischen CLI-Workflow.

Aspekt Manueller Terraform-CLI-Einsatz GitLab-Terraform-Integration
State-Verwaltung Lokal oder selbst betriebenes Backend GitLab-gemanagtes HTTP-Backend mit Locking
Review vor Aenderung Optional, abhaengig von Teamdisziplin Plan-Diff automatisch im Merge-Request-Widget
Zugriffskontrolle Lokale Cloud-Zugangsdaten pro Person Protected environment, GitLab-Projektrechte
Audit-Historie Meist nicht vorhanden Vollstaendig ueber Pipeline- und MR-Historie

Mironsoft

CI/CD-Pipelines, Zero-Downtime-Deployments und Release-Automatisierung

Deployments, die ohne Ausfallzeit und ohne Nervenkitzel laufen?

Wir prüfen bestehende GitLab-Pipelines auf fragile Deployment-Schritte und fehlende Absicherung und bauen daraus einen Release-Prozess mit Zero-Downtime-Deployments, automatisierten Checks und einem Rollback, dem ihr im Ernstfall vertrauen könnt.

Pipeline-Review

Bestehende .gitlab-ci.yml auf Fragilität, fehlende Stages und Sicherheitslücken prüfen.

Zero-Downtime-Deployment

Symlink-Releases, Health-Checks und Rollback-Strategien für Magento-Shops aufbauen.

CI/CD-Automatisierung

Tests, Security-Scans und Deployments zu einer zuverlässigen Pipeline verbinden.

10. Zusammenfassung

GitLab-Terraform-Integration: Das Wichtigste auf einen Blick

Template einbinden

include: template: Terraform/Base.latest.gitlab-ci.yml liefert vordefinierte plan- und apply-Jobs.

Plan vor Apply

apply nutzt needs: auf plan und wendet exakt den zuvor berechneten und gepruften Plan an.

Gemanagtes State-Backend

GitLab speichert den Terraform-State versioniert und mit Locking direkt im Projekt.

Plan-Diff im Merge Request

Ein automatisches Widget zeigt erstellte, geaenderte und geloeschte Ressourcen vor der Freigabe.

11. FAQ: GitLab-Terraform-Integration: Das Wichtigste auf einen Blick

1Wie binde ich die GitLab-Terraform-Integration in eine Pipeline ein?
Ueber include: template: Terraform/Base.latest.gitlab-ci.yml in der .gitlab-ci.yml, ergaenzt um ein Terraform-faehiges Image und Variablen wie TF_ROOT und TF_STATE_NAME.
2Wo wird der Terraform-State bei der GitLab-Integration gespeichert?
In einem GitLab-gemanagten, HTTP-basierten Backend, das den State versioniert im Projekt speichert und ueber Locking gegen gleichzeitige Aenderungen absichert.
3Wie zeigt GitLab geplante Terraform-Aenderungen im Merge Request an?
Ueber ein automatisches Widget, das aus dem JSON-Report des plan-Jobs erstellte, geaenderte und geloeschte Ressourcen zusammengefasst darstellt, mit der Moeglichkeit ins Detail zu klicken.
4Muss terraform apply immer manuell ausgeloest werden?
Aus Sicherheitsgruenden wird das dringend empfohlen, meist kombiniert mit when: manual und einem protected environment, damit nur autorisierte Personen echte Infrastrukturaenderungen freigeben.
5Wie stellt GitLab sicher, dass apply denselben Plan anwendet, der im MR angezeigt wurde?
Der plan-Output wird als Artefakt an den apply-Job weitergereicht, statt den Plan innerhalb von apply neu zu berechnen, sodass keine Abweichung durch zwischenzeitliche Aenderungen entstehen kann.
6Welche Vorteile bietet das GitLab-State-Backend gegenueber S3 oder Azure Blob Storage?
Es erfordert keine separate Cloud-Ressource ausschliesslich fuer die State-Verwaltung und nutzt dieselben GitLab-Projektrechte fuer die Zugriffskontrolle, statt eigene Cloud-IAM-Regeln zu pflegen.
7Wie funktioniert Locking beim GitLab-Terraform-Backend?
Das HTTP-Backend unterstuetzt Lock- und Unlock-Endpunkte, sodass ein laufender apply verhindert, dass ein zweiter Job gleichzeitig denselben State veraendert.
8Kann ich mehrere Terraform-Umgebungen wie Staging und Produktion getrennt verwalten?
Ja, ueber unterschiedliche TF_STATE_NAME-Werte pro Umgebung lassen sich getrennte States im selben Projekt fuehren, kombiniert mit jeweils eigenen protected environments fuer die apply-Jobs.
9Welche Terraform-Version wird in der Pipeline verwendet?
Die im gewaehlten Terraform-Image installierte Version, steuerbar ueber den Image-Tag, zum Beispiel registry.gitlab.com/gitlab-org/terraform-images/stable:1.7, statt einer zufaellig auf dem Runner vorhandenen Version.
10Was ist der groesste Vorteil gegenueber manuellem Terraform-CLI-Einsatz?
Die Kombination aus erzwungenem Review vor jeder Anwendung, zentral verwaltetem State mit Locking und einer vollstaendigen, ueber die Pipeline-Historie nachvollziehbaren Audit-Spur.