0Pricing
DevOps Bootcamp · Lektion

Cloud-Ressourcen per CLI und jq skripten

Steuern Sie Cloud-Provider-CLIs idempotent und analysieren Sie JSON-Antworten, um Ressourcen bereitzustellen und wieder zu entfernen.

Cloud-Ressourcen per CLI und jq skripten ist eine kostenlose DevOps Bootcamp-Lektion auf CoddyKit. Dies ist Lektion 3 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des DevOps Bootcamp-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der DevOps Bootcamp-Kurs umfasst insgesamt 4 Lektionen.

Warum idempotentes Cloud-Scripting wichtig ist

Auf C2-Niveau geht es beim Cloud-Scripting nicht darum, Schaltflächen anzuklicken, sondern Code zu schreiben, der mehrfach sicher ausgeführt werden kann, ohne doppelte Ressourcen zu erzeugen oder beim zweiten Durchlauf fehlzuschlagen.

Ein idempotentes Skript prüft vor dem Erstellen, ob eine Ressource bereits vorhanden ist. Das ist der Grundpfeiler zuverlässiger Infrastrukturautomatisierung.

  • Cloud-CLIs (AWS, GCP, Azure) geben JSON zurück – die Auswertung dieser Ausgabe ist unverzichtbar.
  • jq ist das Standard-Unix-Tool zum Ausschneiden, Filtern und Transformieren von JSON in Shell-Skripten.
  • Durch die Kombination aus CLI, jq und bedingter Logik können Sie robuste, wiederholbar ausführbare Provisioning-Skripte schreiben.

Im Verlauf dieser Lektion stellen Sie S3-Buckets, EC2-Instanzen und IAM-Rollen mit der AWS CLI als Referenz bereit. Die Muster lassen sich direkt auf gcloud und az übertragen.

Cloud-CLIs installieren und überprüfen

Prüfen Sie vor dem Skripting, ob die richtigen Tools vorhanden sind. Fixieren Sie in CI immer die Versionen, um Abweichungen zwischen Umgebungen zu vermeiden.

Das folgende Snippet prüft auf AWS CLI v2, jq und das GCP SDK und installiert nur, was fehlt – ein nützliches Muster für Bootstrap-Skripte auf neuen VMs oder in Containern.

#!/usr/bin/env bash
set -euo pipefail

check_or_install() {
  local cmd="$1"
  local install_cmd="$2"
  if ! command -v "$cmd" &>/dev/null; then
    echo "[INFO] $cmd not found — installing..."
    eval "$install_cmd"
  else
    echo "[OK]   $cmd $("$cmd" --version 2>&1 | head -1)"
  fi
}

# AWS CLI v2
check_or_install aws \
  'curl -fsSL https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip -o /tmp/awscliv2.zip && unzip -q /tmp/awscliv2.zip -d /tmp && sudo /tmp/aws/install'

# jq
check_or_install jq \
  'sudo apt-get install -y jq 2>/dev/null || sudo yum install -y jq'

# gcloud (optional)
check_or_install gcloud \
  'echo "Install gcloud SDK manually from https://cloud.google.com/sdk"'

echo "All prerequisites satisfied."

Vorhandene Ressourcen mit jq abfragen

Der erste Schritt jedes idempotenten Skripts ist ein Lesen: Fragen Sie die API, ob die Ressource bereits vorhanden ist, und verzweigen Sie anschließend entsprechend.

Die AWS CLI gibt immer JSON zurück. Mit jq können Sie genau das benötigte Feld extrahieren:

  • jq -r '.Buckets[].Name' – rohe Zeichenkettenausgabe, ein Bucketname pro Zeile.
  • jq -e – wird mit Code 1 beendet, wenn der Ausdruck null oder false ergibt, und eignet sich daher ideal für if-Bedingungen.
  • jq '.[] | select(.Name == env.BUCKET)' – filtert mithilfe von env anhand einer Shell-Variablen.

Das folgende Snippet listet alle S3-Buckets auf und prüft, ob ein Ziel-Bucket bereits vorhanden ist.

#!/usr/bin/env bash
set -euo pipefail

BUCKET="my-devops-artifacts-$(date +%Y%m)"

echo "Fetching existing S3 buckets..."
EXISTING=$(aws s3api list-buckets --output json)

# Extract names as newline-separated list
echo "$EXISTING" | jq -r '.Buckets[].Name'

# Check if target bucket exists
if echo "$EXISTING" | jq -e --arg b "$BUCKET" '.Buckets[] | select(.Name == $b)' > /dev/null 2>&1; then
  echo "[EXISTS] Bucket $BUCKET already present — skipping creation."
else
  echo "[MISSING] Bucket $BUCKET not found — will create."
fi

Idempotentes Erstellen von S3-Buckets

Nachdem die Existenzprüfung eingerichtet ist, kapseln Sie das Erstellen in einer Bedingung. Eine gut strukturierte Cloud-Funktion folgt diesem Muster:

  1. Lesen Sie den aktuellen Zustand über die API.
  2. Vergleichen Sie den gewünschten Zustand mit dem tatsächlichen Zustand.
  3. Handeln Sie nur bei einer Abweichung.

Beachten Sie das Flag --create-bucket-configuration – es ist für alle Regionen außer us-east-1 erforderlich. Wenn Sie die Region im Skript fest codieren, vermeiden Sie stille Fehler, falls AWS_DEFAULT_REGION nicht gesetzt ist.

#!/usr/bin/env bash
set -euo pipefail

REGION="eu-west-1"
BUCKET="my-devops-artifacts-$(date +%Y%m)"

ensure_bucket() {
  local bucket="$1"
  local region="$2"

  local existing
  existing=$(aws s3api list-buckets --query 'Buckets[].Name' --output json)

  if echo "$existing" | jq -e --arg b "$bucket" 'index($b) != null' > /dev/null 2>&1; then
    echo "[SKIP] Bucket $bucket already exists."
    return 0
  fi

  echo "[CREATE] Creating bucket $bucket in $region..."
  aws s3api create-bucket \
    --bucket "$bucket" \
    --region "$region" \
    --create-bucket-configuration LocationConstraint="$region"

  # Enable versioning immediately after creation
  aws s3api put-bucket-versioning \
    --bucket "$bucket" \
    --versioning-configuration Status=Enabled

  echo "[DONE] Bucket $bucket created with versioning enabled."
}

ensure_bucket "$BUCKET" "$REGION"

Verschachteltes JSON analysieren: EC2-Instanzstatus

EC2-Antworten sind tief verschachtelt. Sowohl die Pfadnavigation von jq als auch --query (JMESPath, nativ in der AWS CLI) funktionieren – für komplexe Logik ist jq jedoch leistungsfähiger.

Wichtige jq-Muster für EC2:

  • .Reservations[].Instances[] – glättet die Struktur aus zwei verschachtelten Arrays.
  • select(.State.Name == "running") – filtert nach Status.
  • .Tags[] | select(.Key == "Name") | .Value – extrahiert einen Tag-Wert.

Das Snippet sucht anhand ihres Name-Tags eine laufende Instanz und gibt deren ID und private IP zurück.

#!/usr/bin/env bash
set -euo pipefail

INSTANCE_NAME="web-server-prod"

RESULT=$(aws ec2 describe-instances \
  --filters \
    "Name=tag:Name,Values=${INSTANCE_NAME}" \
    "Name=instance-state-name,Values=running" \
  --output json)

# Extract instance ID and private IP using jq
INSTANCE_ID=$(echo "$RESULT" | jq -r \
  '.Reservations[].Instances[] | .InstanceId')

PRIVATE_IP=$(echo "$RESULT" | jq -r \
  '.Reservations[].Instances[] | .PrivateIpAddress')

if [[ -z "$INSTANCE_ID" ]]; then
  echo "[WARN] No running instance named '$INSTANCE_NAME' found."
  exit 1
fi

echo "Instance ID : $INSTANCE_ID"
echo "Private IP  : $PRIVATE_IP"

Idempotentes Bereitstellen von IAM-Rollen

IAM-Ressourcen sind global und dürfen nicht dupliziert werden. AWS gibt den spezifischen Fehlercode EntityAlreadyExists zurück, wenn Sie versuchen, eine bereits vorhandene Rolle zu erstellen. Das Abfangen dieses Codes ist bei IAM im großen Maßstab ein saubereres Idempotenzmuster als ein vorheriger Listenaufruf.

Das folgende Skript demonstriert:

  • Das Erfassen des CLI-Exitcodes mit || true, damit set -e nicht zum Abbruch führt.
  • Das Analysieren der JSON-Fehlermeldung, die AWS über stderr ausgibt, mithilfe einer Prozesssubstitution.
  • Das Anhängen einer Policy nur dann, wenn sie noch nicht angehängt ist.
#!/usr/bin/env bash
set -euo pipefail

ROLE_NAME="DevOpsDeployRole"
POLICY_ARN="arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"

TRUST_POLICY='{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": { "Service": "ec2.amazonaws.com" },
    "Action": "sts:AssumeRole"
  }]
}'

# Attempt creation; ignore EntityAlreadyExists
CREATE_OUTPUT=$(aws iam create-role \
  --role-name "$ROLE_NAME" \
  --assume-role-policy-document "$TRUST_POLICY" \
  --output json 2>&1) || {
  if echo "$CREATE_OUTPUT" | grep -q 'EntityAlreadyExists'; then
    echo "[SKIP] Role $ROLE_NAME already exists."
  else
    echo "[ERROR] Unexpected error: $CREATE_OUTPUT" >&2
    exit 1
  fi
}

# Attach policy (attach-role-policy is idempotent by default)
aws iam attach-role-policy \
  --role-name "$ROLE_NAME" \
  --policy-arn "$POLICY_ARN"

echo "[OK] Role $ROLE_NAME ready with policy $POLICY_ARN."

jq für Fortgeschrittene: Transformationen, Maps und toentries

Antworten aus der Infrastruktur enthalten Dutzende Felder. Mit jq-Transformationen können Sie die Ausgabe für nachgelagerte Tools, Logs oder Konfigurationsdateien umformen.

Wichtige fortgeschrittene Muster:

  • map(select(...)) – filtert ein Array, ohne den Array-Wrapper zu entfernen.
  • to_entries | map(select(.value != null)) – entfernt Nullfelder, bevor in eine Konfiguration geschrieben wird.
  • [.[] | {id: .InstanceId, ip: .PrivateIpAddress}] – bildet eine neue Struktur.
  • @csv, @tsv, @base64 – integrierte Formatkonverter.

Das folgende Snippet extrahiert alle laufenden Instanzen und schreibt eine TSV-Inventardatei.

#!/usr/bin/env bash
set -euo pipefail

OUTPUT_FILE="/tmp/ec2_inventory.tsv"

aws ec2 describe-instances \
  --filters "Name=instance-state-name,Values=running" \
  --output json \
| jq -r '
  ["InstanceId", "Name", "PrivateIp", "Type", "AZ"],
  [
    .Reservations[].Instances[] | [
      .InstanceId,
      (.Tags // [] | map(select(.Key == "Name")) | .[0].Value // "(none)"),
      (.PrivateIpAddress // "N/A"),
      .InstanceType,
      .Placement.AvailabilityZone
    ]
  ][]
| @tsv' > "$OUTPUT_FILE"

echo "Inventory written to $OUTPUT_FILE:"
column -t "$OUTPUT_FILE"

Auf asynchrone Vorgänge warten: Polling mit jq

Cloud-Vorgänge laufen asynchron ab. Beim Erstellen einer EC2-Instanz wird sofort der Status pending zurückgegeben. Zuverlässige Skripte müssen so lange abfragen, bis der gewünschte Status erreicht ist, bevor sie fortfahren.

Das folgende Muster verwendet eine until-Schleife mit exponentiellem Backoff. Die AWS CLI stellt auch wait-Unterbefehle bereit (z. B. aws ec2 wait instance-running), aber selbst implementiertes Polling ermöglicht eine individuelle Timeout-Steuerung und ausführlichere Protokollierung.

#!/usr/bin/env bash
set -euo pipefail

INSTANCE_ID="i-0abcdef1234567890"
MAX_WAIT=300   # seconds
INTERVAL=10
ELAPSED=0

echo "Waiting for instance $INSTANCE_ID to reach 'running' state..."

while true; do
  STATE=$(aws ec2 describe-instances \
    --instance-ids "$INSTANCE_ID" \
    --output json \
  | jq -r '.Reservations[0].Instances[0].State.Name')

  echo "  [$(date +%T)] state = $STATE"

  [[ "$STATE" == "running" ]] && break

  if [[ "$STATE" == "terminated" || "$STATE" == "shutting-down" ]]; then
    echo "[FATAL] Instance entered terminal state: $STATE" >&2
    exit 1
  fi

  if (( ELAPSED >= MAX_WAIT )); then
    echo "[TIMEOUT] Instance did not reach 'running' after ${MAX_WAIT}s." >&2
    exit 1
  fi

  sleep "$INTERVAL"
  (( ELAPSED += INTERVAL ))
done

echo "[OK] Instance $INSTANCE_ID is running."

Multi-Cloud-Muster: Entsprechungen für GCP und Azure

Das idempotente Muster „Lesen, dann handeln“ lässt sich direkt auf andere Cloud-CLIs übertragen. Sowohl gcloud als auch az geben JSON zurück und unterstützen Filter:

  • GCP: gcloud ... --format='json' – leiten Sie die Ausgabe genau wie bei AWS an jq weiter. Verwenden Sie gcloud ... --quiet, um Eingabeaufforderungen in Skripten zu unterdrücken.
  • Azure: az ... --output json – dasselbe Muster. az group exists gibt eine einfache boolesche Zeichenkette (true/false) zurück, sodass jq in einfachen Fällen nicht benötigt wird.

Das Snippet zeigt nebeneinander das idempotente Erstellen einer Ressourcengruppe in Azure und eines GCS-Buckets in GCP, jeweils mit demselben Absicherungsmuster.

#!/usr/bin/env bash
set -euo pipefail

# --- Azure: idempotent resource group ---
RG="devops-rg"
LOCATION="westeurope"

if [[ $(az group exists --name "$RG") == "true" ]]; then
  echo "[SKIP] Azure resource group $RG already exists."
else
  echo "[CREATE] Creating Azure resource group $RG..."
  az group create --name "$RG" --location "$LOCATION" --output json \
    | jq '{name: .name, location: .location, provisioningState: .properties.provisioningState}'
fi

# --- GCP: idempotent GCS bucket ---
GCS_BUCKET="gs://devops-artifacts-prod"
PROJECT="my-gcp-project"

if gcloud storage buckets describe "$GCS_BUCKET" \
     --project="$PROJECT" --format='value(name)' &>/dev/null; then
  echo "[SKIP] GCS bucket $GCS_BUCKET already exists."
else
  echo "[CREATE] Creating GCS bucket $GCS_BUCKET..."
  gcloud storage buckets create "$GCS_BUCKET" \
    --project="$PROJECT" \
    --location=EU \
    --uniform-bucket-level-access
fi

Teardown: Ressourcen sicher zerstören

Skripte zum Zerstören sind ebenso wichtig wie Skripte zum Erstellen. Ein sicherer Teardown:

  • Listet Ressourcen vor dem Löschen auf und gibt eine Zusammenfassung zur menschlichen Prüfung aus.
  • Akzeptiert ein --dry-run-Flag, damit Operatoren den Plan bestätigen können, ohne Änderungen vorzunehmen.
  • Löscht in der richtigen Abhängigkeitsreihenfolge (z. B. Instanzen beenden, bevor Sicherheitsgruppen gelöscht werden).

Das folgende Snippet beendet alle EC2-Instanzen mit dem Tag Env=staging und verwendet dabei eine Dry-Run-Absicherung.

#!/usr/bin/env bash
set -euo pipefail

DRY_RUN="${1:-}"

echo "Finding staging EC2 instances..."

INSTANCE_IDS=$(aws ec2 describe-instances \
  --filters \
    "Name=tag:Env,Values=staging" \
    "Name=instance-state-name,Values=running,stopped" \
  --output json \
| jq -r '[.Reservations[].Instances[].InstanceId] | @sh')

if [[ -z "$INSTANCE_IDS" ]]; then
  echo "[INFO] No staging instances found. Nothing to do."
  exit 0
fi

echo "Instances to terminate: $INSTANCE_IDS"

if [[ "$DRY_RUN" == "--dry-run" ]]; then
  echo "[DRY-RUN] No changes made."
  exit 0
fi

read -rp "Terminate these instances? [yes/N]: " CONFIRM
[[ "$CONFIRM" != "yes" ]] && { echo "Aborted."; exit 0; }

# shellcheck disable=SC2086
aws ec2 terminate-instances --instance-ids $INSTANCE_IDS --output json \
| jq '.TerminatingInstances[] | {id: .InstanceId, state: .CurrentState.Name}'

echo "[DONE] Termination initiated."

End-to-End: Idempotentes Infrastruktur-Bootstrap-Skript

Alles zusammengeführt: Ein produktionsreifes Bootstrap-Skript orchestriert mehrere Ressourcen in der richtigen Reihenfolge, ist vollständig idempotent und gibt strukturierte Protokolle aus, die ein CI-System verarbeiten kann.

Gezeigte zentrale Vorgehensweisen:

  • Strukturierte Protokollierung über einen log()-Helper, der [INFO], [WARN] und [ERROR] voranstellt.
  • Zustandsdatei — Schreiben Sie die IDs erstellter Ressourcen in eine JSON-Zustandsdatei, damit nachfolgende Ausführungen und Teardown-Skripte dieselben Referenzen verwenden.
  • Fehlerbehandlung — trap fängt unerwartete Abbrüche ab und meldet die Nummer der fehlerhaften Zeile.
#!/usr/bin/env bash
set -euo pipefail

STATE_FILE="/tmp/infra_state.json"
REGION="eu-west-1"
BUCKET="devops-bootstrap-$(date +%Y%m)"
ROLE="BootstrapRole"

log() { echo "[$(date -u +%T)] [$1] ${*:2}"; }
trap 'log ERROR "Script failed at line $LINENO"' ERR

# Initialize state
[[ -f "$STATE_FILE" ]] || echo '{}' > "$STATE_FILE"

# --- Step 1: S3 bucket ---
EXISTING_BUCKETS=$(aws s3api list-buckets --query 'Buckets[].Name' --output json)
if echo "$EXISTING_BUCKETS" | jq -e --arg b "$BUCKET" 'index($b) != null' > /dev/null; then
  log INFO "Bucket $BUCKET exists — skipping."
else
  aws s3api create-bucket --bucket "$BUCKET" --region "$REGION" \
    --create-bucket-configuration LocationConstraint="$REGION" > /dev/null
  log INFO "Bucket $BUCKET created."
fi

# Update state file
jq --arg b "$BUCKET" '.bucket = $b' "$STATE_FILE" > /tmp/_state_tmp && mv /tmp/_state_tmp "$STATE_FILE"

# --- Step 2: IAM role ---
if aws iam get-role --role-name "$ROLE" &>/dev/null; then
  log INFO "Role $ROLE exists — skipping."
else
  aws iam create-role --role-name "$ROLE" \
    --assume-role-policy-document '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"Service":"ec2.amazonaws.com"},"Action":"sts:AssumeRole"}]}' \
    --output json | jq '{RoleName: .Role.RoleName, Arn: .Role.Arn}'
  log INFO "Role $ROLE created."
fi

jq --arg r "$ROLE" '.role = $r' "$STATE_FILE" > /tmp/_state_tmp && mv /tmp/_state_tmp "$STATE_FILE"

log INFO "Bootstrap complete. State: $(cat "$STATE_FILE" | jq -c .)"

Wissenscheck: Idempotenzschutz mit jq

Testen Sie Ihr Verständnis idempotenter Cloud-Skripterstellung mit jq.

Rückblick: Cloud-Ressourcen per CLI und jq skripten

In dieser Lektion haben Sie den vollständigen Lebenszyklus einer idempotenten Cloud-Automatisierung mit Shell-Skripten, Cloud-CLIs und jq behandelt.

Zentrale Prinzipien:

  • Vor dem Schreiben lesen — Fragen Sie immer zuerst den vorhandenen Zustand ab und wenden Sie nur die Änderungen an.
  • jq -e für Prüfungen — Verwenden Sie den Exit-Status-Modus, um if-Verzweigungen anhand von JSON-Antworten zu steuern.
  • Bedeutung von Fehlercodes — Fangen Sie anbieterspezifische Fehlercodes wie EntityAlreadyExists ab, anstatt Ressourcen vorher aufzulisten, wenn dies effizienter ist.
  • Asynchronen Zustand abfragen — Verwenden Sie until-Schleifen mit Timeouts und nehmen Sie niemals an, dass eine Ressource direkt nach ihrer Erstellung bereit ist.
  • Zustandsdateien — Schreiben Sie Ressourcen-IDs in eine gemeinsam genutzte JSON-Datei, damit jede Phase des Skripts und das Teardown dieselben Referenzen verwenden.
  • Dry-Run-Flags — Unterstützen Sie immer --dry-run, damit Bediener Aktionen vor potenziell destruktiven Änderungen sicher überprüfen können.

Diese Muster bilden — zusammen mit einem strikt gesetzten set -euo pipefail-Header und einem ERR-Trap — die Grundlage für produktionsreife Infrastruktur-Skripterstellung auf C2-Niveau.

Häufig gestellte Fragen

Ist die Lektion „Cloud-Ressourcen per CLI und jq skripten“ kostenlos?

Ja — der vollständige Text von „Cloud-Ressourcen per CLI und jq skripten“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des DevOps Bootcamp-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der DevOps Bootcamp-Kurs umfasst insgesamt 4 Lektionen.

Was lerne ich in „Cloud-Ressourcen per CLI und jq skripten“?

Steuern Sie Cloud-Provider-CLIs idempotent und analysieren Sie JSON-Antworten, um Ressourcen bereitzustellen und wieder zu entfernen. Du übst DevOps Bootcamp mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.

Brauche ich Erfahrung, um DevOps Bootcamp zu starten?

Keine Vorkenntnisse erforderlich. DevOps Bootcamp auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 3 von 4.

Wie lange dauert die Lektion „Cloud-Ressourcen per CLI und jq skripten“?

Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.

Kann ich in dieser DevOps Bootcamp-Lektion Code schreiben und ausführen?

Ja. Jede DevOps Bootcamp-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.

Alle Lektionen in diesem Kurs

  1. Schlanke Dockerfiles und Shell-Entrypoints schreiben
  2. Konfigurationen mit envsubst und Heredocs templatisieren
  3. Cloud-Ressourcen per CLI und jq skripten
  4. Health-Probes, Readiness-Gates und Warteschleifen
← Zurück zu DevOps Bootcamp