
Vous savez lancer des commandes ad-hoc ; il est temps de basculer en playbook. Cette page écrit votre premier playbook complet : installation de nginx sur les webservers, ouverture du port 80 dans firewalld, démarrage du service, vérification HTTP. Tout en YAML structuré, exécuté en une commande (ansible-playbook), idempotent (un second passage affiche changed=0). C’est le passage de la commande jetable à l’infrastructure-as-code.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Comprendre la structure d’un playbook :
name,hosts,become,gather_facts,tasks; - Écrire un playbook YAML qui enchaîne 5 tâches sur le groupe
webservers; - Exécuter avec
ansible-playbooket lire chaque colonne du PLAY RECAP ; - Vérifier l’idempotence en relançant le playbook :
changed=0au second passage ; - Tester le résultat avec une tâche
uri:qui vérifie le code HTTP retourné.
Prérequis
Section intitulée « Prérequis »- Le lab provisionné, préparé, et la connexion SSH validée (cf. les pages précédentes de cette section) ;
- L’inventaire YAML chargé via
ansible.cfg; - Une compréhension des commandes ad-hoc (cf. Commandes ad-hoc) — un playbook est essentiellement une suite ordonnée de commandes ad-hoc encapsulées en YAML.
L’anatomie d’un playbook
Section intitulée « L’anatomie d’un playbook »Un playbook est un fichier YAML qui contient une liste de plays. Un play cible un groupe d’hôtes et exécute une liste de tâches dans l’ordre. Une tâche appelle un module avec ses paramètres.
playbook└── play (cible : un groupe d'hôtes) ├── tasks │ ├── task 1 (module + paramètres) │ ├── task 2 (module + paramètres) │ └── ... └── handlers (optionnel — déclenchés par notify:)Les clés essentielles d’un play :
| Clé | Rôle |
|---|---|
name: | Description humaine du play (apparaît dans le PLAY RECAP) |
hosts: | Pattern d’hôtes ciblé (all, webservers, web1.lab:!web2.lab…) |
become: | Élévation sudo si true |
gather_facts: | Collecte les facts au début (true par défaut) |
vars: | Variables locales au play |
tasks: | Liste ordonnée de tâches |
handlers: | Tâches déclenchées en fin de play par notify: |
Le playbook complet
Section intitulée « Le playbook complet »Créez le fichier labs/premiers-pas/premier-playbook/site.yml à la racine du repo ansible-training. Ce playbook installe nginx + ouvre le port 80 + démarre le service + vérifie le code HTTP :
---- name: Déployer nginx sur les webservers hosts: webservers become: true gather_facts: true
tasks: - name: Installer le paquet nginx ansible.builtin.dnf: name: nginx state: present
- name: Démarrer et activer nginx au boot ansible.builtin.systemd: name: nginx state: started enabled: true
- name: Ouvrir le port HTTP (80) dans firewalld ansible.posix.firewalld: service: http permanent: true immediate: true state: enabled
- name: Vérifier que nginx répond sur localhost ansible.builtin.uri: url: http://localhost status_code: 200 register: nginx_response
- name: Afficher le code HTTP retourné ansible.builtin.debug: msg: "nginx répond avec le code {{ nginx_response.status }} sur {{ inventory_hostname }}"Ligne par ligne
Section intitulée « Ligne par ligne »hosts: webservers — cible le groupe défini dans l’inventaire (web1.lab + web2.lab). Pas all, pas db1.lab. Le play ne tourne que sur les hôtes voulus.
become: true — toutes les tâches s’exécutent en root via sudo. Sans cette ligne, dnf install échouerait avec une erreur de permission.
gather_facts: true — Ansible exécute le module setup au démarrage et expose les facts (ansible_distribution, ansible_default_ipv4…). Vous pouvez les désactiver avec false si vous savez que vous n’en avez pas besoin (gain de quelques secondes).
Tâche 1 — ansible.builtin.dnf : installe le paquet nginx (idempotent — n’agit que s’il n’est pas déjà installé). Le FQCN (ansible.builtin.dnf) est la forme recommandée depuis Ansible 2.10 ; c’est l’objectif RHCE.
Tâche 2 — ansible.builtin.systemd : combine enable: true (démarrage au boot) et state: started (démarré maintenant). Idempotent : si le service est déjà dans cet état, changed=false.
Tâche 3 — ansible.posix.firewalld : ouvre le service http (port 80) dans la zone par défaut. permanent: true écrit la règle dans /etc/firewalld/zones/, immediate: true l’applique tout de suite. Sans immediate, la règle ne prend effet qu’au prochain firewall-cmd --reload ou reboot.
Tâche 4 — ansible.builtin.uri : interroge http://localhost côté managed node, vérifie que le code retour est 200. La sortie est stockée dans la variable nginx_response via register:.
Tâche 5 — ansible.builtin.debug : affiche un message contenant {{ nginx_response.status }} (le code HTTP) et {{ inventory_hostname }} (le nom de l’hôte courant). Une interpolation Jinja2 classique.
Deux réflexes à prendre dès maintenant
Section intitulée « Deux réflexes à prendre dès maintenant »Deux habitudes qui font la différence entre du code qui démarre et du code qui tient en production — autant les ancrer sur ce premier playbook :
- FQCN partout (
ansible.builtin.dnf,ansible.posix.firewalld). Plus long de 14 caractères, mais c’est l’usage attendu en 2026 et un objectif RHCE explicite. Prenez l’habitude dès la première ligne plutôt que d’avoir à migrer tout votre code dans 6 mois. - Jamais
shell: dnf install -y nginxquand un module dédié existe.shell:rejoue la commande à chaque exécution et casse l’idempotence. Si le réflexe « passer par shell » revient, c’est qu’il faut chercher le bon module avecansible-doc -l.
Exécuter le playbook
Section intitulée « Exécuter le playbook »Depuis la racine du repo :
ansible-playbook labs/premiers-pas/premier-playbook/site.ymlSortie attendue au premier passage (sur des managed nodes vierges de nginx) :
PLAY [Déployer nginx sur les webservers] ***************************************
TASK [Gathering Facts] *********************************************************ok: [web1.lab]ok: [web2.lab]
TASK [Installer le paquet nginx] ***********************************************changed: [web1.lab]changed: [web2.lab]
TASK [Démarrer et activer nginx au boot] ***************************************changed: [web1.lab]changed: [web2.lab]
TASK [Ouvrir le port HTTP (80) dans firewalld] *********************************changed: [web1.lab]changed: [web2.lab]
TASK [Vérifier que nginx répond sur localhost] *********************************ok: [web1.lab]ok: [web2.lab]
TASK [Afficher le code HTTP retourné] ******************************************ok: [web1.lab] => msg: nginx répond avec le code 200 sur web1.labok: [web2.lab] => msg: nginx répond avec le code 200 sur web2.lab
PLAY RECAP *********************************************************************web1.lab : ok=5 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0web2.lab : ok=5 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0Lire le PLAY RECAP
Section intitulée « Lire le PLAY RECAP »Le PLAY RECAP est la ligne la plus importante de toute exécution Ansible. Chaque colonne a un sens précis :
| Colonne | Sens |
|---|---|
ok | Tâches exécutées sans erreur (avec ou sans changement) |
changed | Tâches qui ont modifié l’état du managed node |
unreachable | Hôtes injoignables (SSH, DNS, firewall) |
failed | Tâches qui ont échoué (rc != 0, exception) |
skipped | Tâches sautées par when: ou tags: |
rescued | Tâches récupérées par un rescue: block |
ignored | Tâches échouées mais ignorées par ignore_errors: true |
Lecture de la sortie ci-dessus : sur web1.lab, 5 tâches OK dont 3 ont modifié l’état (installation nginx, démarrage, règle firewall). Les deux autres (uri et debug) sont en ok sans changement — elles lisent, elles ne modifient pas.
Vérifier l’idempotence
Section intitulée « Vérifier l’idempotence »Relancez immédiatement la même commande :
ansible-playbook labs/premiers-pas/premier-playbook/site.ymlPLAY RECAP attendu :
web1.lab : ok=5 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0web2.lab : ok=5 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0changed=0 sur les deux hôtes — c’est la preuve d’idempotence. Tous les modules ont vérifié l’état réel, l’ont trouvé conforme à l’état désiré, et n’ont rien modifié. Le playbook est sûr à relancer indéfiniment.
Tester depuis l’extérieur
Section intitulée « Tester depuis l’extérieur »Le playbook a vérifié http://localhost depuis chaque webserver. Vous pouvez aussi tester depuis votre poste de contrôle :
curl -I http://10.10.20.21HTTP/1.1 200 OKServer: nginx/1.26.1Date: Sat, 25 Apr 2026 12:48:32 GMTContent-Type: text/html...C’est le résultat tangible : nginx tourne, firewalld autorise le port 80, le service est accessible depuis le réseau du lab.
Pièges fréquents
Section intitulée « Pièges fréquents »| Symptôme | Cause | Fix |
|---|---|---|
Permission denied (publickey) au début du run | Mauvais chemin de clé dans l’inventaire ou ansible.cfg | Vérifier ansible_ssh_private_key_file ; depuis un sous-répertoire, utiliser {{ inventory_dir }}/../ssh/id_ed25519 plutôt que playbook_dir |
MODULE FAILURE: No module named 'firewall' | python3-firewall non installé sur le managed node | Lancer le playbook labs/bootstrap/prepare-managed-nodes/playbook.yml qui installe les bindings Python |
changed=N à chaque relance | Module non-idempotent (shell, command) | Bascule sur le module idempotent dédié, ou ajoute creates:/removes: |
state: started ne démarre pas le service | Le service n’existe pas / nom mal orthographié | ansible web1.lab -m systemd -a 'name=nginx' | jq .status.LoadState ; ou ssh ansible@web1.lab systemctl status nginx |
ConnectionError: Connection timeout sur uri: | Port pas ouvert dans firewalld | Vérifier que la tâche firewalld s’est bien exécutée AVANT uri: (ordre des tâches) |
À retenir
Section intitulée « À retenir »- Un playbook est un fichier YAML qui contient une ou plusieurs plays, chaque play ciblant un pattern d’hôtes.
- Les clés essentielles d’un play :
name,hosts,become,gather_facts,tasks. - Les tâches utilisent des modules (
ansible.builtin.dnf,ansible.builtin.systemd,ansible.posix.firewalld,ansible.builtin.uri,ansible.builtin.debug) — la forme FQCN est la norme RHCE. register:capture la sortie d’un module dans une variable,{{ var }}l’interpole dans un message ou un autre paramètre.- Le PLAY RECAP est la ligne de vérité —
changed=0au second passage est le signal d’idempotence. - Un playbook idempotent est sûr à relancer ; c’est la base du contrat Ansible.
Pratiquer dans le lab
Section intitulée « Pratiquer dans le lab »Cette page a un lab d’accompagnement : labs/premiers-pas/premier-playbook/ dans
stephrobert/ansible-training. Il contient
un README.md guidé, un Makefile (make verify lance les tests), et un
challenge final auto-évalué : déployer Apache (httpd) sur db1.lab avec ouverture firewalld port 8080 et page custom.
Une fois le lab provisionné :
cd ~/Projets/ansible-training/labs/premiers-pas/premier-playbook/
cat README.md # tuto pas à pascat challenge/README.md # consigne du challenge finalpytest -v challenge/tests/ # lancer les tests testinfraSi les tests passent, vous maîtrisez les concepts couverts dans ce guide. En
cas de blocage, docs/troubleshooting.md
à la racine du repo couvre les pièges fréquents (rate-limit SSH, clé absente,
collection manquante).