
Cette page est une cheat-sheet de survie. Elle présente les 15 modules Ansible que vous allez croiser dès le premier playbook, bien avant d'avoir lu les guides détaillés qui leur sont dédiés. L'objectif est simple : vous donner le squelette minimal de chaque module pour ne pas être perdu quand un lab ou un guide les utilise en filigrane.
Chaque entrée suit le même format : quand l'utiliser, squelette le plus court, exemple concret, lien vers le guide complet. Quelques minutes de lecture suffisent à parcourir l'ensemble, gardez cette page ouverte pendant vos premiers labs.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Les 15 modules que vous croiserez dans 90% des playbooks débutants.
- Le squelette minimal de chaque module (5-10 lignes).
- Comment distinguer des modules proches (
copyvstemplate,commandvsshell). - Vers quel lab dédié aller pour approfondir chaque module.
ansible.builtin.debug
Section intitulée « ansible.builtin.debug »Quand l'utiliser : afficher la valeur d'une variable ou un message dans le PLAY RECAP. C'est l'équivalent du print() Python, votre meilleur allié pour comprendre ce qu'Ansible voit.
- name: Afficher une variable ansible.builtin.debug: var: ansible_distribution # → "ansible_distribution": "AlmaLinux"
- name: Afficher un message custom ansible.builtin.debug: msg: "OS detecte : {{ ansible_distribution }} {{ ansible_distribution_version }}"Pas de lab dédié, vous l'utiliserez partout dès les premiers playbooks. Plus de détails dans la doc Ansible officielle (builtin.debug).
ansible.builtin.copy
Section intitulée « ansible.builtin.copy »Quand l'utiliser : transférer un fichier du control node vers le managed node, ou écrire un contenu inline.
# Transfert d un fichier local- ansible.builtin.copy: src: files/banner.txt dest: /etc/issue.net owner: root group: root mode: "0644"
# Contenu inline- ansible.builtin.copy: content: "Provisionne par Ansible\n" dest: /etc/ansible-managed mode: "0644"Pièges classiques : oublier le \n final dans content:, oublier les guillemets sur mode: "0644" (sinon YAML décimal). Voir Lab 31, Module copy.
ansible.builtin.file
Section intitulée « ansible.builtin.file »Quand l'utiliser : gérer l'état d'un fichier ou répertoire sans transférer de contenu, créer un dossier, supprimer un fichier, créer un symlink, ajuster les permissions.
# Creer un repertoire- ansible.builtin.file: path: /var/log/myapp state: directory owner: nobody mode: "0750"
# Supprimer un fichier (ou un dossier — recursif)- ansible.builtin.file: path: /etc/old-config.conf state: absent
# Symlink- ansible.builtin.file: src: /opt/myapp/releases/v1.0.0 dest: /opt/myapp/current state: link force: true6 valeurs de state: : file, directory, absent, link, hard, touch. Voir Lab 32, Module file.
ansible.builtin.lineinfile
Section intitulée « ansible.builtin.lineinfile »Quand l'utiliser : modifier ou ajouter une seule ligne dans un fichier existant, sans toucher au reste. Idéal pour les configs partagées (/etc/sysctl.conf, /etc/hosts).
- ansible.builtin.lineinfile: path: /etc/ssh/sshd_config regexp: '^#?PermitRootLogin' line: 'PermitRootLogin no' backup: true validate: 'sshd -t -f %s' notify: Reload sshdToujours mettre regexp: quand vous modifiez une option existante, sans regexp, Ansible empile une nouvelle ligne à chaque run. Voir Lab 30, lineinfile vs template.
ansible.builtin.template
Section intitulée « ansible.builtin.template »Quand l'utiliser : générer un fichier complet depuis un template Jinja2 avec interpolation de variables. Successeur de copy: quand vous avez besoin de logique.
- ansible.builtin.template: src: templates/myapp.conf.j2 dest: /etc/myapp.conf owner: root group: root mode: "0644" backup: true validate: 'myapp --check-config %s' lstrip_blocks: true trim_blocks: trueLe template .j2 vit dans templates/ du playbook. Toujours activer lstrip_blocks: true + trim_blocks: true pour le whitespace control. Voir Lab 29, Module template.
ansible.builtin.dnf (RHEL) / package (multi-distro)
Section intitulée « ansible.builtin.dnf (RHEL) / package (multi-distro) »Quand l'utiliser : installer ou supprimer des paquets. Sur RHEL/AlmaLinux/Rocky, préférer dnf: (options spécifiques). Sur multi-distro, package: suffit.
# RHEL : dnf- ansible.builtin.dnf: name: - vim-enhanced - htop - tree state: present
# Multi-distro : package- ansible.builtin.package: name: nginx state: presentstate: accepte : present (défaut), latest (à éviter en prod, upgrade systématique), absent. Toujours préférer la liste dans name: plutôt qu'une boucle (3-5× plus rapide). Voir Lab 36, package et Lab 37, dnf options.
ansible.builtin.systemd_service
Section intitulée « ansible.builtin.systemd_service »Quand l'utiliser : gérer un service systemd (RHEL 7+, Ubuntu 16.04+), démarrer, arrêter, recharger, activer au boot.
- ansible.builtin.systemd_service: name: chronyd state: started # started | stopped | restarted | reloaded enabled: true # Au boot
# Apres un nouveau unit file dans /etc/systemd/system/- ansible.builtin.systemd_service: name: myapp state: started enabled: true daemon_reload: truestate: (immédiat) et enabled: (boot) sont indépendants. Préférer reloaded à restarted quand le service le supporte. Voir Lab 38, systemd_service.
ansible.builtin.user
Section intitulée « ansible.builtin.user »Quand l'utiliser : créer, modifier ou supprimer un utilisateur Linux.
- ansible.builtin.user: name: alice shell: /bin/bash groups: [wheel, docker] append: true state: present password: "{{ 'plain_password' | password_hash('sha512') }}"Bonnes pratiques : append: true pour ajouter aux groupes sans écraser les existants ; password hashé côté Ansible avec password_hash('sha512'), voir Lab 27, Filtres Jinja2 avancés.
ansible.builtin.stat
Section intitulée « ansible.builtin.stat »Quand l'utiliser : récupérer des informations sur un fichier ou dossier (existence, taille, mode, owner, checksum). C'est le module n°1 pour la logique conditionnelle.
- ansible.builtin.stat: path: /etc/myapp.conf register: myapp_stat
- name: Action si le fichier existe et est non vide ansible.builtin.copy: content: "ok\n" dest: /tmp/marker.txt when: myapp_stat.stat.exists and myapp_stat.stat.size > 0Champs courants : .stat.exists, .stat.isfile, .stat.isdir, .stat.size, .stat.mode, .stat.checksum. Pas de lab dédié, utilisé en duo avec register: + when: (cf. Lab 16).
ansible.builtin.uri
Section intitulée « ansible.builtin.uri »Quand l'utiliser : faire un appel HTTP depuis le managed node (équivalent curl). Pratique pour les healthchecks, les appels API, les webhooks.
- ansible.builtin.uri: url: http://localhost:8080/health method: GET status_code: [200, 204] return_content: true register: health_check
- ansible.builtin.debug: msg: "Body : {{ health_check.json | default(health_check.content) }}"Champs utiles du register : .status, .content, .json (parsé automatiquement si Content-Type: application/json). Pas de lab dédié, couvert en filigrane dans plusieurs labs.
ansible.builtin.assert
Section intitulée « ansible.builtin.assert »Quand l'utiliser : valider une condition au début d'un play et échouer fail-fast si elle n'est pas satisfaite. Outil de programmation défensive.
- name: Valider les prerequis ansible.builtin.assert: that: - ansible_distribution == "AlmaLinux" - ansible_distribution_major_version | int >= 9 - app_config is defined - app_config.port is integer fail_msg: "Prerequis non remplis — voir les conditions ci-dessus" success_msg: "Tous les prerequis sont valides"Préférer assert: à un long when: quand la validation est explicitement le but de la tâche. Voir Lab 28, Tests Jinja2.
ansible.builtin.set_fact
Section intitulée « ansible.builtin.set_fact »Quand l'utiliser : créer une variable au runtime dans un play, à partir d'une logique de calcul ou d'une capture précédente.
- name: Calculer un label de deploy ansible.builtin.set_fact: deploy_label: "{{ inventory_hostname }}-{{ ansible_date_time.epoch }}"
- name: Utiliser le label ansible.builtin.copy: content: "Label : {{ deploy_label }}\n" dest: /tmp/marker.txtNiveau 18 dans la précédence, bat vars: du play (niveau 14). Pour persister entre runs : ajouter cacheable: true (nécessite fact_caching configuré). Voir Lab 16, register et set_fact.
ansible.builtin.pause
Section intitulée « ansible.builtin.pause »Quand l'utiliser : attendre un nombre de secondes, ou demander une confirmation interactive à l'opérateur. Utile pour les rolling updates et les opérations critiques.
- name: Attendre 5s pour laisser le service redemarrer ansible.builtin.pause: seconds: 5
- name: Confirmer manuellement avant la migration ansible.builtin.pause: prompt: "Appuyez sur ENTREE pour continuer la migration BDD"Cas d'usage typique : entre un restart et un healthcheck, pour laisser le service finir son init. Pas de lab dédié, utilisé dans les labs 09 (parallélisme) et 10 (async).
ansible.posix.firewalld
Section intitulée « ansible.posix.firewalld »Quand l'utiliser : ouvrir/fermer un port dans firewalld (RHEL/AlmaLinux/Rocky). Module fourni par la collection ansible.posix (pas builtin).
- name: Ouvrir le port 80 (HTTP) ansible.posix.firewalld: service: http state: enabled permanent: true immediate: true
# Port custom- name: Ouvrir le port 8443 ansible.posix.firewalld: port: 8443/tcp state: enabled permanent: true immediate: trueToujours permanent: true ET immediate: true, sinon la règle est appliquée maintenant mais disparaît au reboot, ou inversement. Phase 4.5 du parcours (à venir) couvrira en détail.
Pourquoi les modules dédiés sont idempotents
Section intitulée « Pourquoi les modules dédiés sont idempotents »Avant de découvrir les trois modules d'exécution brute (raw, command, shell), il faut comprendre ce qui rend les autres idempotents, parce que c'est exactement ce qui manque à ces trois-là.
Quand vous écrivez ansible.builtin.dnf: name=nginx state=present, Ansible n'exécute pas bêtement dnf install nginx. Il transfère sur la cible un script Python (le module dnf) qui :
- Lit l'état actuel :
rpm -q nginxpour vérifier si le paquet est déjà installé. - Compare à l'état désiré demandé (
state: present). - N'agit que si nécessaire :
dnf installlancé seulement si le paquet manque, sinon retourchanged=falseimmédiat.
Tous les modules dédiés suivent ce schéma lecture → comparaison → action conditionnelle. Le module file lit stat avant de modifier le mode. Le module copy calcule un checksum SHA-256 et compare au fichier distant avant de transférer. Le module lineinfile lit le fichier ligne par ligne et applique le regexp: avant d'écrire. C'est le code Python du module qui implémente l'idempotence, Ansible lui-même n'en sait rien, il fait juste circuler des dictionnaires JSON.
Cette propriété a un coût : écrire un module idempotent est plus complexe que d'écrire un script shell. C'est pour ça qu'il en existe plus de 3 000 dans l'écosystème Ansible, chacun encapsule la logique « état actuel vs état désiré » d'un domaine précis (paquets, services, firewall, utilisateurs…). Quand vous utilisez le bon module, vous héritez gratuitement de tout ce travail.
Modules d'exécution brute, uniquement en dernier recours
Section intitulée « Modules d'exécution brute, uniquement en dernier recours »raw, command, shell font l'inverse : ils exécutent une commande arbitraire sur la cible. Ansible n'a aucun moyen générique de savoir ce que cette commande va faire ni si elle a déjà produit l'effet attendu, il l'exécute donc à chaque run, ce qui marque la tâche changed=true à chaque exécution et casse l'idempotence de votre playbook.
Règle absolue : avant d'écrire command: ou shell:, cherchez le module dédié avec ansible-doc -l | grep <thème>. Si vous écrivez shell: dnf install -y nginx, vous écrivez du Bash en YAML, c'est un échec de conception, pas une simplification. Ces modules existent pour les 3 cas légitimes suivants :
- Aucun module dédié n'existe pour la tâche (cas rare en 2026, vérifiez deux fois avant d'y croire).
- Bootstrap d'un host sans Python : le seul cas où
rawest obligatoire (Ansible ne peut pas exécuter de module Python tant que Python 3 n'est pas installé). - Lecture-seule pour récupérer une valeur dans une variable (
register:), toujours avecchanged_when: false.
ansible.builtin.raw
Section intitulée « ansible.builtin.raw »Quand l'utiliser : exécuter une commande sans Python côté cible. Le seul cas légitime : bootstrapper Python 3 sur une image minimale (RHEL UBI, Alpine, équipement réseau Cisco/Junos).
- name: Installer Python 3 sur un host sans Python ansible.builtin.raw: which python3 || dnf install -y python3 register: py_install changed_when: "'Installed' in py_install.stdout"gather_facts: false est obligatoire dans le play, sinon Ansible essaie d'exécuter le module setup qui demande Python. Une fois Python installé via raw, basculez sur les modules normaux dans les plays suivants.
ansible.builtin.command
Section intitulée « ansible.builtin.command »Quand l'utiliser : exécuter une commande sans shell (pas de pipe |, pas de redirection >, pas de glob *). C'est la forme préférée entre les deux quand vous avez besoin d'exécuter un binaire, Ansible passe directement les arguments sans interprétation shell, ce qui ferme la porte aux injections.
- name: Lire la version d openssl (lecture-seule) ansible.builtin.command: openssl version register: ssl_version changed_when: false
- name: Lancer un script qui crée un fichier marqueur (idempotent grâce à creates:) ansible.builtin.command: /usr/local/bin/init-db.sh args: creates: /var/lib/myapp/.initializedGarde-fous d'idempotence :
changed_when: falsesur toute commande lecture-seule (sinonchanged=1à chaque run).creates: <chemin>: la tâche est skippée si le fichier existe, utile pour les scripts d'init.removes: <chemin>: la tâche est skippée si le fichier n'existe pas, utile pour les scripts de nettoyage.
Voir ecrire-code/failed-when-changed-when pour le détail.
ansible.builtin.shell
Section intitulée « ansible.builtin.shell »Quand l'utiliser : comme command: mais avec shell (pipe, redirection, expansion de variables). À utiliser seulement si vous avez vraiment besoin d'une fonctionnalité shell, sinon préférer command: pour la sécurité.
- name: Compter les lignes contenant "Error" ansible.builtin.shell: | grep -c "Error" /var/log/myapp.log || true register: error_count changed_when: falseRègle de choix : besoin de |, >, *, && ? → shell:. Sinon → command:. Et dans tous les cas : avez-vous vraiment vérifié qu'aucun module dédié n'existe ?
Comment savoir quel module utiliser ?
Section intitulée « Comment savoir quel module utiliser ? »Quand vous savez ce que vous voulez faire mais pas quel module, ces 4 questions guident le choix :
- "Modifier un fichier" →
copy:(statique),template:(avec variables),lineinfile:(1 ligne),blockinfile:(3-10 lignes). - "Gérer un fichier" (sans toucher au contenu) →
file:(mode, owner, état). - "Gérer un paquet/service" →
dnf:(RHEL) oupackage:(multi-distro),systemd_service:(services). - "Exécuter une commande arbitraire" → vérifier d'abord avec
ansible-doc -l | grep <thème>qu'aucun module dédié n'existe. Si vraiment rien :command:(sans shell, préféré),shell:(avec pipe/redirection),raw:(sans Python). Toujours avec un garde-fou (creates:,removes:ouchanged_when:).
Pour tout le reste, la doc officielle est l'outil de recherche : Ansible builtin modules.
Pièges récurrents communs aux modules
Section intitulée « Pièges récurrents communs aux modules »Trois pièges qui touchent plusieurs modules à la fois :
mode: 0644non quoté → YAML décimal au lieu d'octal. Toujoursmode: "0644"(avec guillemets).{{ }}danswhen:,failed_when:,changed_when:→ warning Ansible 2.16+. Ces directives sont déjà des expressions Jinja2.register:+loop:→ la variable contient.results(liste), pas.stdoutdirectement. Voir Lab 16.
À retenir
Section intitulée « À retenir »- 15 modules suffisent pour 90% des playbooks débutants.
- Cette page est une référence rapide, gardez-la ouverte pendant vos premiers labs.
- Chaque module a un squelette minimal de 5-10 lignes, pas besoin de tout connaître au départ.
- Convention RHCE 2026 : préfixer toujours par le namespace (
ansible.builtin.copy, pascopy). - Les labs dédiés approfondissent chaque module avec exercices et challenges pytest.