Ansible est un outil en ligne de commande (CLI), développé par Red Hat, qui permet d'automatiser la configuration de serveurs selon le principe de l'Infrastructure as Code (IaC).
Il se connecte généralement aux machines à configurer via SSH, puis exécute différentes tâches à l'aide de modules Ansible, qui s'appuient notamment sur Python côté machine distante. Les tâches à effectuer sont décrites dans des fichiers de configuration, comme des playbooks YAML, qui définissent l'état souhaité des machines.
Plongeons ensemble dedans !
1. Pourquoi Ansible ?
Faire son premier serveur, c'est chouette. On le bichonne.
Son deuxième ? C'est chouette aussi, on le bichonne.
Le troisième ? C'est toujours chouette… on le bichonne encore…
ZzzzzZZZZ…
Au bout d'un moment, ça devient répétitif. Et quand on arrive au dixième serveur, il faut arrêter de les traiter comme des pièces uniques et commencer à penser autrement.
C'est d'ailleurs pour ça que, dans la communauté DevOps, on entend souvent cet adage :
"Traitez vos serveurs comme du bétail, pas comme des animaux de compagnie."
C'est un peu violent comme phrase, je te l'accorde. Mais l'infrastructure, ce n'est pas fait pour être cajolée. Un serveur ne devrait pas être irremplaçable, ni nécessiter des heures de manipulations manuelles pour retrouver une configuration fonctionnelle.
J'adore faire de l'infra. Mais quand un serveur tombe en rade, c'est là que le stress monte. Il faut comprendre ce qui s'est passé, réparer, parfois tout reconstruire… et espérer ne rien oublier au passage.
Et si on pouvait reconstruire un serveur sans repartir de zéro à la main ?
C'est justement là qu'Ansible entre en jeu.
En décrivant la configuration de nos serveurs dans des fichiers, on peut automatiser leur installation et leur configuration. On sait ce qui doit être installé, quels services doivent tourner et quels fichiers doivent être présents.
Résultat : moins de manipulations manuelles, moins d'oublis et surtout, la possibilité de reconstruire un serveur de manière reproductible.
Ansible ne supprime pas les pannes. Mais il permet de mieux s'y préparer, et de passer moins de temps à tout reconstruire dans l'urgence.
2. L'inventaire et la connexion
2.1. L'inventaire
Pour qu'Ansible puisse se connecter à nos serveurs, il lui faut un inventaire de ceux-ci.
Pour ça, on crée un dossier (pour moi ce sera ~/lab) et un fichier : lab/inventory.yml :
web:
hosts:
web01:
ansible_host: ipduserveur
ansible_user: root
ansible_port: 22
ansible_ssh_private_key_file: ~/.ssh/id_ansible
ansible_ssh_common_args: "-o StrictHostKeyChecking=no"
Voici un exemple de notre inventaire.
webc'est le nom du groupe pour regrouper plusieurs hosts.hostsc'est pour indiquer qu'on va ajouter des machinesweb01c'est le nom d'une machine
Bien évidemment, web et web01 sont des noms arbitraires et on peut totalement les changer. On verra juste après à quoi ils servent.
ansible_* sont des variables qu'Ansible va utiliser quand on voudra se connecter.
Pour le tuto, j'ai ajouté StrictHostKeyChecking=no. C'est pratique sur un serveur jetable ou qu'on vient d'acheter : ça évite qu'Ansible reste bloqué sur une erreur de vérification (car l'hôte n'est pas encore connu). Mais c'est à enlever pour les playbooks qui n'initialisent pas les serveurs !
On peut bien entendu ajouter des machines à la suite. Il faut juste respecter l'indentation car on est sur un yaml.
Quelques commandes intéressantes pour vérifier qu'on a bien notre machine dans la liste : ansible all -i inventory.yml --list-hosts.

On peut aussi filtrer par groupe : ansible web -i inventory.yml --list-hosts.
Décortiquons la commande :
ansibleappelle le toolweb / allappelle le groupe ou l'intégralité des groupes-i inventory.ymlindique l'inventaire à utiliser pour cette commande--list-hostspermet d'avoir la liste des machines
2.2. La connexion
Pour vérifier qu'Ansible a accès à notre serveur, on a à disposition un module ping :
ansible web -i inventory.yml -m ping
-m permet d'appeler un module suivi de son nom
Si tout se passe bien, la machine répond par un "pong".
3. Les facts
Ansible peut collecter des informations liées à la machine que l'on peut consulter à l'aide du module setup : ansible web -i inventory.yml -m setup.

Attention ... cette commande a tendance à rendre un pavé de résultats. En fait Ansible va récupérer tout un tas d'infos liées à notre machine et on va pouvoir plus tard les utiliser.
Avec filter, on peut réduire drastiquement la liste : ansible web -i inventory.yml -m setup -a "filter=ansible_hostname"

4. Les playbooks
Bien, maintenant que l'on a vu qu'Ansible pouvait se connecter à des machines via l'inventaire et récupérer automatiquement des infos via les facts, on va pouvoir commencer à automatiser un peu les tâches !
On a jusque-là uniquement fait des commandes unitaires. Mais le but c'est pas de taper des commandes pour appeler des modules à la mano. Nous on veut au-to-ma-ti-ser !
4.1. Mon premier playbook
Un playbook c'est un fichier avec un ensemble de tâches. Voyons un peu un exemple dans ~/lab/playbook.yml :
---
- name: Tuto Playbook
hosts: all
tasks:
- name: Affichage d'un message
ansible.builtin.debug:
msg: Mon premier message en console
Décomposons le fichier :
---Initialisation du playbooknamenom du play (ensemble de machines et de tâches)hostson cible le type d'hosts (iciall)tasksce sont les tâches qu'on lançait juste avant en commande.
Ansible propose des modules comme ici : ansible.builtin.debug qui prend en paramètre un msg .
Dans tasks, on peut avoir une ou plusieurs task qui se composent toujours de la même manière :
namequi est le nom de la tâcheansible.builtin.debugqui est le nom du module et sa catégorie (ici ansible, module debug)- des paramètres pour le module
Pour le lancer : ansible-playbook -i inventory.yml playbook.yml .
La majorité des erreurs que j'ai rencontrées étaient des erreurs dues à l'indentation. Le yaml c'est vraiment un langage structuré, parfois je cherchais ailleurs alors que c'était juste une tabulation en trop ou absente.

Notre playbook a bien lancé le play "Tuto Playbook".
On voit qu'il a lancé deux tâches. Mais c'est étrange car dans notre play, on en a déclaré qu'une seule. Il faut savoir qu'à chaque fois qu'on va lancer notre commande de playbook, Ansible va de lui-même appeler en début de play, l'équivalent de la commande ansible all -i inventory.yml -m setup pour y récupérer nos facts que l'on a vus juste avant.
Mais à quoi ça peut servir ? Pour pouvoir les avoir à dispo dans nos tasks !
Voici un exemple pour récupérer le nom de la machine :
---
- name: Tuto Playbook
hosts: all
tasks:
- name: Affichage d'un message
ansible.builtin.debug:
msg: Mon premier message en console
- name: Affichage de la date
ansible.builtin.debug:
var: ansible_facts['date_time'].date
En utilisant var à la place de msg, j'affiche directement une variable récupérée depuis les facts.

ansible_facts est l'intégralité des facts, on réduit à la date_time puis à date. Si je n'utilise pas la date :

Là on a juste affiché finalement la propriété d'un objet récupéré depuis les facts. Mais on peut aussi enregistrer des variables récupérées à la volée :
---
- name: Tuto Playbook
hosts: all
tasks:
- name: Affichage d'un message
ansible.builtin.debug:
msg: Mon premier message en console
- name: Collecte de la date en commande
ansible.builtin.command: date
register: result_date
- name: Affichage de la date
ansible.builtin.debug:
var: result_date.stdout
Grâce à register on envoie le résultat de la commande dans la variable result_date puis on affiche la sortie avec le debug et stdout.
4.2. Les Statuts
Parfois en fonction de l'exécution du playbook, la sortie dans le terminal peut afficher différents statuts à différents endroits. Pour les statuts les plus communs :
oktout s'est bien passé, rien à changer dans l'étatchangedtout s'est bien passé mais il est possible que l'état d'un fichier ait été modifiéunreachableen général c'est l'hôte qui ne répond pasfailedc'est un échec, une commande qui n'existe pas, une mauvaise indentation, etc.skippedc'est une tâche qui a été ignorée (on le verra plus tard)
On va s'attarder sur changed car il est un peu tricky. Quand Ansible effectue une tâche, il va indiquer à la fin son statut. En fonction de la tâche effectuée, Ansible peut indiquer que l'état a provoqué un changement sur l'hôte même si ce n'est pas le cas.
En effet, quand on utilise ce module : ansible.builtin.command: date en soi, rien n'a été modifié sur l'hôte, mais Ansible ne regarde pas la commande en elle-même, il détecte juste le module command. Par sécurité, il indique changed.
C'est pour cela que lors de l'exécution du playbook précédent, j'avais eu un changed=1.

Ça peut être problématique au niveau de la lecture quand on a beaucoup de tâches car ça peut brouiller les vraies tâches qui auraient provoqué un changement.
Heureusement, on peut indiquer si une tâche peut rester sans changement avec changed_when.
- name: Collecte de la date en commande
ansible.builtin.command: date
register: result_date
changed_when: false
De cette manière, si on relance, aucun changement n'a été détecté sur le système :

Attention cependant avec cette méthode, car si la commande est vraiment susceptible de faire un changement sur le serveur, alors on ne sera pas au courant.
4.3. Les variables
On vient de voir avec register qu'on pouvait stocker des variables.
Mais on peut aller plus loin que ça avec. On peut déclarer une variable directement dans le play.
---
- name: Premier Playbook
hosts: all
vars:
server_name: web01
Ici on a initialisé notre variable server_name. Et on peut les appeler par interpolation :
---
- name: Premier Playbook
hosts: all
vars:
server_name: web01
tasks:
- name: "Affichage d'un message"
ansible.builtin.debug:
msg: "Hello world ! [{{ server_name }}]"
Et comme vu plus haut, on peut les appeler directement via var pour la commande debug :
tasks:
- name: "Affichage d'un message"
ansible.builtin.debug:
var: server_name
On peut donc déclarer à la volée ou à l'initialisation.
Dans un playbook parfois ce n'est pas pratique, on peut aussi les déclarer dans un fichier. Je vais créer un ~/lab/vars.yml :
---
server_name: "web01"
J'appelle ensuite ce fichier dans mon playbook :
---
- name: Premier Playbook
hosts: all
var_files:
- vars.yml
tasks:
- name: "Affichage d'un message"
ansible.builtin.debug:
msg: "Hello world ! [{{ server_name }}]"
On verra plus tard qu'on peut aussi trier les variables par groups (web) ou par hosts (web01).
4.4. Les conditions
Ansible nous permet de rendre des tâches conditionnelles en fonction d'opérateurs (comme en dev).
---
- name: Premier Playbook
hosts: all
vars:
server_name: web01
tasks:
- name: "Affichage d'un message"
ansible.builtin.debug:
msg: "Hello world ! [{{ server_name }}]"
when: ansible_facts["distribution"] == "Debian"
De cette façon, si le when ne passe pas à true, la tâche est ignorée et le statut de la tâche passe à SKIPPED.
On trouve toutes sortes d'opérateurs :
| Opérateurs | Signification |
== |
Est égal à |
!= |
Est différent de |
and |
ET : les deux conditions doivent être vraies |
or |
OU : au moins une condition doit être vraie |
> |
Supérieur à |
< |
Inférieur à |
>= |
Supérieur ou égal à |
<= |
Inférieur ou égal à |
4.5. Les boucles
On peut ajouter une boucle à une tâche :
- name: "Que la force soit avec toi"
ansible.builtin.debug:
msg: "Hello {{ item }}"
loop:
- Obiwan
- Anakin
- Quigon
item correspond à chaque entrée de la boucle.
- name: "Que la force soit avec toi"
ansible.builtin.debug:
msg: "Hello {{ item }}"
loop:
- Obiwan
- Anakin
- Quigon
when: item == Anakin
Et en combinant tout ce qu'on a vu, on peut remplacer les entrées de la loop avec un var :
- name: Premier Playbook
hosts: all
vars:
server_name:
- Obiwan
- Anakin
- Quigon
tasks:
- name: "Affichage d'un message"
ansible.builtin.debug:
msg: "Hello world ! [{{ server_name }}]"
when: ansible_facts["distribution"] == "Debian"
- name: "Que la force soit avec toi"
ansible.builtin.debug:
msg: "Hello {{ item }}"
loop: "{{ server_name }}"
4.6. Les handlers
Les handlers sont des actions qui sont effectuées à la fin du play. Prenons un exemple :
- name: Premier Playbook
hosts: all
tasks:
- name: "Affichage d'un premier message"
ansible.builtin.debug:
msg: "Hello city !"
- name: "Affichage d'un second message"
ansible.builtin.debug:
msg: "Hello country !"
- name: "Affichage d'un troisième message"
ansible.builtin.debug:
msg: "Hello world !"
Ici on a donc 3 messages d'affilée.
J'aimerais qu'à la fin du second message, s'il y a eu un changement, cela déclenche un message. Pour ça on va ajouter un handler :
---
- name: Premier Playbook
hosts: all
tasks:
- name: "Affichage d'un premier message"
ansible.builtin.debug:
msg: "Hello city !"
- name: "Affichage d'un second message"
ansible.builtin.debug:
msg: "Hello country !"
changed_when: true
notify: "Trigger second message"
- name: "Affichage d'un troisième message"
ansible.builtin.debug:
msg: "Hello world !"
handlers:
- name: "Trigger second message"
ansible.builtin.debug:
msg: "Le second message a trigger ce handler"
Si une tâche est censée déclencher une action, alors on va mettre un paramètre notify qui va prendre en valeur le nom du handler.
Dans l'exemple on utilise ansible.builtin.debug donc par défaut il ne déclenche pas de changement, on force ici le changement d'état avec changed_when: true.
Ce qui aura pour action à la fin du play, de déclencher le handler correspondant.

Bien sûr ici on a un exemple simple mais voici ce qui se passe si on utilise nginx :
---
- name: Configurer Nginx
hosts: web
tasks:
- name: Déployer la configuration Nginx
ansible.builtin.copy:
src: ./nginx.conf
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: '0644'
notify: Redémarrer Nginx
handlers:
- name: Redémarrer Nginx
ansible.builtin.service:
name: nginx
state: restarted
J'ai fait un changement dans nginx (copie de fichiers) et il faut redémarrer nginx pour prendre en compte les changements. Pour ça on voit bien l'intérêt du handler.

4.7. Actions sur les fichiers et dossiers
On va voir à présent comment gérer les fichiers et les dossiers et ça tombe bien car l'exemple du dessus a entamé tout ça.
---
- name: Configurer Nginx
hosts: web
tasks:
- name: Création d'un dossier
ansible.builtin.file:
state: directory
path: /root/test-ansible
owner: root
group: root
mode: '0755'
Ici on dit : le path ciblé doit être un dossier (directory). S'il n'existe pas, crée-le.
Ensuite il regarde les permissions. Si le dossier existe, alors il ne vérifiera que les permissions.
Bien entendu on peut faire beaucoup d'opérations avec le module ansible.builtin.file. Je te laisse aller voir la doc.
Dans le point juste au-dessus on a vu brièvement comment copier :
---
- name: Configurer Nginx
hosts: web
tasks:
- name: Déployer la configuration Nginx
ansible.builtin.copy:
src: ./nginx.conf
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: '0644'
notify: Redémarrer Nginx
handlers:
- name: Redémarrer Nginx
ansible.builtin.service:
name: nginx
state: restarted
Ici src est la machine qui exécute la commande Ansible, pas l'hôte. Mais si je rajoute remote_src: true alors src n'est plus la machine qui contrôle Ansible mais la machine contrôlée (host) :
- name: Déployer la configuration Nginx
ansible.builtin.copy:
src: /etc/nginx/nginx.bkp
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: '0644'
remote_src: true
notify: Redémarrer Nginx
4.8. Les services
Imaginons que je souhaite redémarrer le service ssh.
Sur une approche "classique" j'aurais fait : systemctl restart sshd.service.
Avec Ansible, on utilise le module ansible.builtin.service :
- name: Redémarrer SSH
ansible.builtin.service:
name: ssh
state: restarted
On peut aussi pour un service que l'on vient d'installer comme nginx l'activer au démarrage :
- name: Redémarrer Nginx
ansible.builtin.service:
name: nginx
state: started
enabled: true
De cette manière on a fait l'équivalent de : systemctl enable --now nginx.service .
Pour le state on a plusieurs paramètres : started , stopped , restarted , reloaded qui ont les mêmes fonctions que leurs commandes respectives sur les serveurs.
Si un service est déjà allumé, et qu'on demande de le started , Ansible agit de manière idempotente : la tâche passe en ok, rien n'est modifié.
4.9. Installation de paquets
Un peu le même principe que la gestion des services mais avec un module différent :
- name: Installer Nginx
ansible.builtin.apt:
name: nginx
state: present
state permet de dire ce que l'on veut, ici vérifier que nginx est installé. S'il ne l'est pas, il l'installe.
state peut aussi prendre comme valeur absent pour vérifier qu'un paquet n'est pas présent (le désinstaller s'il est présent).
Avant d'installer des paquets, le mieux sur une machine c'est d'update sa liste des paquets avec un sudo apt update .
Avec Ansible tout pareil :
- name: Actualiser la liste des paquets
ansible.builtin.apt:
update_cache: true
Et si je veux sudo apt update && apt upgrade ?
- name: Actualiser la liste des paquets et mettre à jour
ansible.builtin.apt:
update_cache: true
upgrade: true
Pour les exemples, je suis en root. Ce n'est pas une bonne pratique, on verra par la suite que sudo se gère avec la propriété become un peu plus bas pour une meilleure gestion des privilèges.
4.10. Les utilisateurs et groupes
Étant donné qu'on peut gérer l'ensemble du serveur via des fichiers, on va voir maintenant pour les users.
Si je veux ajouter un user, sur un serveur je fais : sudo adduser obiwan .
En playbook c'est ça :
- name: Créer un utilisateur
ansible.builtin.user:
name: obiwan
state: present
Tu remarques qu'on utilise souvent state , c'est le même principe, on vérifie qu'il existe, sinon on le crée. Si on met state: absent ça supprimera le user.
Si je veux créer le répertoire (pour un user qui n'est pas un user de service) :
- name: Créer un utilisateur
ansible.builtin.user:
name: obiwan
state: present
create_home: true
Si je veux ajouter un groupe à un user c'est très simple aussi :
- name: Promotion d'un user
ansible.builtin.user:
name: anakin
state: present
groups: chevalier
append: true
On ajoute un groupe avec groups suivi de son nom. Mais à quoi sert append ?
Si anakin était dans le groupe maître sans le append alors il aurait perdu son rang de maître au profit de chevalier. (Bon en réalité Anakin n'a jamais été promu au rang de maître !)
Tout ça n'est possible que si les groupes sont déjà existants, sinon il faut les créer en avance :
- name: Créer le groupe des chevaliers jedi
ansible.builtin.group:
name: chevalier
state: present
- name: Promotion d'un user
ansible.builtin.user:
name: anakin
state: present
groups: chevalier
append: true
4.11. Become or not become
Comme sur un serveur directement, certaines tâches nécessitent de passer sudo pour ce faire. Comme pour créer un utilisateur, il faut être sudo.
Jusque là je n'avais jamais parlé de droits et privilèges. C'est le moment.
Si on regarde le tuto, on voit bien qu'on exécute certaines actions qui, si elles avaient été faites sur le serveur, auraient nécessité sudo :
ansible.builtin.aptansible.builtin.groupansible.builtin.useransible.builtin.service
En général ces actions demandent du sudo. Pour ça, on va utiliser un paramètre : become .
Reprenons notre installation de nginx :
- name: Installer Nginx
ansible.builtin.apt:
name: nginx
state: present
Si Ansible se connecte avec un user qui n'a pas les privilèges nécessaires alors il aura un échec de permission pour exécuter ce play. Pour ça on va ajouter une élévation temporaire de privilèges :
- name: Installer Nginx
ansible.builtin.apt:
name: nginx
state: present
become: true
Grâce à ça, plus d'erreurs. Par contre … il faut bien entendu qu'Ansible dispose d'un user capable de sudo.
Ici j'ai placé become au niveau de la tâche, mais on peut aussi placer become au niveau du play en entier :
---
- name: Configurer Nginx
hosts: web
become: true
Si sudo demande un mot de passe, il le demandera également pour Ansible.
Pour ça on peut ajouter l'argument -K à la commande : ansible-playbook -i inventory.yml playbook.yml -K .
On peut aussi le passer en variable via : ansible_become_password mais ce n'est pas une très bonne pratique de laisser des mots de passe Linux traîner dans des fichiers !!
Petit retex à propos du become à partir d'Ubuntu 25.10... Durant l'écriture de cet article, j'ai utilisé un Ubuntu et impossible de passer sudo depuis Ansible. J'avais une erreur qui m'a fait littéralement rager devant l'ordi :
FAILED! => {"msg": "Timeout (12s) waiting for privilege escalation prompt: "}
En fait, cette erreur, si elle t'arrive, est due à sudo qui est passé depuis Ubuntu 25.10 à sudo-rs écrit en Rust. (Ce changement vise à renforcer la sécurité : le sudo historique a d'ailleurs fait l'objet en 2025 d'une CVE critique, la CVE-2025-32463.) Et c'est cette subtilité qui m'a fait rager. (Coucou @Cédric)
Alors voici la solution à cette galère.
On vérifie sur le serveur la version de sudo : /usr/bin/sudo --version. Si la sortie est sudo-rs, alors c'est bien la version écrite en Rust et on est bien sur ce problème.
Ajoute simplement sur l'host dans le inventory.yml la variable ansible_become_exe: sudo.ws pour indiquer à Ansible d'utiliser le sudo classique (écrit en C) plutôt que sudo-rs quand become est appelé. Les versions récentes d'ansible-core gèrent désormais sudo-rs nativement.
web:
hosts:
web01:
ansible_host: ipduserveur
ansible_user: root
ansible_port: 22
ansible_ssh_private_key_file: ~/.ssh/id_ansible
ansible_ssh_common_args: "-o StrictHostKeyChecking=no"
ansible_become_exe: sudo.ws
5. Les tags
Les tags nous permettent de mieux organiser notre playbook et pouvoir aussi exécuter uniquement certaines tâches plutôt que l'ensemble :
- name: Premier Playbook
hosts: all
vars:
server_name:
- obiwan
- anakin
- quigon
tasks:
- name: "Affichage d'un message"
ansible.builtin.debug:
msg: "Hello world ! [{{ server_name }}]"
when: ansible_facts["distribution"] == "Debian"
tags: world
- name: "On s'amuse !!!"
ansible.builtin.debug:
msg: "Hello {{ item }}"
loop: "{{ server_name }}"
tags: persons
Ici on a ajouté deux tags : world et persons.
Si je veux exécuter le play uniquement avec le tag world : ansible-playbook -i inventory.yml playbook.yml --tags world .
Ça me permet d'avoir un fichier playbook avec des tâches généralistes, et pouvoir en exécuter certaines plutôt que d'autres sans réécrire le fichier.
6. Les rôles
On le voit avec les tags, c'est bien quand le play en contient une vingtaine.
Quand on commence à voir grossir le fichier, il est temps de le ranger. Un peu comme dans du code, on va refactoriser tout ça :
Si mon playbook installe nginx et apache2 :
---
- name: Installation et configuration de Nginx et Apache2
hosts: web
become: true
tasks:
- name: Installer Nginx et Apache2
ansible.builtin.apt:
name:
- nginx
- apache2
state: present
update_cache: true
- name: Copier la configuration nginx.conf
ansible.builtin.copy:
src: files/nginx.conf
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: '0644'
validate: 'nginx -t -c %s'
notify: Restart Nginx
- name: Activer et démarrer Nginx
ansible.builtin.systemd:
name: nginx
enabled: true
state: started
- name: Activer et démarrer Apache2
ansible.builtin.systemd:
name: apache2
enabled: true
state: started
handlers:
- name: Restart Nginx
ansible.builtin.systemd:
name: nginx
state: restarted
On voit qu'on a 5 tâches, on sent que c'est facile d'avoir un playbook.yml à rallonge très (trop) facilement si on n'a pas de rigueur.
6.1 La galaxy
Pour ça, Ansible peut nous aider avec cette commande : ansible-galaxy init web-server.
Cette commande nous permet d'avoir un dossier préconfiguré pour y ranger tout ce qui concerne le sujet de notre choix (ici nginx et apache2).

Je vais placer mes tâches de mon playbook dans web-server/tasks/main.yml :
---
- name: Installer Nginx et Apache2
ansible.builtin.apt:
name:
- nginx
- apache2
state: present
update_cache: true
- name: Copier la configuration nginx.conf
ansible.builtin.copy:
src: files/nginx.conf
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: '0644'
validate: 'nginx -t -c %s'
notify: Restart Nginx
- name: Activer et démarrer Nginx
ansible.builtin.systemd:
name: nginx
enabled: true
state: started
- name: Activer et démarrer Apache2
ansible.builtin.systemd:
name: apache2
enabled: true
state: started
Et le handler il est où ? Dans le fichier web-server/handlers/main.yml :
---
- name: Restart Nginx
ansible.builtin.systemd:
name: nginx
state: restarted
Tu auras remarqué que dans les fichiers, on ne précise plus tasks: ou handlers: car les fichiers les rendent implicites.
Mais du coup, mon playbook comment il sait quelle tâche lancer ?
Chaque dossier représente un rôle, on va l'inclure dans le playbook :
---
- name: Installation et configuration de Nginx et Apache2
hosts: web
roles:
- web-server
Et la commande pour lancer reste la même. Un playbook plus court, plus simple à lire.
Pour encore organiser et gagner plus de place, import_tasks nous permet de conserver la propagation des tags lors du lancement du playbook et ainsi gagner aussi de la place dans le main.yml:
- name: Importer les tâches Nginx
ansible.builtin.import_tasks: tasks/nginx.yml
- name: Importer les tâches Apache
ansible.builtin.import_tasks: tasks/apache.yml
ansible-galaxy init nous crée beaucoup de dossiers, mais si on n'en utilise que quelques-uns, libre à nous de supprimer les dossiers vides.
6.2 Les variables et la priorité
La gestion des variables dans Ansible est un peu plus complexe, car toutes les variables ne possèdent pas la même priorité.
Dans un rôle, le répertoire defaults/ permet de définir des valeurs par défaut que l'utilisateur pourra facilement personnaliser. C'est l'endroit idéal pour exposer les paramètres configurables du rôle, comme le port d'écoute de Nginx ou le répertoire racine du site web.
À l'inverse, le répertoire vars/ permet de définir des variables dont la priorité est supérieure. On y placera plutôt des variables internes au rôle, que l'on souhaite moins facilement remplacer.
Par exemple, une variable définie dans roles/web-server/vars/main.yml aura la priorité sur une variable portant le même nom définie dans roles/web-server/defaults/main.yml.
On peut rapprocher ce fonctionnement de celui des fichiers .env et .env.local, dans lesquels certaines valeurs peuvent être remplacées par d'autres. Il faut néanmoins garder à l'esprit qu'Ansible possède son propre mécanisme de priorité, plus complexe.
Privilégie defaults/main.yml pour les paramètres que l'utilisateur doit pouvoir personnaliser facilement et réserve vars/main.yml aux variables internes qui nécessitent une priorité plus élevée.
7. Les collections
Avec ansible.builtin.xxx on se servait de ce qu'Ansible connaissait comme modules. Mais on peut aussi s'aider des modules que la communauté nous propose.
Par exemple moi pour mes serveurs, j'aime bien utiliser ufw comme surcouche de pare-feu.
Mais Ansible ne propose pas nativement cette gestion. Eh bien la communauté d'Ansible a résolu le problème en créant ce module. Je ne me gêne pas pour l'utiliser !
Pour ça il faut d'abord installer la collection qui possède ufw. On va la chercher : ansible-doc -l | grep ufw :

On installe la collection community.general à l'aide de cette commande :
ansible-galaxy collection install community.general
Ensuite je peux créer un play utilisant ufw :
---
- name: Configurer le pare-feu
hosts: web
become: true
tasks:
- name: Installation de UFW
ansible.builtin.apt:
name: ufw
state: present
- name: Police de sécurité entrante générale
community.general.ufw:
direction: incoming
default: deny
- name: Police de sécurité sortante générale
community.general.ufw:
direction: outgoing
default: allow
- name: Activer UFW
community.general.ufw:
state: enabled
On en a terminé pour ce tour d'horizon d'Ansible. Je n'ai fait que survoler les possibilités dingues de ce tool. Je ne saurais trop te conseiller d'aller éplucher la doc, c'est une vraie pépite !
Et surtout souviens-toi... treat your servers like cattle, not pets.