Aller au contenu
Réussir mon BTS SIO

Tape une notion, une commande ou le nom d'une épreuve.

Par exemple

Chapitre 414 minIntermédiaire

Commandes ad hoc et modules Ansible

Par Josselyn C., prof de BTS SIO. Mis à jour le .

À la fin de ce chapitre, tu sauras

  • lancer une commande ad hoc sur un groupe de serveurs ;
  • choisir entre command, shell et un module dédié ;
  • élever ses privilèges avec become ;
  • lire les facts d'un serveur avec setup ;
  • trouver les options d'un module avec ansible-doc.

4 exercices corrigés

Une commande ad hoc est une action Ansible lancée directement en ligne de commande, sans playbook : un seul module, appliqué tout de suite à un ou plusieurs hôtes. Ce chapitre explique la syntaxe de ansible, les modules à connaître, le choix entre command, shell et un module dédié, la lecture des facts et la documentation intégrée, ansible-doc.

La syntaxe d'une commande ad hoc

bash
ansible <motif> -m <module> -a "<arguments>" [options]
  • le motif désigne les hôtes (chapitre 3) ;
  • -m nomme le module, de préférence avec son nom complet (FQCN) ;
  • -a donne ses arguments, sous la forme clé=valeur séparés par des espaces.

Sans -m, Ansible utilise le module command. Les options utiles :

OptionEffet
-b (--become)Exécuter avec élévation de privilèges (sudo sous Linux)
-K (--ask-become-pass)Demander le mot de passe d'élévation
-u <compte>Se connecter avec un autre compte que remote_user
-k (--ask-pass)Demander le mot de passe SSH (il faut le paquet sshpass sur le nœud de contrôle)
-i <inventaire>Utiliser un autre inventaire que celui d'ansible.cfg
-f <n>Traiter n hôtes en parallèle
-C (--check)Simuler : dire ce qui changerait, sans rien modifier
-D (--diff)Montrer les différences dans les fichiers modifiés
-v, -vv, -vvvPlus de détails ; -vvv montre la connexion SSH, utile pour dépanner

Des commandes ad hoc utiles

Interroger le parc, sans rien modifier :

bash
# Depuis quand chaque serveur Linux tourne
ansible linux -a "uptime"

# Place libre sur la partition racine
ansible linux -a "df -h /"

# Version du noyau
ansible linux -a "uname -r"

Chaque hôte répond par une ligne nom | statut | rc=code >>, suivie de la sortie de la commande. Avec ansible linux -a "hostname" :

text
srv-journaux | CHANGED | rc=0 >>
srv-journaux
srv-web | CHANGED | rc=0 >>
srv-web

L'ordre des réponses dépend de l'hôte qui répond le plus vite. rc est le code de retour de la commande : 0 signifie qu'elle a réussi.

Agir sur le parc, avec des modules idempotents :

bash
# Installer tcpdump sur tous les serveurs Linux
ansible linux -b -m ansible.builtin.apt -a "name=tcpdump state=present update_cache=true"

# S'assurer qu'Apache tourne sur le serveur web
ansible serveurs_web -b -m ansible.builtin.service -a "name=apache2 state=started"

# Déposer un message d'accueil
ansible linux -b -m ansible.builtin.copy -a "content='Serveur de Sorbier Logistique : accès réservé\n' dest=/etc/motd mode=0644"

# Récupérer le journal d'Apache sur le nœud de contrôle (un dossier par hôte)
ansible serveurs_web -b -m ansible.builtin.fetch -a "src=/var/log/apache2/error.log dest=./journaux/"

# Redémarrer les serveurs Linux un par un, en attendant que chacun revienne
ansible linux -b -f 1 -m ansible.builtin.reboot

La première exécution de la commande copy affiche CHANGED et le détail du fichier déposé ; la deuxième affiche SUCCESS avec "changed": false, car le fichier a déjà le bon contenu et les bons droits :

text
srv-web | SUCCESS => {
    "changed": false,
    "checksum": "a25d38fa32e8f0a29d38110f8d44888908dacb74",
    "dest": "/etc/motd",
    "gid": 0,
    "group": "root",
    "mode": "0644",
    "owner": "root",
    "path": "/etc/motd",
    "size": 49,
    "state": "file",
    "uid": 0
}

command, shell ou un module dédié ?

Trois façons de faire exécuter quelque chose :

ModuleCe qu'il faitQuand l'utiliser
ansible.builtin.commandLance un programme, sans shell : pas de `, >, &&, *ni de variable$HOME`
ansible.builtin.shellLance la commande dans /bin/sh : tuyaux, redirections et jokers fonctionnentQuand on a vraiment besoin du shell
Un module dédié (apt, user, service…)Décrit un état et ne fait que ce qui manqueDès qu'il existe

command et shell ne savent pas si la commande a déjà été faite : Ansible les marque changed à chaque fois et les relance à chaque exécution. Deux arguments rendent une commande idempotente quand elle produit un fichier :

  • creates=<chemin> : ne lance la commande que si le chemin n'existe pas ;
  • removes=<chemin> : ne la lance que si le chemin existe.
bash
# Ne génère la clé Diffie-Hellman que si le fichier n'existe pas déjà
ansible serveurs_web -b -m ansible.builtin.command \
  -a "openssl dhparam -out /etc/ssl/dhparam.pem 2048 creates=/etc/ssl/dhparam.pem"
Au lieu de…Utilise
shell: apt-get install -y apache2ansible.builtin.apt avec name: apache2
shell: systemctl restart apache2ansible.builtin.service avec state: restarted, dans un handler
shell: useradd -m clambertansible.builtin.user avec name: clambert
shell: echo "…" >> /etc/hostsansible.builtin.lineinfile
shell: mkdir -p /srv/data && chmod 750 /srv/dataansible.builtin.file avec state: directory et mode: "0750"
shell: wget https://… -O /tmp/xansible.builtin.get_url
shell: ufw allow 22/tcpcommunity.general.ufw

Les modules à connaître

Les modules de ansible.builtin sont livrés avec ansible-core ; les autres viennent des collections installées au chapitre 2.

BesoinModuleExemple d'arguments
Paquets Debian, Ubuntuansible.builtin.aptname=apache2 state=present ; upgrade=dist
Paquets Red Hat, Rocky, Almaansible.builtin.dnfname=httpd state=latest
Paquets, quelle que soit la distributionansible.builtin.packagename=chrony state=present
Servicesansible.builtin.service, ansible.builtin.systemd_servicename=apache2 state=started enabled=true
Fichier copié tel quelansible.builtin.copysrc=index.html dest=/var/www/suivi/
Fichier généré depuis un modèleansible.builtin.templatesrc=chrony.conf.j2 dest=/etc/chrony/chrony.conf (chapitre 7)
Dossier, lien, droits, suppressionansible.builtin.filepath=/srv/data state=directory mode=0750
Une ligne dans un fichieransible.builtin.lineinfilepath=/etc/hosts line="192.168.10.180 srv-journaux"
Un bloc de lignes dans un fichieransible.builtin.blockinfilepath=/etc/hosts block="…"
Comptes et groupesansible.builtin.user, ansible.builtin.groupname=clambert groups=sudo append=true
Clés SSH autoriséesansible.posix.authorized_keyuser=clambert key="ssh-ed25519 …"
Tâches planifiéesansible.builtin.cronname=sauvegarde hour=2 minute=0 job=/usr/local/bin/sauve.sh
Télécharger, décompresseransible.builtin.get_url, ansible.builtin.unarchiveurl=https://… dest=/opt/
Pare-feu ufw (Debian, Ubuntu)community.general.ufwrule=allow port=443 proto=tcp
Pare-feu firewalld (Red Hat)ansible.posix.firewalldservice=https permanent=true state=enabled immediate=true
Points de montageansible.posix.mountpath=/mnt/nas src=192.168.10.183:/mnt/tank/partages fstype=nfs4 state=mounted
Fuseau horairecommunity.general.timezonename=Europe/Paris
Redémarrer et attendreansible.builtin.rebootreboot_timeout=600
Attendre un portansible.builtin.wait_forport=443 timeout=60
Tester une URLansible.builtin.uriurl=https://suivi.sorbier.example status_code=200

Lire les facts d'un serveur

Les facts sont les informations qu'Ansible collecte sur un hôte : système, version, adresses IP, interfaces, mémoire, disques, processeur. Le module setup les affiche ; un filtre évite d'en recevoir des centaines :

bash
ansible srv-journaux -m ansible.builtin.setup -a "filter=ansible_distribution*"
text
srv-journaux | SUCCESS => {
    "ansible_facts": {
        "ansible_distribution": "Debian",
        "ansible_distribution_file_parsed": true,
        "ansible_distribution_file_path": "/etc/os-release",
        "ansible_distribution_file_variety": "Debian",
        "ansible_distribution_major_version": "13",
        "ansible_distribution_release": "trixie",
        "ansible_distribution_version": "13.7",
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false
}

Quelques facts courants (le filtre filter= accepte les noms avec le préfixe ansible_ et le joker *) :

FactExemple de valeur
ansible_hostname, ansible_fqdnsrv-journaux, srv-journaux.ad.sorbier.example
ansible_os_family, ansible_distribution, ansible_distribution_major_versionDebian, Debian, 13
ansible_default_ipv4.address192.168.10.180
ansible_memtotal_mb, ansible_processor_vcpus7951, 4
ansible_mountsLa liste des partitions montées, avec leur taille et leur espace libre

Dans un playbook, les facts sont collectés automatiquement au début de chaque play et se lisent dans le dictionnaire ansible_facts, sans le préfixe : ansible_facts['distribution'], ansible_facts['default_ipv4']['address'] (chapitre 6).

Trouver les options d'un module : ansible-doc

Chaque module a sa documentation, installée avec lui et lisible hors ligne :

bash
# La documentation complète : options, valeurs par défaut, exemples, valeurs renvoyées
ansible-doc ansible.builtin.user

# Un squelette de tâche avec toutes les options, à copier dans un playbook
ansible-doc -s ansible.builtin.user

# Les modules d'une collection
ansible-doc -l community.general | grep -i ufw

# Les plugins d'un autre type : connexion, inventaire, filtre, lookup
ansible-doc -t connection -l

Dans la documentation, une option précédée de = est obligatoire, une option précédée de - est facultative ; default: donne sa valeur par défaut et choices: les valeurs permises. La section EXAMPLES vaut souvent mieux que bien des tutoriels. La même documentation est en ligne sur docs.ansible.com, mais ansible-doc a l'avantage de correspondre exactement à la version installée.

Ad hoc ou playbook ?

Commande ad hocPlaybook
Une action ponctuelle : une vérification, une urgence, une collecte d'informationUne configuration qu'on veut retrouver et rejouer
Une seule tâcheDes dizaines de tâches dans un ordre précis
Rien n'est gardé, sauf l'historique du shellVersionnée dans Git, relue, documentée

Une bonne règle : si tu tapes la même commande ad hoc une deuxième fois, elle mérite d'aller dans un playbook.

Exercices

Exercice 1 sur 4 Écrire les commandes ad hoc Réussi À revoir

Écris la commande ad hoc pour chaque besoin, avec le module le plus adapté.

  1. Afficher la mémoire libre (free -m) de tous les serveurs Linux.
  2. Installer le paquet rsync sur srv-journaux.
  3. Créer le compte mdiallo (Moussa Diallo) dans le groupe sudo, sans retirer ses autres groupes, sur tous les serveurs Linux.
  4. Afficher l'adresse IPv4 principale de chaque serveur Linux, à partir des facts.
  5. Vérifier sans rien modifier si le paquet telnet serait supprimé sur les serveurs Linux.
Voir la réponseMasquer la réponse de l'exercice 1
bash
ansible linux -a "free -m"
ansible srv-journaux -b -m ansible.builtin.apt -a "name=rsync state=present"
ansible linux -b -m ansible.builtin.user -a "name=mdiallo comment='Moussa Diallo' groups=sudo append=true"
ansible linux -m ansible.builtin.setup -a "filter=ansible_default_ipv4"
ansible linux -b -C -m ansible.builtin.apt -a "name=telnet state=absent"

Pour le compte, append=true est indispensable : sans lui, groups=sudo retire l'utilisateur de tous ses autres groupes secondaires. Pour la dernière, -C (mode check) affiche CHANGED sur les serveurs où le paquet est installé, SUCCESS ailleurs, sans rien supprimer.

Tu avais trouvé ?

Exercice 2 sur 4 Pourquoi ça ne marche pas ? Réussi À revoir

Chloé tape : ansible linux -a "cat /var/log/syslog | grep -i error > /tmp/erreurs.txt". Ansible répond, pour chaque serveur, une erreur de cat : « cat: '|': No such file or directory ». Explique et corrige.

Voir la réponseMasquer la réponse de l'exercice 2

Sans -m, Ansible utilise le module command, qui lance le programme sans shell : |, grep et > sont passés comme des arguments de cat, qui cherche un fichier nommé |. Il faut le module shell : ansible linux -b -m ansible.builtin.shell -a "grep -i error /var/log/syslog > /tmp/erreurs.txt". Le -b est nécessaire pour lire les journaux. Au passage, cat … | grep est inutile : grep lit le fichier lui-même. Sur Debian 13, les journaux sont dans le journal de systemd et /var/log/syslog n'existe pas toujours : journalctl -p err --since today est souvent la meilleure source.

Tu avais trouvé ?

Exercice 3 sur 4 Rendre une commande idempotente Réussi À revoir

Le serveur srv-journaux doit recevoir l'archive d'un outil, /opt/outil.tar.gz, et la décompresser dans /opt/outil, ce qui crée le fichier /opt/outil/bin/outil. Moussa propose la commande ad hoc -m ansible.builtin.shell -a "tar xzf /opt/outil.tar.gz -C /opt".

  1. Que se passe-t-il à chaque exécution ?
  2. Rends-la idempotente sans changer de module.
  3. Quel module dédié ferait mieux ?
Voir la réponseMasquer la réponse de l'exercice 3
  1. L'archive est décompressée à chaque fois et la tâche est toujours marquée changed, même si rien n'a changé : impossible de savoir si quelque chose a vraiment été modifié.
  2. -m ansible.builtin.command -a "tar xzf /opt/outil.tar.gz -C /opt creates=/opt/outil/bin/outil" : la commande n'est lancée que si le fichier n'existe pas. command suffit, puisqu'il n'y a ni tuyau ni redirection.
  3. ansible.builtin.unarchive avec src=/opt/outil.tar.gz dest=/opt remote_src=true creates=/opt/outil/bin/outil : il gère la décompression, accepte aussi une archive envoyée depuis le nœud de contrôle (sans remote_src) ou téléchargée depuis une URL.

Tu avais trouvé ?

Exercice 4 sur 4 Lire la documentation Réussi À revoir

Avec ansible-doc ansible.builtin.user, Chloé lit, entre autres : = name, - password_lock, - state avec choices: [absent, present] et default: present, et - remove avec default: false.

  1. Quelle option est obligatoire ?
  2. Quelle tâche ad hoc supprime le compte jdupont et son dossier personnel ?
  3. Que fait la même commande sans remove=true ?
Voir la réponseMasquer la réponse de l'exercice 4
  1. name, précédée de =.
  2. ansible linux -b -m ansible.builtin.user -a "name=jdupont state=absent remove=true".
  3. Elle supprime le compte mais laisse /home/jdupont et ses fichiers, qui appartiennent alors à un identifiant numérique sans nom. Si un nouveau compte reçoit plus tard le même UID, il hérite de ces fichiers : c'est une raison de choisir consciemment entre garder (pour archivage) et supprimer.

Tu avais trouvé ?

Chapitre terminé !

Fais les 4 exercices avant de passer à la suite : c'est en t'entraînant que tout se fixe.

chapitres
–
de série
–
du cours
–
Chapitre suivant : Écrire un playbook AnsibleRevoir le sommaire du cours