#!/bin/bash # check.sh # Usage: # ./check.sh [inventory.ini] [limit] # Examples: # ./check.sh bootstrap-debian13.yml # ./check.sh bootstrap-debian13.yml inventory.ini finlog_dev # ./check.sh bootstrap-debian13.yml inventory.ini localdev.host.getfinlog.app # # Behavior: # - Attempt 1: Dry-run with key auth as user ${SSH_KEY_USER:-bummsa} # - If SSH/unreachable: Attempt 2 with --ask-pass as root and -e ansible_become=false # - If still unreachable: exit 0 (treated as “validated”); otherwise propagate error set -euo pipefail PLAYBOOK="${1:-}" INVENTORY="${2:-inventory.ini}" LIMIT_ARG="${3:-}" # default SSH key user (can override: SSH_KEY_USER=admin ./check.sh ...) SSH_KEY_USER="${SSH_KEY_USER:-bummsa}" if [ -z "$PLAYBOOK" ]; then echo "Usage: $0 [inventory.ini] [limit]" exit 1 fi [ -f "$PLAYBOOK" ] || { echo "❌ Playbook not found: $PLAYBOOK"; exit 1; } [ -f "$INVENTORY" ] || { echo "❌ Inventory not found: $INVENTORY"; exit 1; } echo "🔍 Playbook: $PLAYBOOK" echo "📂 Inventory: $INVENTORY" [ -n "$LIMIT_ARG" ] && echo "🎯 Limit: $LIMIT_ARG" echo "👤 Dry-run key user: $SSH_KEY_USER" echo # --- helpers --- is_unreachable_output() { grep -Eq 'UNREACHABLE!|Failed to connect to the host via ssh|Permission denied \(publickey,password\)|Permission denied, please try again|No route to host|Could not resolve hostname|Name or service not known|Host key verification failed' "$1" } PLAY_HAS_BECOME=false if grep -Eq '^\s*become:\s*true\b' "$PLAYBOOK"; then PLAY_HAS_BECOME=true fi ANSIBLE_LIMIT_OPTS=() [ -n "$LIMIT_ARG" ] && ANSIBLE_LIMIT_OPTS=(--limit "$LIMIT_ARG") # 1) YAML lint (soft) if command -v yamllint >/dev/null 2>&1; then echo "=== YAML Lint ===" yamllint "$PLAYBOOK" echo else echo "⚠️ yamllint not installed, skipping…" echo fi # 2) ansible-lint (soft) if command -v ansible-lint >/dev/null 2>&1; then echo "=== Ansible Lint ===" ansible-lint "$PLAYBOOK" echo else echo "⚠️ ansible-lint not installed, skipping…" echo fi # 3) Inventory check (hard) echo "=== Inventory Validation ===" ansible-inventory -i "$INVENTORY" --list >/dev/null echo "✅ Inventory OK" echo # 4) Syntax check (hard) echo "=== Ansible Syntax Check ===" ansible-playbook -i "$INVENTORY" "${ANSIBLE_LIMIT_OPTS[@]}" -u "$SSH_KEY_USER" "$PLAYBOOK" --syntax-check echo "✅ Syntax OK" echo # 5) Targets info (soft) echo "=== Target Hosts (per play) ===" ansible-playbook -i "$INVENTORY" "${ANSIBLE_LIMIT_OPTS[@]}" -u "$SSH_KEY_USER" "$PLAYBOOK" --list-hosts || true echo # 6) Dry run – Attempt 1 (Key as $SSH_KEY_USER) echo "=== Dry Run (Check Mode) — Attempt 1 (Key as $SSH_KEY_USER) ===" TMP1="$(mktemp)" set +e ansible-playbook -i "$INVENTORY" "${ANSIBLE_LIMIT_OPTS[@]}" -u "$SSH_KEY_USER" "$PLAYBOOK" --check --diff 2>&1 | tee "$TMP1" RC1=${PIPESTATUS[0]} set -e if [ $RC1 -eq 0 ]; then echo echo "✅ Dry Run erfolgreich (Key as $SSH_KEY_USER)" exit 0 fi # 7) Fallback — Attempt 2 (Password as root, no become) if is_unreachable_output "$TMP1"; then echo echo "⚠️ Host unreachable/SSH failed im ersten Versuch." if [ -t 0 ]; then echo "🔁 Starte zweiten Versuch mit Passwort-Login (als root, ohne become)…" TMP2="$(mktemp)" set +e ansible-playbook -i "$INVENTORY" "${ANSIBLE_LIMIT_OPTS[@]}" "$PLAYBOOK" --check --diff --ask-pass -u root -e ansible_become=false 2>&1 | tee "$TMP2" RC2=${PIPESTATUS[0]} set -e if [ $RC2 -eq 0 ]; then echo echo "✅ Dry Run erfolgreich (Password-Fallback als root)" exit 0 fi if is_unreachable_output "$TMP2"; then echo echo "⚠️ Auch mit Passwort-Fallback unreachable. Ignoriere für Dry-Run (Exit 0)." echo " Tipp: Key verteilen (ssh-copy-id) oder Zugang prüfen." exit 0 else echo echo "❌ Dry Run fehlgeschlagen (kein reiner SSH/Unreachable-Fehler)." exit $RC2 fi else echo "ℹ️ Keine TTY verfügbar → kann kein Passwort abfragen. Ignoriere Unreachable für Dry-Run (Exit 0)." exit 0 fi else echo echo "❌ Dry Run fehlgeschlagen (kein SSH/Unreachable-Thema)." exit $RC1 fi