vom Provisioning zum konfigurierten Server in einem Lauf
Terraform provisioniert Infrastruktur, Ansible konfiguriert sie, aber zwischen beiden Werkzeugen klafft in vielen Teams eine manuelle Lücke. Bash Glue Code schließt diese Lücke: State auslesen, Inventory generieren, Playbooks anstoßen und Fehler an einer zentralen Stelle behandeln, statt zwei Tools per Copy-Paste zu verbinden.
Inhaltsverzeichnis
- 1. Warum Terraform und Ansible eine Bruecke brauchen
- 2. Ein Wrapper-Skript fuer terraform init, plan und apply
- 3. Dynamisches Ansible-Inventory aus dem Terraform-State
- 4. Variablen und Secrets zwischen beiden Welten synchronisieren
- 5. Den kompletten Lauf orchestrieren: Provisioning bis Konfiguration
- 6. Drift-Erkennung und Idempotenz im Bash-Workflow
- 7. Terraform und Ansible in der CI-Pipeline mit Bash steuern
- 8. Fehlerbehandlung, Rollback und typische Fallstricke
- 9. Terraform, Ansible und Bash Glue Code im Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Warum Terraform und Ansible eine Bruecke brauchen
Terraform und Ansible lösen unterschiedliche Probleme und genau das macht die Kombination attraktiv: Terraform verwaltet deklarativ Cloud-Ressourcen wie VMs, Netzwerke und Load Balancer, während Ansible sich um Paketinstallation, Konfigurationsdateien und Anwendungsdeployment auf den bereits existierenden Maschinen kümmert. In der Praxis heißt das aber, dass jemand die IP-Adressen, die Terraform gerade erzeugt hat, manuell in eine Ansible-Inventory-Datei eintragen muss, bevor das Playbook überhaupt starten kann. Genau an dieser Stelle entsteht der Bedarf an Bash Glue Code.
Bash Glue Code ist in diesem Kontext kein Feature-Werkzeug, sondern die Klebeschicht zwischen zwei ausgereiften, aber nicht miteinander verdrahteten Systemen. Ein gutes Stück Bash Glue Code liest den Terraform-State strukturiert aus, erzeugt daraus ein gültiges Ansible-Inventory, prüft Voraussetzungen wie SSH-Erreichbarkeit und startet erst dann das Playbook. Ohne diesen Klebstoff bleiben Terraform und Ansible zwei getrennte manuelle Schritte mit hoher Fehleranfälligkeit, vor allem wenn mehrere Personen im selben Repository arbeiten und die Inventory-Datei per Hand pflegen.
Der Vorteil eines konsequent gepflegten Bash Glue Code zeigt sich besonders bei wiederholten Deployments: Ein neues Environment, ein Failover oder ein horizontal skalierter Node-Pool lässt sich dann mit einem einzigen Kommando aufsetzen und konfigurieren, statt jeden Schritt erneut manuell nachzuvollziehen. Die folgenden Abschnitte zeigen konkrete Skripte für Wrapper, dynamisches Inventory, Variablen-Sync und CI-Integration.
2. Ein Wrapper-Skript fuer terraform init, plan und apply
Der erste Baustein von Bash Glue Code ist ein Wrapper, der die drei Terraform-Kommandos init, plan und apply in einer kontrollierten Reihenfolge ausführt und dabei Locking, Logging und eine explizite Bestätigung vor dem Apply einbaut. Ohne diesen Wrapper landet der terraform apply-Befehl früher oder später in einer Pipeline oder einem Cronjob, ohne dass jemand das Plan-Ergebnis geprüft hat, was bei destruktiven Änderungen wie dem Neuaufbau einer Datenbank-Instanz fatal sein kann.
Ein zentrales Element dieses Bash Glue Code ist das Speichern des Plans als Datei mit terraform plan -out=tfplan, damit der spätere Apply exakt den geprüften Plan anwendet und nicht zwischen Plan und Apply ein zweites Mal neu plant, was durch zwischenzeitliche State-Änderungen zu Überraschungen führen könnte. Das Skript nutzt außerdem flock, um zu verhindern, dass zwei Kollegen gleichzeitig denselben State bearbeiten.
#!/usr/bin/env bash
# tf-wrapper.sh — safe terraform init/plan/apply wrapper as Bash glue code
set -euo pipefail
IFS=$'\n\t'
readonly ENV="${1:?Usage: tf-wrapper.sh <environment>}"
readonly WORKDIR="infra/environments/${ENV}"
readonly LOCK_FILE="/tmp/terraform-${ENV}.lock"
readonly PLAN_FILE="tfplan-${ENV}"
cleanup() {
rm -f "${PLAN_FILE}"
}
trap cleanup EXIT
[[ -d "$WORKDIR" ]] || { echo "[ERROR] Unknown environment: ${ENV}" >&2; exit 1; }
exec 9>"$LOCK_FILE"
flock -n 9 || { echo "[ERROR] Another Terraform run is active for ${ENV}" >&2; exit 1; }
cd "$WORKDIR"
echo "[INFO] terraform init for ${ENV}"
terraform init -input=false -upgrade=false
echo "[INFO] terraform plan for ${ENV}"
terraform plan -input=false -out="../../../${PLAN_FILE}"
read -r -p "Apply this plan for ${ENV}? [yes/NO] " confirm
[[ "$confirm" == "yes" ]] || { echo "[INFO] Aborted by operator"; exit 0; }
echo "[INFO] terraform apply for ${ENV}"
terraform apply -input=false "../../../${PLAN_FILE}"
echo "[INFO] Writing outputs for downstream Ansible step"
terraform output -json > "../../../tf-outputs-${ENV}.json"
Dieser Wrapper ist bewusst konservativ: Er fragt vor jedem Apply nach, außer im CI-Kontext, wo eine separate Variante ohne interaktive Rückfrage läuft, dafür aber mit striktem Vier-Augen-Prinzip über Pull-Request-Reviews arbeitet. Der letzte Schritt, das Schreiben von tf-outputs-ENV.json, ist die Schnittstelle zum nächsten Abschnitt: Genau diese Datei liefert die Rohdaten für das dynamische Ansible-Inventory.
3. Dynamisches Ansible-Inventory aus dem Terraform-State
Ansible braucht ein Inventory, das ihm sagt, welche Hosts existieren und über welche Adresse sie erreichbar sind. Statt diese Datei manuell zu pflegen, generiert guter Bash Glue Code sie automatisch aus den Terraform-Outputs. Terraform gibt mit terraform output -json eine strukturierte JSON-Ausgabe zurück, die sich mit jq präzise in das von Ansible erwartete Format überführen lässt, entweder als statische INI-Datei oder als YAML-Inventory mit Gruppen und Host-Variablen.
Der entscheidende Vorteil dieses Ansatzes: Die Inventory-Datei ist niemals veraltet, weil sie bei jedem Lauf neu aus dem aktuellen State erzeugt wird. Wird eine Instanz durch Terraform ersetzt, weil sich beispielsweise die AMI-ID geändert hat, taucht die neue IP-Adresse automatisch im nächsten generierten Inventory auf, ohne dass jemand eine Textdatei anfassen muss. Das ist der Kern dessen, was Bash Glue Code in diesem Workflow leistet: Terraform-State als einzige Quelle der Wahrheit behandeln.
#!/usr/bin/env bash
# generate-inventory.sh — build Ansible inventory from Terraform JSON output
set -euo pipefail
readonly TF_OUTPUT="${1:?Usage: generate-inventory.sh <tf-outputs.json>}"
readonly INVENTORY_FILE="inventory/generated.yml"
command -v jq >/dev/null || { echo "[ERROR] jq is required" >&2; exit 1; }
[[ -f "$TF_OUTPUT" ]] || { echo "[ERROR] File not found: ${TF_OUTPUT}" >&2; exit 1; }
mkdir -p "$(dirname "$INVENTORY_FILE")"
{
echo "all:"
echo " children:"
echo " web:"
echo " hosts:"
jq -r '.web_instance_ips.value[] | " \(.) :\n ansible_user: deploy\n ansible_ssh_private_key_file: ~/.ssh/deploy_key"' "$TF_OUTPUT"
echo " db:"
echo " hosts:"
jq -r '.db_instance_ips.value[] | " \(.) :\n ansible_user: deploy"' "$TF_OUTPUT"
} > "$INVENTORY_FILE"
echo "[INFO] Inventory written to ${INVENTORY_FILE}"
ansible-inventory -i "$INVENTORY_FILE" --list >/dev/null \
&& echo "[OK] Inventory syntax valid" \
|| { echo "[ERROR] Generated inventory is invalid" >&2; exit 1; }
Ein Detail, das in vielen Implementierungen von Bash Glue Code fehlt: die Validierung des generierten Inventories mit ansible-inventory --list, bevor das eigentliche Playbook läuft. Diese eine Zeile verhindert, dass ein Tippfehler in der jq-Filterlogik erst mitten im Playbook-Lauf auffällt, wenn Ansible bereits versucht, Hosts zu erreichen, die es nicht sauber parsen konnte.
4. Variablen und Secrets zwischen beiden Welten synchronisieren
Terraform-Variablen liegen typischerweise in .tfvars-Dateien, Ansible-Variablen in group_vars und host_vars. Ohne bewussten Abgleich entstehen schnell zwei Wahrheiten: Terraform kennt einen Datenbanknamen, den Ansible in einer Konfigurationsdatei anders schreibt. Bash Glue Code kann hier eine einzige Quelle definieren, etwa eine zentrale YAML-Datei, aus der sowohl ein terraform.tfvars.json als auch eine Ansible-Vars-Datei generiert werden.
Bei Secrets ist Vorsicht geboten: Terraform-State enthält häufig sensible Werte im Klartext, wenn sie nicht explizit als sensitive = true markiert sind. Guter Bash Glue Code filtert beim Übertrag in Ansible-Variablen konsequent nach Namensmuster und leitet echte Geheimnisse stattdessen an einen Vault-Lookup weiter, statt sie durch die Pipeline zu schleusen. Das folgende Skript zeigt eine einfache, aber wirksame Trennung.
#!/usr/bin/env bash
# sync-vars.sh — derive Ansible vars from a single source of truth
set -euo pipefail
readonly SOURCE="config/shared-vars.yml"
readonly TF_VARS_OUT="infra/environments/prod/generated.auto.tfvars.json"
readonly ANSIBLE_VARS_OUT="ansible/group_vars/all/generated.yml"
command -v yq >/dev/null || { echo "[ERROR] yq is required" >&2; exit 1; }
# Non-secret values flow into both tools
yq -o=json '. | with_entries(select(.key != "secrets"))' "$SOURCE" > "$TF_VARS_OUT"
{
echo "# Auto-generated from ${SOURCE} — do not edit by hand"
yq '. | with_entries(select(.key != "secrets"))' "$SOURCE"
echo "db_password: \"{{ lookup('community.hashi_vault.hashi_vault', 'secret=kv/prod/db:password') }}\""
} > "$ANSIBLE_VARS_OUT"
echo "[OK] Generated ${TF_VARS_OUT} and ${ANSIBLE_VARS_OUT}"
echo "[INFO] Secrets were routed to Vault lookups, not copied into either file"
5. Den kompletten Lauf orchestrieren: Provisioning bis Konfiguration
Mit Wrapper, Inventory-Generator und Variablen-Sync liegen die Bausteine bereit, ein übergeordnetes Skript verbindet sie zu einem einzigen Ablauf. Dieses Orchestrierungsskript ist der eigentliche Kern dessen, was Teams meinen, wenn sie von Bash Glue Code sprechen: Ein Aufruf, der Terraform provisioniert, das Inventory generiert, wartet bis SSH erreichbar ist, und dann Ansible startet.
Wichtig ist die Reihenfolge der Prüfungen: Nach terraform apply sind neue Instanzen zwar in der Cloud-API sichtbar, aber der SSH-Daemon braucht oft noch einige Sekunden zum Hochfahren. Ein naives Skript, das sofort ansible-playbook startet, läuft in Verbindungsfehler. Bash Glue Code löst das mit einer Wartefunktion, die per nc oder ssh -o ConnectTimeout pollt, bis der Port offen ist.
#!/usr/bin/env bash
# provision-and-configure.sh — full Terraform-to-Ansible run
set -euo pipefail
readonly ENV="${1:?Usage: provision-and-configure.sh <environment>}"
wait_for_ssh() {
local host="$1" tries=30
until nc -z -w2 "$host" 22 2>/dev/null; do
((tries--)) || { echo "[ERROR] SSH never came up on ${host}" >&2; return 1; }
echo "[INFO] Waiting for SSH on ${host}... (${tries} left)"
sleep 5
done
}
echo "[STEP 1/4] Provisioning infrastructure"
./tf-wrapper.sh "$ENV"
echo "[STEP 2/4] Generating dynamic inventory"
./generate-inventory.sh "tf-outputs-${ENV}.json"
echo "[STEP 3/4] Waiting for SSH on all hosts"
mapfile -t hosts < <(jq -r '.web_instance_ips.value[], .db_instance_ips.value[]' "tf-outputs-${ENV}.json")
for host in "${hosts[@]}"; do
wait_for_ssh "$host"
done
echo "[STEP 4/4] Running Ansible playbook"
ansible-playbook -i inventory/generated.yml site.yml \
--extra-vars "environment=${ENV}"
echo "[DONE] ${ENV} provisioned and configured"
6. Drift-Erkennung und Idempotenz im Bash-Workflow
Infrastructure as Code funktioniert nur, wenn der deklarierte Zustand und der tatsächliche Zustand übereinstimmen. Drift entsteht, wenn jemand manuell in der Cloud-Konsole eine Einstellung ändert, die eigentlich von Terraform verwaltet wird. Bash Glue Code kann Drift proaktiv erkennen, indem regelmäßig ein terraform plan -detailed-exitcode läuft, dessen Exit-Code 2 signalisiert, dass Änderungen anstehen, ohne dass tatsächlich etwas angewendet wurde.
Auch auf der Ansible-Seite lohnt sich Idempotenz-Prüfung: Ein Playbook, das im Check-Mode (--check --diff) auf einem bereits konfigurierten Server keine Änderungen mehr anzeigt, ist ein starkes Signal für sauber geschriebene Tasks. Bash Glue Code für Drift-Erkennung läuft typischerweise als Cronjob oder scheduled CI-Job und meldet Abweichungen über Slack oder E-Mail, statt sie stillschweigend zu ignorieren.
#!/usr/bin/env bash
# drift-check.sh — detect Terraform and Ansible drift, notify without applying
set -euo pipefail
readonly ENV="${1:?Usage: drift-check.sh <environment>}"
readonly SLACK_WEBHOOK="${SLACK_WEBHOOK_URL:?SLACK_WEBHOOK_URL not set}"
notify() {
curl -sf -X POST -H 'Content-Type: application/json' \
-d "{\"text\": \"$1\"}" "$SLACK_WEBHOOK" >/dev/null
}
cd "infra/environments/${ENV}"
terraform init -input=false >/dev/null
set +e
terraform plan -input=false -detailed-exitcode -out=/dev/null
tf_exit=$?
set -e
if [[ $tf_exit -eq 2 ]]; then
notify "[DRIFT] Terraform detected drift in ${ENV} — review required"
elif [[ $tf_exit -eq 1 ]]; then
notify "[ERROR] Terraform plan failed in ${ENV}"
exit 1
else
echo "[OK] No Terraform drift in ${ENV}"
fi
cd -
ansible-playbook -i inventory/generated.yml site.yml --check --diff \
--extra-vars "environment=${ENV}" | tee /tmp/ansible-check.log
if grep -q "changed=[1-9]" /tmp/ansible-check.log; then
notify "[DRIFT] Ansible check-mode reported pending changes in ${ENV}"
fi
7. Terraform und Ansible in der CI-Pipeline mit Bash steuern
In einer CI-Pipeline entfällt die interaktive Bestätigung des Wrapper-Skripts, dafür rückt Vier-Augen-Prinzip über Merge Requests in den Vordergrund: Ein Terraform-Plan wird als Pipeline-Artefakt oder Kommentar im Pull Request veröffentlicht, ein menschlicher Reviewer bestätigt ihn, erst danach läuft der Apply-Job. Bash Glue Code übernimmt in diesem Szenario die Rolle, Plan-Ausgabe lesbar zu formatieren und als Kommentar zu posten, statt die rohe Terraform-Ausgabe unkommentiert im Log zu vergraben.
Für GitLab CI oder GitHub Actions bedeutet das ein Skript, das terraform show -json parst, eine kompakte Zusammenfassung aus hinzugefügten, geänderten und gelöschten Ressourcen baut und über die jeweilige API als Kommentar postet. Dieser Teil von Bash Glue Code ist oft der Unterschied zwischen einer Pipeline, der Reviewer vertrauen, und einer, deren Ausgabe niemand mehr liest.
#!/usr/bin/env bash
# pipeline-plan-summary.sh — post a readable Terraform plan summary to a PR
set -euo pipefail
readonly PLAN_JSON="${1:?Usage: pipeline-plan-summary.sh <plan.json>}"
readonly PR_API="${CI_MERGE_REQUEST_COMMENTS_URL:?CI_MERGE_REQUEST_COMMENTS_URL not set}"
added=$(jq '[.resource_changes[] | select(.change.actions == ["create"])] | length' "$PLAN_JSON")
changed=$(jq '[.resource_changes[] | select(.change.actions == ["update"])] | length' "$PLAN_JSON")
destroyed=$(jq '[.resource_changes[] | select(.change.actions == ["delete"])] | length' "$PLAN_JSON")
summary="### Terraform Plan Summary
- Resources to add: ${added}
- Resources to change: ${changed}
- Resources to destroy: **${destroyed}**"
if [[ "$destroyed" -gt 0 ]]; then
summary="${summary}
WARNING: this plan destroys existing resources. Review carefully before approving."
fi
curl -sf -X POST -H "PRIVATE-TOKEN: ${CI_JOB_TOKEN}" \
--data-urlencode "body=${summary}" "$PR_API" >/dev/null
echo "[OK] Plan summary posted (create=${added} update=${changed} delete=${destroyed})"
8. Fehlerbehandlung, Rollback und typische Fallstricke
Der häufigste Fehler in selbstgebautem Bash Glue Code ist fehlendes set -euo pipefail, kombiniert mit stillschweigend ignorierten Terraform-Fehlern in Pipes. Wenn terraform apply | tee apply.log ohne pipefail läuft, meldet die Pipeline Erfolg, selbst wenn Terraform mit einem Fehler abgebrochen ist, weil tee immer Exit-Code 0 zurückgibt. Das Ergebnis: ein halb fertig provisionierter State, der als erfolgreich gilt.
Echtes Rollback ist bei Terraform grundsätzlich schwierig, weil es kein natives Undo gibt, ein vorheriger, funktionierender State kann aber aus der Terraform-State-Historie oder aus einem Backend mit Versionierung wie S3 mit Versioning wiederhergestellt werden. Bash Glue Code für Rollback sichert vor jedem Apply eine Kopie des aktuellen State und stellt bei einem fehlgeschlagenen Lauf automatisch den letzten bekannten guten Zustand wieder her, statt den Cluster in einem unbestimmten Zwischenzustand zu belassen.
Ein dritter Fallstrick betrifft die Reihenfolge von Terraform und Ansible in CI-Jobs mit parallelen Stages: Wenn der Ansible-Job startet, bevor der Terraform-Job seinen Output vollständig geschrieben hat, schlägt die Inventory-Generierung fehl. Bash Glue Code sollte Stages explizit mit needs: in GitLab CI oder needs: in GitHub Actions verketten, statt sich auf implizite Reihenfolge zu verlassen.
9. Terraform, Ansible und Bash Glue Code im Vergleich
Die folgende Tabelle zeigt, welches Werkzeug für welche Aufgabe zuständig ist und wo Bash Glue Code die Lücke zwischen beiden schließt.
| Aufgabe | Ohne Bash Glue Code | Mit Bash Glue Code | Vorteil |
|---|---|---|---|
| Inventory pflegen | manuell in INI-Datei | automatisch aus Terraform-State | nie veraltet, keine Tippfehler |
| Verbindung nach Apply | sofortiger Ansible-Start | Wartefunktion bis SSH bereit | keine Connection-Refused-Fehler |
| Secrets | in tfvars und group_vars dupliziert | Vault-Lookup, eine Quelle | kein Secret-Drift zwischen Tools |
| Plan-Review | rohe Konsolenausgabe im CI-Log | strukturierte Zusammenfassung im PR | Reviewer sehen Risiko sofort |
| Drift-Erkennung | fällt erst beim nächsten Apply auf | geplanter Cronjob mit Benachrichtigung | frühzeitige Warnung, kein Überraschungs-Apply |
Die Tabelle macht deutlich: Terraform und Ansible bleiben in ihrer jeweiligen Domäne stark, aber erst Bash Glue Code macht aus zwei Insellösungen einen zusammenhängenden Workflow, der ohne manuelle Zwischenschritte funktioniert und in CI-Pipelines reproduzierbar bleibt.
Mironsoft
Infrastructure as Code, DevOps-Tooling und Automatisierung
Terraform und Ansible sollen zusammenspielen, statt zwei getrennte Werkzeuge zu bleiben?
Wir bauen Bash Glue Code, der Terraform-State, dynamisches Ansible-Inventory und CI-Pipelines zu einem durchgängigen, nachvollziehbaren Provisioning-Workflow verbindet.
IaC-Audit
Analyse bestehender Terraform- und Ansible-Workflows auf manuelle Lücken
Wrapper-Entwicklung
Dynamisches Inventory, Locking und Fehlerbehandlung als robuster Bash Glue Code
CI-Integration
Terraform-Plan-Review, Drift-Erkennung und Rollback in bestehende Pipelines einbauen
10. Zusammenfassung
Bash Glue Code ist die pragmatische Antwort darauf, dass Terraform und Ansible sich nicht von selbst verstehen. Ein Wrapper-Skript mit Locking und expliziter Plan-Bestätigung macht Terraform-Läufe sicher, ein Generator baut aus dem Terraform-State ein dynamisches, immer aktuelles Ansible-Inventory, eine zentrale Variablenquelle verhindert Drift zwischen den Konfigurationen beider Tools, und eine Wartefunktion für SSH-Erreichbarkeit vermeidet Race Conditions direkt nach dem Provisioning.
In CI-Pipelines übernimmt Bash Glue Code zusätzlich die Rolle, Terraform-Pläne lesbar zusammenzufassen und Drift proaktiv zu melden, statt es dem Zufall zu überlassen, wann jemand einen manuell geänderten Server bemerkt. Wer diese Bausteine einmal sauber aufbaut, kann neue Environments, Failover-Szenarien oder Skalierungs-Events mit einem einzigen Kommando durchführen, statt jeden Schritt erneut manuell zu verdrahten.
Terraform und Ansible mit Bash Glue Code — Das Wichtigste auf einen Blick
Dynamic Inventory
Terraform-Output per jq in Ansible-Inventory umwandeln, nie manuell pflegen, immer aktuell.
Wrapper mit Locking
flock und gespeicherte Pläne verhindern parallele State-Änderungen und ungeprüfte Applies.
Secrets sauber trennen
Vault-Lookup statt Duplikate in tfvars und group_vars, eine einzige Quelle der Wahrheit.
Drift proaktiv erkennen
terraform plan -detailed-exitcode als Cronjob meldet Abweichungen, bevor sie zum Problem werden.