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,shellet 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
ansible <motif> -m <module> -a "<arguments>" [options]
- le motif désigne les hôtes (chapitre 3) ;
-mnomme le module, de préférence avec son nom complet (FQCN) ;-adonne ses arguments, sous la formeclé=valeurséparés par des espaces.
Sans -m, Ansible utilise le module command. Les options utiles :
| Option | Effet |
|---|---|
-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, -vvv | Plus de détails ; -vvv montre la connexion SSH, utile pour dépanner |
Des commandes ad hoc utiles
Interroger le parc, sans rien modifier :
# 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" :
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 :
# 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 :
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 :
| Module | Ce qu'il fait | Quand l'utiliser |
|---|---|---|
ansible.builtin.command | Lance un programme, sans shell : pas de ` | , >, &&, *ni de variable$HOME` |
ansible.builtin.shell | Lance la commande dans /bin/sh : tuyaux, redirections et jokers fonctionnent | Quand on a vraiment besoin du shell |
Un module dédié (apt, user, service…) | Décrit un état et ne fait que ce qui manque | Dè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.
# 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 apache2 | ansible.builtin.apt avec name: apache2 |
shell: systemctl restart apache2 | ansible.builtin.service avec state: restarted, dans un handler |
shell: useradd -m clambert | ansible.builtin.user avec name: clambert |
shell: echo "…" >> /etc/hosts | ansible.builtin.lineinfile |
shell: mkdir -p /srv/data && chmod 750 /srv/data | ansible.builtin.file avec state: directory et mode: "0750" |
shell: wget https://… -O /tmp/x | ansible.builtin.get_url |
shell: ufw allow 22/tcp | community.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.
| Besoin | Module | Exemple d'arguments |
|---|---|---|
| Paquets Debian, Ubuntu | ansible.builtin.apt | name=apache2 state=present ; upgrade=dist |
| Paquets Red Hat, Rocky, Alma | ansible.builtin.dnf | name=httpd state=latest |
| Paquets, quelle que soit la distribution | ansible.builtin.package | name=chrony state=present |
| Services | ansible.builtin.service, ansible.builtin.systemd_service | name=apache2 state=started enabled=true |
| Fichier copié tel quel | ansible.builtin.copy | src=index.html dest=/var/www/suivi/ |
| Fichier généré depuis un modèle | ansible.builtin.template | src=chrony.conf.j2 dest=/etc/chrony/chrony.conf (chapitre 7) |
| Dossier, lien, droits, suppression | ansible.builtin.file | path=/srv/data state=directory mode=0750 |
| Une ligne dans un fichier | ansible.builtin.lineinfile | path=/etc/hosts line="192.168.10.180 srv-journaux" |
| Un bloc de lignes dans un fichier | ansible.builtin.blockinfile | path=/etc/hosts block="…" |
| Comptes et groupes | ansible.builtin.user, ansible.builtin.group | name=clambert groups=sudo append=true |
| Clés SSH autorisées | ansible.posix.authorized_key | user=clambert key="ssh-ed25519 …" |
| Tâches planifiées | ansible.builtin.cron | name=sauvegarde hour=2 minute=0 job=/usr/local/bin/sauve.sh |
| Télécharger, décompresser | ansible.builtin.get_url, ansible.builtin.unarchive | url=https://… dest=/opt/ |
| Pare-feu ufw (Debian, Ubuntu) | community.general.ufw | rule=allow port=443 proto=tcp |
| Pare-feu firewalld (Red Hat) | ansible.posix.firewalld | service=https permanent=true state=enabled immediate=true |
| Points de montage | ansible.posix.mount | path=/mnt/nas src=192.168.10.183:/mnt/tank/partages fstype=nfs4 state=mounted |
| Fuseau horaire | community.general.timezone | name=Europe/Paris |
| Redémarrer et attendre | ansible.builtin.reboot | reboot_timeout=600 |
| Attendre un port | ansible.builtin.wait_for | port=443 timeout=60 |
| Tester une URL | ansible.builtin.uri | url=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 :
ansible srv-journaux -m ansible.builtin.setup -a "filter=ansible_distribution*"
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 *) :
| Fact | Exemple de valeur |
|---|---|
ansible_hostname, ansible_fqdn | srv-journaux, srv-journaux.ad.sorbier.example |
ansible_os_family, ansible_distribution, ansible_distribution_major_version | Debian, Debian, 13 |
ansible_default_ipv4.address | 192.168.10.180 |
ansible_memtotal_mb, ansible_processor_vcpus | 7951, 4 |
ansible_mounts | La 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 :
# 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 hoc | Playbook |
|---|---|
| Une action ponctuelle : une vérification, une urgence, une collecte d'information | Une configuration qu'on veut retrouver et rejouer |
| Une seule tâche | Des dizaines de tâches dans un ordre précis |
| Rien n'est gardé, sauf l'historique du shell | Versionné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é.
- Afficher la mémoire libre (
free -m) de tous les serveurs Linux. - Installer le paquet
rsyncsursrv-journaux. - Créer le compte
mdiallo(Moussa Diallo) dans le groupesudo, sans retirer ses autres groupes, sur tous les serveurs Linux. - Afficher l'adresse IPv4 principale de chaque serveur Linux, à partir des facts.
- Vérifier sans rien modifier si le paquet
telnetserait supprimé sur les serveurs Linux.
Voir la réponseMasquer la réponse de l'exercice 1
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".
- Que se passe-t-il à chaque exécution ?
- Rends-la idempotente sans changer de module.
- Quel module dédié ferait mieux ?
Voir la réponseMasquer la réponse de l'exercice 3
- 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é. -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.commandsuffit, puisqu'il n'y a ni tuyau ni redirection.ansible.builtin.unarchiveavecsrc=/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 (sansremote_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.
- Quelle option est obligatoire ?
- Quelle tâche ad hoc supprime le compte
jdupontet son dossier personnel ? - Que fait la même commande sans
remove=true?
Voir la réponseMasquer la réponse de l'exercice 4
name, précédée de=.ansible linux -b -m ansible.builtin.user -a "name=jdupont state=absent remove=true".- Elle supprime le compte mais laisse
/home/jdupontet 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é ?