Module 2 — Scripting Bash pour l'automatisation
Bonne lecture et bon apprentissage ! Junior TSAFACK – 12/09/2026 Temps de lecture estimé : 12 minutes
Les scripts de ce module ont été réellement exécutés sur une machine Debian 13, pas rédigés puis simplement décrits. Chaque sortie ci-dessous est une sortie réelle, copiée telle quelle.
1. Rapport de santé système
Section titled “1. Rapport de santé système”Un script de supervision minimal : charge, mémoire, disque, et une alerte simple si un point de montage dépasse 80 % d’utilisation.
#!/usr/bin/env bashset -euo pipefail
echo "=== Rapport de santé système — $(date '+%Y-%m-%d %H:%M:%S') ==="
echoecho "--- Charge système (load average) ---"uptime
echoecho "--- Mémoire ---"free -h
echoecho "--- Espace disque ---"df -h --output=target,size,used,avail,pcent | grep -E '^/|Mounted|Avail'
echoecho "--- Top 5 processus par consommation CPU ---"ps -eo pid,comm,%cpu,%mem --sort=-%cpu | head -n 6
echoecho "--- Alerte seuil disque (>80%) ---"df -h --output=pcent,target | tail -n +2 | while read -r pcent target; do usage="${pcent%\%}" if [ "$usage" -ge 80 ]; then echo "ATTENTION : $target est utilisé à ${usage}%" fidoneecho "Vérification terminée."Exécution réelle :
=== Rapport de santé système — 2026-09-12 22:31:15 ===
--- Charge système (load average) --- 22:31:15 up 0 min, 1 user, load average: 0.00, 0.00, 0.00
--- Mémoire --- total used free shared buff/cache availableMem: 7.7Gi 449Mi 7.1Gi 3.4Mi 249Mi 7.2GiSwap: 2.0Gi 0B 2.0Gi
--- Top 5 processus par consommation CPU --- PID COMMAND %CPU %MEM 261 bash 6.6 0.0 1 systemd 3.7 0.1 76 systemd-udevd 2.2 0.1 40 systemd-journal 1.5 0.1 213 systemd 0.8 0.1
--- Alerte seuil disque (>80%) ---ATTENTION : /usr/lib/wsl/drivers est utilisé à 94%ATTENTION : /mnt/c est utilisé à 94%ATTENTION : /mnt/d est utilisé à 96%Vérification terminée.Ce genre de script ne remplace pas un vrai outil de supervision (voir le module Observabilité) — mais c’est exactement ce que fait un script de garde (cron toutes les 5 minutes, alerte envoyée si le seuil est dépassé) avant qu’une équipe n’investisse dans Prometheus.
2. Rotation des journaux applicatifs
Section titled “2. Rotation des journaux applicatifs”On compresse les journaux de plus de N jours, puis on supprime les archives trop anciennes.
#!/usr/bin/env bashset -euo pipefail
LOG_DIR="${1:-/var/log/demo-app}"MAX_AGE_DAYS="${2:-7}"
mkdir -p "$LOG_DIR"
echo "=== Nettoyage des journaux dans $LOG_DIR (plus vieux que $MAX_AGE_DAYS jours) ==="
find "$LOG_DIR" -type f -name '*.log' -mtime "+$MAX_AGE_DAYS" -print0 | while IFS= read -r -d '' file; do gzip -f "$file" echo "Compressé : ${file}.gz" done
find "$LOG_DIR" -type f -name '*.log.gz' -mtime +30 -print -deletePour le tester réellement (pas de simulation), 5 faux journaux ont été créés avec des dates de modification réelles (1 à 5 jours), via touch -d "N days ago" :
--- État avant nettoyage ----rw-r--r-- old-1.log (1 jour)-rw-r--r-- old-2.log (2 jours)-rw-r--r-- old-3.log (3 jours)-rw-r--r-- old-4.log (4 jours)-rw-r--r-- old-5.log (5 jours)-rw-r--r-- recent.log (1 heure)
--- Fichiers ciblés (mtime > 3 jours) ---/tmp/demo-app/logs/old-5.log/tmp/demo-app/logs/old-4.log
--- Compression puis suppression des originaux ---Compressé : /tmp/demo-app/logs/old-5.log.gzCompressé : /tmp/demo-app/logs/old-4.log.gz
--- État après nettoyage ---old-1.log, old-2.log, old-3.log, recent.log (inchangés)old-4.log.gz, old-5.log.gz (nouvellement compressés)Avec un seuil de 3 jours, seuls old-4.log et old-5.log dépassent l’âge limite — exactement les deux fichiers compressés. Le comportement est vérifié, pas supposé.
3. Sauvegarde avec rotation
Section titled “3. Sauvegarde avec rotation”Un script de sauvegarde qui archive un dossier, puis ne conserve que les N sauvegardes les plus récentes.
#!/usr/bin/env bashset -euo pipefail
SRC_DIR="${1:-/opt/demo-app/data}"BACKUP_DIR="${2:-/opt/backups}"KEEP="${3:-3}"
mkdir -p "$SRC_DIR" "$BACKUP_DIR"timestamp="$(date '+%Y%m%d-%H%M%S')"archive="$BACKUP_DIR/backup-${timestamp}.tar.gz"
tar -czf "$archive" -C "$(dirname "$SRC_DIR")" "$(basename "$SRC_DIR")"echo "Archive créée : $archive"
mapfile -t backups < <(ls -1t "$BACKUP_DIR"/backup-*.tar.gz)if [ "${#backups[@]}" -gt "$KEEP" ]; then for old in "${backups[@]:$KEEP}"; do echo "Suppression (trop ancienne) : $old" rm -f "$old" donefiExécuté 4 fois de suite avec KEEP=3 :
Backup 1 → 1 sauvegarde présente, rien à supprimerBackup 2 → 2 sauvegardes présentes, rien à supprimerBackup 3 → 3 sauvegardes présentes, rien à supprimerBackup 4 → 4 sauvegardes présentes Suppression (trop ancienne) : backup-20260912-223208.tar.gz → 3 sauvegardes conservées (les plus récentes)La rotation ne se déclenche exactement qu’à la 4ᵉ exécution, quand le nombre de sauvegardes dépasse la limite — comportement attendu, vérifié en conditions réelles plutôt que déduit du code.
4. Audit des comptes utilisateurs
Section titled “4. Audit des comptes utilisateurs”#!/usr/bin/env bashset -euo pipefail
echo "--- Comptes humains (UID >= 1000) ---"awk -F: '$3 >= 1000 && $3 < 65534 {printf "%-15s UID=%-6s shell=%s\n", $1, $3, $7}' /etc/passwd
echo "--- Membres du groupe sudo ---"getent group sudo | awk -F: '{print $4}'
echo "--- Comptes avec mot de passe vide ---"while IFS=: read -r user pass _; do [ -z "$pass" ] && echo "ALERTE : $user n'a pas de mot de passe"done < /etc/shadowLire /etc/shadow nécessite les droits root — exécuté avec les privilèges administrateur, sur la vraie machine :
--- Comptes humains (UID >= 1000) ---junior UID=1000 shell=/bin/bashbatman UID=1001 shell=/bin/sh
--- Membres du groupe sudo ---junior
--- Comptes avec mot de passe vide ou verrouillé ---(aucune alerte : tous les comptes ont un mot de passe défini)5. Audit des ports ouverts
Section titled “5. Audit des ports ouverts”#!/usr/bin/env bashset -euo pipefail
ALLOWED_PORTS=(22 80 443)
listening_ports="$(ss -tln | awk 'NR>1 {print $4}' | grep -oE '[0-9]+$' | sort -un)"
for port in $listening_ports; do allowed=0 for a in "${ALLOWED_PORTS[@]}"; do [ "$port" -eq "$a" ] && allowed=1 && break done if [ "$allowed" -eq 1 ]; then echo "OK : port $port (attendu)" else echo "ALERTE : port $port en écoute mais absent de la liste blanche" fidoneSortie réelle sur la machine de test :
--- Ports TCP en LISTEN ---State Recv-Q Send-Q Local Address:Port Peer Address:PortLISTEN 0 1000 10.255.255.254:53 0.0.0.0:*
--- Comparaison avec la liste blanche (22 80 443) ---ALERTE : port 53 en écoute mais absent de la liste blancheLe port 53 détecté est un vrai résolveur DNS local propre à l’environnement WSL de la machine de test — ce n’est pas une faille, mais le script fait exactement ce qu’on attend : il signale tout ce qui n’est pas dans la liste blanche, à charge pour l’humain de qualifier l’alerte (c’est le principe même d’un contrôle de conformité : il détecte des écarts, il ne juge pas leur gravité à votre place).
Prochaine étape
Section titled “Prochaine étape”Module 3 : Bonnes pratiques et pièges réels du scripting shell — où l’on montre un vrai bug rencontré en écrivant les scripts de ce module, pas un piège théorique.