De Linux-opdrachtregel en Bash-scripting beheersen · Les

Cloudresources scripten met CLI en jq

Beheer cloudprovider-CLI's idempotent en parse JSON-antwoorden om resources aan te maken en weer af te breken.

Les 3 van 413 stappen

Cloudresources scripten met CLI en jq is een gratis De Linux-opdrachtregel en Bash-scripting beheersen-les op CoddyKit. Dit is les 3 van 4. Je kunt de volledige les hieronder gratis lezen en daarna in de browser praktisch oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is. Deze les maakt deel uit van het leertraject De Linux-opdrachtregel en Bash-scripting beheersen. Je voortgang wordt gesynchroniseerd op het web en in de CoddyKit-app. De cursus De Linux-opdrachtregel en Bash-scripting beheersen bevat in totaal 4 lessen.

Waarom idempotente cloudscripts belangrijk zijn

Op C2-niveau draait scripts schrijven voor de cloud niet om op knoppen te klikken — het gaat erom code te schrijven die meerdere keren veilig kan worden uitgevoerd zonder dubbele cloudmiddelen te maken of bij de tweede uitvoering te mislukken.

Een idempotent script controleert of een cloudmiddel al bestaat voordat het dit aanmaakt. Dit vormt de basis van betrouwbare infrastructuurautomatisering.

  • Cloud-CLI's (AWS, GCP, Azure) retourneren JSON — het ontleden van die uitvoer is essentieel.
  • jq is het standaardhulpmiddel van Unix voor het uitsnijden, filteren en omvormen van JSON uit shellscripts.
  • Door een CLI, jq en voorwaardelijke logica te combineren, kun je robuuste, herhaalbare scripts voor het inrichten van infrastructuur schrijven.

In deze hele les richt je S3-buckets, EC2-instanties en IAM-rollen in met de AWS CLI als voorbeeld. De patronen zijn rechtstreeks overdraagbaar naar gcloud en az.

Cloud-CLI's installeren en controleren

Controleer vóór je scripts schrijft of de juiste hulpmiddelen aanwezig zijn. Leg in CI altijd versies vast om afwijkingen tussen omgevingen te voorkomen.

Het onderstaande codefragment controleert op AWS CLI v2, jq en de GCP SDK en installeert alleen wat ontbreekt — een patroon dat nuttig is in opstartscripts voor nieuwe virtuele machines of containers.

#!/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."

Bestaande cloudmiddelen opvragen met jq

De eerste stap van elk idempotent script is lezen — vraag de API of het cloudmiddel al bestaat en kies daarna de juiste tak.

De AWS CLI retourneert altijd JSON. Met jq haal je precies het veld op dat je nodig hebt:

  • jq -r '.Buckets[].Name' — onbewerkte tekenreeksuitvoer, één bucketnaam per regel.
  • jq -e — sluit af met code 1 als de expressie null of false oplevert, waardoor het ideaal is voor if-voorwaarden.
  • jq '.[] | select(.Name == env.BUCKET)' — filter met een shellvariabele via env.

Het onderstaande codefragment geeft een lijst van alle S3-buckets en controleert of een doelbucket al bestaat.

#!/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

Idempotente S3-bucket aanmaken

Nu de bestaanscontrole aanwezig is, plaats je het aanmaken binnen een voorwaarde. Een goed gestructureerde cloudfunctie volgt dit patroon:

  1. Lees de huidige status uit de API.
  2. Vergelijk de gewenste status met de werkelijke status.
  3. Voer alleen wijzigingen door.

Let op de vlag --create-bucket-configuration — deze is vereist voor alle regio's behalve us-east-1. Door de regio in het script vast te leggen, voorkom je stille fouten wanneer AWS_DEFAULT_REGION niet is ingesteld.

#!/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"

Geneste JSON ontleden: status van een EC2-instantie

EC2-antwoorden zijn diep genest. Padnavigatie met jq en --query (JMESPath, ingebouwd in de AWS CLI) werken allebei — maar jq is krachtiger voor complexe logica.

Belangrijke jq-patronen voor EC2:

  • .Reservations[].Instances[] — maak de structuur met twee arrays plat.
  • select(.State.Name == "running") — filter op status.
  • .Tags[] | select(.Key == "Name") | .Value — haal een tagwaarde op.

Het codefragment vindt een actieve instantie aan de hand van de tag Name en retourneert de ID en het privé-IP-adres.

#!/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"

Idempotente IAM-rol inrichten

IAM-middelen zijn globaal en mogen niet dubbel worden aangemaakt. AWS retourneert een specifieke foutcode — EntityAlreadyExists — wanneer je een rol probeert aan te maken die al bestaat. Deze code opvangen is een netter idempotentiepatroon dan eerst een lijst opvragen wanneer je IAM op grote schaal gebruikt.

Het onderstaande script laat zien hoe je:

  • de afsluitcode van de CLI opvangt met || true, zodat set -e niet afbreekt.
  • de JSON met de foutmelding die AWS naar de standaardfoutuitvoer schrijft, ontleedt met procesvervanging.
  • een beleid alleen koppelt als het nog niet is gekoppeld.
#!/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 geavanceerd: transformaties, maps en toentries

Antwoorden uit echte infrastructuur bevatten tientallen velden. Met transformaties van jq kun je de uitvoer aanpassen voor volgende hulpmiddelen, logboeken of configuratiebestanden.

Belangrijke geavanceerde patronen:

  • map(select(...)) — filter een array zonder de arrayomhulling te verliezen.
  • to_entries | map(select(.value != null)) — verwijder null-velden voordat je naar een configuratiebestand schrijft.
  • [.[] | {id: .InstanceId, ip: .PrivateIpAddress}] — zet de gegevens om naar een nieuwe structuur.
  • @csv, @tsv, @base64 — ingebouwde omzetters voor indelingen.

Het onderstaande codefragment haalt alle actieve instanties op en schrijft een TSV-inventarisbestand.

#!/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"

Wachten op asynchrone bewerkingen: periodiek controleren met jq

Cloudbewerkingen zijn asynchroon. Bij het aanmaken van een EC2-instantie krijg je meteen een antwoord met de status pending. Betrouwbare scripts moeten blijven controleren totdat de gewenste status is bereikt voordat ze doorgaan.

Het onderstaande patroon gebruikt een until-lus met een oplopende wachttijd. De AWS CLI biedt ook wait-subopdrachten, bijvoorbeeld aws ec2 wait instance-running, maar met zelfgeschreven periodieke controles krijg je meer controle over de time-out en uitgebreidere logboekregistratie.

#!/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."

Patroon voor meerdere clouds: tegenhangers voor GCP en Azure

Het idempotente patroon van eerst lezen en dan handelen is rechtstreeks overdraagbaar naar andere cloud-CLI's. Zowel gcloud als az retourneren JSON en ondersteunen filteren:

  • GCP: gcloud ... --format='json' — stuur dit net als bij AWS door naar jq. Gebruik gcloud ... --quiet om vragen in scripts te onderdrukken.
  • Azure: az ... --output json — hetzelfde patroon. az group exists retourneert een gewone booleaanse tekenreeks (true/false), zodat je in eenvoudige gevallen geen jq nodig hebt.

Het codefragment laat zien hoe je in Azure idempotent een resourcegroep aanmaakt en in GCP een GCS-bucket, naast elkaar en met dezelfde voorwaarde.

#!/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

Opruimen: cloudmiddelen veilig vernietigen

Scripts voor vernietiging zijn net zo belangrijk als scripts voor aanmaken. Veilig opruimen betekent:

  • cloudmiddelen vóór het verwijderen opsommen en een samenvatting afdrukken die iemand kan controleren.
  • een vlag --dry-run accepteren, zodat beheerders het plan kunnen bevestigen zonder het uit te voeren.
  • verwijderen in de juiste volgorde van afhankelijkheden, bijvoorbeeld instanties beëindigen voordat je beveiligingsgroepen verwijdert.

Het onderstaande codefragment beëindigt alle EC2-instanties met de tag Env=staging en gebruikt een voorwaarde voor een proefuitvoering.

#!/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."

Van begin tot eind: idempotent bootstrapscript voor infrastructuur

Alles samengebracht: een bootstrapscript van productiekwaliteit orkestreert meerdere infrastructuuronderdelen in de juiste volgorde, is volledig idempotent en produceert gestructureerde logberichten die een CI-systeem kan parseren.

Belangrijke getoonde werkwijzen:

  • Gestructureerde logregistratie via een log()-hulpfunctie die de voorvoegsels [INFO], [WARN] en [ERROR] toevoegt.
  • Toestandsbestand — schrijf ID's van aangemaakte infrastructuuronderdelen naar een JSON-toestandsbestand, zodat volgende uitvoeringen en afbraakscripts dezelfde verwijzingen delen.
  • Foutafhandeling — trap vangt onverwachte beëindigingen op en meldt het regelnummer waarop het misgaat.
#!/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 .)"

Kennischeck: idempotentiebewaking met jq

Test je begrip van idempotente scripts voor de cloud met jq.

Samenvatting: cloudonderdelen scripten via de CLI en jq

In deze les behandelde je de volledige levenscyclus van idempotente cloudautomatisering met shellscripts, cloud-CLI's en jq.

Kernprincipes:

  • Lees vóór je schrijft — vraag altijd eerst de bestaande toestand op en handel alleen het verschil af.
  • jq -e voor voorwaarden — gebruik de modus voor afsluitstatussen om op basis van JSON-antwoorden if-vertakkingen aan te sturen.
  • Semantiek van foutcodes — vang cloudspecificieke foutcodes op, zoals EntityAlreadyExists, in plaats van vooraf een lijst op te vragen wanneer dat efficiënter is.
  • Controleer asynchrone toestand — gebruik until-lussen met time-outs; ga er nooit van uit dat een infrastructuuronderdeel direct na het aanmaken gereed is.
  • Toestandsbestanden — schrijf ID's van infrastructuuronderdelen naar een gedeeld JSON-bestand, zodat elke fase van het script en de afbraak dezelfde verwijzingen delen.
  • Simulatievlaggen — ondersteun altijd --dry-run voor veilige controle door beheerders vóór destructieve acties.

Deze patronen — gecombineerd met een strikte kopregel set -euo pipefail en een ERR-foutafhandeling — vormen de basis van scripts voor infrastructuur van productiekwaliteit op C2-niveau.

Gratis beginnen

Leer Bash met een AI-tutor — gratis

Schrijf echte code en voer die uit in je browser, krijg direct hulp van een AI-tutor die 24/7 beschikbaar is en ga verder waar je gebleven bent op het web of in de app.

Cursussen
22
Lessen
88

Veelgestelde vragen

Is de les “Cloudresources scripten met CLI en jq” gratis?

Ja — de volledige tekst van “Cloudresources scripten met CLI en jq” kun je hier gratis op het web lezen. Als je interactief wilt oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is, en de rest van de cursus De Linux-opdrachtregel en Bash-scripting beheersen wilt ontgrendelen, kun je upgraden naar CoddyKit PRO. De cursus De Linux-opdrachtregel en Bash-scripting beheersen bevat in totaal 4 lessen.

Wat leer ik in “Cloudresources scripten met CLI en jq”?

Beheer cloudprovider-CLI's idempotent en parse JSON-antwoorden om resources aan te maken en weer af te breken. Je oefent met De Linux-opdrachtregel en Bash-scripting beheersen door code rechtstreeks in de browser uit te voeren. Een AI-begeleider die 24/7 beschikbaar is beantwoordt je vragen terwijl je de les doorwerkt.

Heb ik ervaring nodig om met De Linux-opdrachtregel en Bash-scripting beheersen te beginnen?

Ervaring vooraf is niet nodig. De Linux-opdrachtregel en Bash-scripting beheersen op CoddyKit is opgebouwd voor beginners tot gevorderden, zodat je hier of bij het begin kunt starten en in je eigen tempo kunt leren. Dit is les 3 van 4.

Hoe lang duurt de les “Cloudresources scripten met CLI en jq”?

De meeste lessen van CoddyKit duren ongeveer 5–10 minuten. Elke les is kort en interactief, zodat je gestaag vooruitgaat en op het web en in de app precies verdergaat waar je was gebleven.

Kan ik code schrijven en uitvoeren in deze les over De Linux-opdrachtregel en Bash-scripting beheersen?

Ja. Elke les over De Linux-opdrachtregel en Bash-scripting beheersen bevat een ingebouwde code-editor, zodat je rechtstreeks in je browser echte code kunt schrijven en uitvoeren en direct feedback van AI krijgt — lokale installatie is niet nodig.

Alle lessen in deze cursus

  1. Slanke Dockerfiles en shell-entrypoints schrijven
  2. Configuraties templaten met envsubst en heredocs
  3. Cloudresources scripten met CLI en jq
  4. Health probes, readiness gates en wachtlussen
← Terug naar De Linux-opdrachtregel en Bash-scripting beheersen