0Pricing
DevOps Bootcamp · Leçon

Script­er des ressources cloud avec la CLI et jq

Pilotez les CLI des fournisseurs cloud de manière idempotente et analysez les réponses JSON pour provisionner et supprimer des ressources.

Script­er des ressources cloud avec la CLI et jq est une leçon DevOps Bootcamp gratuite sur CoddyKit. Ceci est la leçon 3 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage DevOps Bootcamp, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours DevOps Bootcamp comprend 4 leçons au total.

Pourquoi les scripts infonuagiques idempotents sont importants

Au niveau C2, les scripts infonuagiques ne consistent pas à cliquer sur des boutons : il s'agit d'écrire du code qui peut être exécuté en toute sécurité plusieurs fois sans créer de ressources en double ni échouer lors de la deuxième exécution.

Un script idempotent vérifie si une ressource existe déjà avant de la créer. C'est la pierre angulaire d'une automatisation fiable de l'infrastructure.

  • Les CLI cloud (AWS, GCP, Azure) renvoient du JSON — l'analyse de cette sortie est essentielle.
  • jq est l'outil Unix standard pour découper, filtrer et transformer le JSON depuis des scripts d'interpréteur de commandes.
  • La combinaison de CLI, jq et de logique conditionnelle permet d'écrire des scripts de provisionnement robustes et répétables.

Tout au long de cette leçon, vous allez provisionner des compartiments S3, des instances EC2 et des rôles IAM à l'aide d'AWS CLI comme référence, avec des modèles qui se transposent directement à gcloud et az.

Installation et vérification des CLI cloud

Avant de créer des scripts, vérifiez que les bons outils sont présents. Dans l'intégration continue, épinglez toujours les versions pour éviter les divergences entre les environnements.

L'extrait ci-dessous vérifie la présence d'AWS CLI v2, de jq et du SDK GCP, et n'installe que ce qui manque — un modèle utile dans les scripts d'initialisation de machines virtuelles ou de conteneurs vierges.

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

Interrogation des ressources existantes avec jq

La première étape de tout script idempotent est une lecture — demandez à l'API si la ressource existe déjà, puis choisissez la marche à suivre.

AWS CLI renvoie toujours du JSON. jq vous permet d'extraire exactement le champ dont vous avez besoin :

  • jq -r '.Buckets[].Name' — sortie de chaîne brute, un nom de compartiment par ligne.
  • jq -e — se termine avec le code 1 si l'expression produit null ou false, ce qui le rend idéal pour les conditions if.
  • jq '.[] | select(.Name == env.BUCKET)' — filtrer à l'aide d'une variable de l'interpréteur de commandes via env.

L'extrait ci-dessous répertorie tous les compartiments S3 et vérifie si un compartiment cible existe déjà.

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

Création idempotente d'un compartiment S3

Une fois la vérification d'existence en place, encapsulez la création dans une condition de garde. Une fonction infonuagique bien structurée suit ce modèle :

  1. Lisez l'état actuel depuis l'API.
  2. Comparez l'état souhaité à l'état réel.
  3. N'agissez que sur la différence.

Notez l'option --create-bucket-configuration — elle est requise dans toutes les régions sauf us-east-1. Inscrire la région en dur dans le script évite les échecs silencieux lorsque AWS_DEFAULT_REGION n'est pas définie.

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

Analyse de JSON imbriqué : état d'une instance EC2

Les réponses EC2 sont profondément imbriquées. Le parcours de chemins avec jq et --query (JMESPath, intégré à AWS CLI) fonctionnent tous les deux — mais jq est plus puissant pour les logiques complexes.

Motifs jq essentiels pour EC2 :

  • .Reservations[].Instances[] — aplatir la structure à deux tableaux.
  • select(.State.Name == "running") — filtrer par état.
  • .Tags[] | select(.Key == "Name") | .Value — extraire la valeur d'une étiquette.

L'extrait trouve une instance en cours d'exécution grâce à son étiquette Name et renvoie son ID ainsi que son adresse IP privée.

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

Provisionnement idempotent d'un rôle IAM

Les ressources IAM sont globales et ne doivent pas être dupliquées. AWS renvoie un code d'erreur spécifique — EntityAlreadyExists — lorsque vous tentez de créer un rôle qui existe déjà. Intercepter ce code constitue un modèle d'idempotence plus propre qu'un appel préalable de liste lorsqu'il s'agit d'IAM à grande échelle.

Le script ci-dessous illustre les opérations suivantes :

  • Capturer le code de sortie de la CLI avec || true afin d'empêcher set -e d'interrompre l'exécution.
  • Analyser le message d'erreur JSON qu'AWS écrit sur la sortie d'erreur standard à l'aide d'une substitution de processus.
  • Attacher une politique uniquement si elle ne l'est pas déjà.
#!/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 avancé : transformations, mappages et toentries

Les réponses réelles de l'infrastructure contiennent des dizaines de champs. Les transformations de jq vous permettent de remodeler la sortie pour les outils en aval, les journaux ou les fichiers de configuration.

Motifs avancés essentiels :

  • map(select(...)) — filtrer un tableau sans perdre son enveloppe.
  • to_entries | map(select(.value != null)) — supprimer les champs nuls avant l'écriture dans une configuration.
  • [.[] | {id: .InstanceId, ip: .PrivateIpAddress}] — projeter les données dans une nouvelle structure.
  • @csv, @tsv, @base64 — convertisseurs de formats intégrés.

L'extrait ci-dessous extrait toutes les instances en cours d'exécution et écrit un fichier d'inventaire TSV.

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

Attente des opérations asynchrones : interrogation répétée avec jq

Les opérations infonuagiques sont asynchrones. La création d'une instance EC2 renvoie immédiatement avec l'état pending. Les scripts fiables doivent interroger jusqu'à ce que l'état souhaité soit atteint avant de continuer.

Le modèle ci-dessous utilise une boucle until avec une temporisation exponentielle. AWS CLI fournit également des sous-commandes wait (par exemple aws ec2 wait instance-running), mais une interrogation écrite manuellement vous offre un contrôle personnalisé du délai d'expiration et une journalisation plus riche.

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

Modèle multicloud : équivalents GCP et Azure

Le modèle idempotent de lecture puis d'action se transpose directement à d'autres CLI cloud. gcloud et az renvoient tous deux du JSON et prennent en charge le filtrage :

  • GCP : gcloud ... --format='json' — transmettez les données à jq exactement comme avec AWS. Utilisez gcloud ... --quiet pour supprimer les invites dans les scripts.
  • Azure : az ... --output json — même modèle. az group exists renvoie une chaîne booléenne simple (true/false), ce qui évite d'avoir besoin de jq dans les cas simples.

L'extrait montre côte à côte la création idempotente d'un groupe de ressources dans Azure et la création d'un compartiment GCS dans GCP, en utilisant le même modèle de garde.

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

Démantèlement : destruction sûre des ressources

Les scripts de destruction sont aussi importants que ceux de création. Un démantèlement sûr :

  • répertorie les ressources avant toute suppression et affiche un récapitulatif à examiner par une personne ;
  • accepte une option --dry-run afin que les opérateurs puissent confirmer le plan sans l'exécuter ;
  • supprime les ressources dans l'ordre correct des dépendances (par exemple, terminer les instances avant de supprimer les groupes de sécurité).

L'extrait ci-dessous met fin à toutes les instances EC2 portant l'étiquette Env=staging, avec une condition de simulation.

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

De bout en bout : script d’amorçage d’infrastructure idempotent

Nous rassemblons tous les éléments : un script d’amorçage de niveau production orchestre plusieurs ressources dans le bon ordre, est entièrement idempotent et émet des journaux structurés qu’un système d’intégration continue peut analyser.

Principales pratiques mises en œuvre :

  • Journalisation structurée au moyen d’un utilitaire log() qui ajoute les préfixes [INFO], [WARN] et [ERROR].
  • Fichier d’état — écrivez les identifiants des ressources créées dans un fichier d’état JSON afin que les exécutions ultérieures et les scripts de démontage partagent les mêmes références.
  • Piège à erreurs — trap intercepte les sorties inattendues et indique le numéro de la ligne en échec.
#!/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 .)"

Contrôle des connaissances : garde d’idempotence de jq

Testez votre compréhension de l’écriture de scripts infonuagiques idempotents avec jq.

Récapitulatif : automatisation des ressources infonuagiques par la CLI et jq

Cette leçon a couvert tout le cycle de vie de l’automatisation infonuagique idempotente au moyen de scripts shell, de CLI infonuagiques et de jq.

Principes fondamentaux :

  • Lire avant d’écrire — interrogez toujours d’abord l’état existant ; n’agissez que sur les différences.
  • jq -e pour les garde-fous — utilisez le mode fondé sur le statut de sortie pour piloter les branches if à partir des réponses JSON.
  • Sémantique des codes d’erreur — interceptez les codes d’erreur propres au fournisseur, par exemple EntityAlreadyExists, plutôt que d’effectuer une liste préalable lorsque cette méthode est plus efficace.
  • Interroger l’état asynchrone — utilisez des boucles until avec des délais d’expiration ; ne supposez jamais qu’une ressource est prête immédiatement après sa création.
  • Fichiers d’état — écrivez les identifiants des ressources dans un fichier JSON partagé afin que chaque phase du script et le démontage partagent les mêmes références.
  • Options d’exécution à blanc — prenez toujours en charge --dry-run pour permettre à l’opérateur d’effectuer une vérification sûre avant les actions destructrices.

Ces modèles, associés à un en-tête strict set -euo pipefail et à un piège ERR, constituent le fondement de l’écriture de scripts d’infrastructure de niveau production au niveau C2.

Questions Fréquemment Posées

La leçon « Script­er des ressources cloud avec la CLI et jq » est-elle gratuite ?

Oui — le texte complet de « Script­er des ressources cloud avec la CLI et jq » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours DevOps Bootcamp, passe à CoddyKit PRO. Le cours DevOps Bootcamp comprend 4 leçons au total.

Qu'est-ce que j'apprendrai dans « Script­er des ressources cloud avec la CLI et jq » ?

Pilotez les CLI des fournisseurs cloud de manière idempotente et analysez les réponses JSON pour provisionner et supprimer des ressources. Tu pratiques DevOps Bootcamp avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer DevOps Bootcamp ?

Aucune expérience préalable n'est requise. DevOps Bootcamp sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 3 sur 4.

Combien de temps prend la leçon « Script­er des ressources cloud avec la CLI et jq » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon DevOps Bootcamp ?

Oui. Chaque leçon DevOps Bootcamp inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. Écrire des Dockerfiles légers et des points d’entrée Shell
  2. Créer des modèles de configuration avec envsubst et des heredocs
  3. Script­er des ressources cloud avec la CLI et jq
  4. Sondes d’état, barrières de disponibilité et boucles d’attente
← Retour à DevOps Bootcamp