L’idempotence est le principe central d’Ansible : appliquer un playbook une ou dix fois doit produire le même résultat. En théorie. En pratique, il suffit d’une mauvaise utilisation du module shell pour casser cette garantie.
Le problème avec shell et command
Ces deux modules exécutent des commandes shell sans vérification — ils ne savent pas si la commande a déjà été exécutée.
# NON idempotent
- name: Créer le répertoire
shell: mkdir /opt/myapp
# Idempotent
- name: Créer le répertoire
file:
path: /opt/myapp
state: directory
mode: '0755'
Règle pratique : si un module Ansible natif fait ce que votre commande shell ferait, utilisez-le. file, copy, template, lineinfile, service, package — ces modules vérifient l’état avant d’agir.
Quand shell est inévitable : creates et removes
Parfois vous devez utiliser shell. Les paramètres creates et removes permettent de conditionner l’exécution :
- name: Installer un binaire custom
shell: |
curl -L https://example.com/tool.tar.gz | tar -xz -C /usr/local/bin
args:
creates: /usr/local/bin/tool
La tâche ne s’exécute que si /usr/local/bin/tool n’existe pas encore. Idempotence restaurée.
lineinfile vs template : lequel choisir ?
lineinfile modifie une ligne dans un fichier. Il est idempotent pour des modifications ponctuelles, mais fragile pour gérer des blocs entiers ou des fichiers structurés.
Préférez template quand vous gérez un fichier de configuration complet. Jinja2 vous permet de rendre le fichier entier à partir de variables — résultat garanti idempotent.
- name: Déployer la configuration nginx
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: '0644'
notify: restart nginx
Tester l’idempotence de vos playbooks
# Premier run
ansible-playbook site.yml
# Deuxième run : aucune tâche ne devrait avoir changed=true
ansible-playbook site.yml
Si des tâches restent à changed au deuxième run, votre playbook n’est pas idempotent. Molecule permet d’automatiser ce test.
Les handlers : pièges d’exécution
Les handlers ne s’exécutent qu’une fois à la fin du play, même si plusieurs tâches les notifient. Mais attention : si un play échoue avant la fin, les handlers ne s’exécutent pas. Utilisez --force-handlers ou meta: flush_handlers si vous devez les forcer en cours de play.
Ces concepts sont pratiqués dans notre formation Ansible.