Aller au contenu
Infrastructure as Code medium

Modules Ansible essentiels : la cheat-sheet de survie

17 min de lecture

Logo Ansible

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.

  • 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 (copy vs template, command vs shell).
  • Vers quel lab dédié aller pour approfondir chaque module.

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).

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.

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: true

6 valeurs de state: : file, directory, absent, link, hard, touch. Voir Lab 32 — Module file.

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 sshd

Toujours mettre regexp: quand vous modifiez une option existante — sans regexp, Ansible empile une nouvelle ligne à chaque run. Voir Lab 30 — lineinfile vs 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: true

Le 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: present

state: 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.

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: true

state: (immédiat) et enabled: (boot) sont indépendants. Préférer reloaded à restarted quand le service le supporte. Voir Lab 38 — systemd_service.

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.

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 > 0

Champs 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).

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.

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.

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.txt

Niveau 18 dans la précédencebat vars: du play (niveau 14). Pour persister entre runs : ajouter cacheable: true (nécessite fact_caching configuré). Voir Lab 16 — register et set_fact.

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).

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: true

Toujours 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.

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 :

  1. Lit l’état actuel : rpm -q nginx pour vérifier si le paquet est déjà installé.
  2. Compare à l’état désiré demandé (state: present).
  3. N’agit que si nécessaire : dnf install lancé seulement si le paquet manque, sinon retour changed=false immé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ù raw est 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 avec changed_when: false.

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.

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/.initialized

Garde-fous d’idempotence :

  • changed_when: false sur toute commande lecture-seule (sinon changed=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.

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: false

Rè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 ?

Quand vous savez ce que vous voulez faire mais pas quel module, ces 4 questions guident le choix :

  1. “Modifier un fichier”copy: (statique), template: (avec variables), lineinfile: (1 ligne), blockinfile: (3-10 lignes).
  2. “Gérer un fichier” (sans toucher au contenu) → file: (mode, owner, état).
  3. “Gérer un paquet/service”dnf: (RHEL) ou package: (multi-distro), systemd_service: (services).
  4. “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: ou changed_when:).

Pour tout le reste, la doc officielle est l’outil de recherche : Ansible builtin modules.

Trois pièges qui touchent plusieurs modules à la fois :

  • mode: 0644 non quoté → YAML décimal au lieu d’octal. Toujours mode: "0644" (avec guillemets).
  • {{ }} dans when:, failed_when:, changed_when: → warning Ansible 2.16+. Ces directives sont déjà des expressions Jinja2.
  • register: + loop: → la variable contient .results (liste), pas .stdout directement. Voir Lab 16.
  • 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, pas copy).
  • Les labs dédiés approfondissent chaque module avec exercices et challenges pytest.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn