Ansible Intermédiaire : variables, templates et rôles
🎯 Objectifs
- Utiliser les variables pour rendre les playbooks flexibles
- Créer des fichiers de configuration dynamiques avec les templates Jinja2
- Déclencher des actions conditionnelles avec les handlers
- Organiser son code Ansible avec la structure des rôles
📋 Prérequis
- Ansible Débutant - inventaire, playbook de base, modules essentiels
- Ansible installé et fonctionnel
- SSH configuré vers au moins un serveur
📦 Les Variables
Pourquoi des variables ?
Sans variables, si tu veux changer le port d'écoute de Nginx, tu dois modifier le playbook à la main à chaque fois. Avec des variables, tu changes une seule valeur, et tout le playbook s'adapte.
Déclarer des variables dans un playbook
---
- name: Déployer une application
hosts: webservers
become: true
vars:
app_port: 8080
app_user: deploy
app_dir: /var/www/monapp
tasks:
- name: Créer le répertoire de l'application
ansible.builtin.file:
path: "{{ app_dir }}"
state: directory
owner: "{{ app_user }}"
mode: '0755'💡 Les variables s'utilisent avec la syntaxe
{{ nom_variable }}. Les accolades doubles viennent de Jinja2, le moteur de templates intégré à Ansible.
Fichiers de variables séparés
Pour ne pas mélanger variables et tâches, crée des fichiers dédiés :
# Arborescence suggérée
playbook.yml
inventaire.ini
vars/
all.yml # Variables communes à tous les serveurs
production.yml # Variables spécifiques à la production# vars/all.yml
app_name: monapp
app_port: 8080
nginx_worker_processes: 2# playbook.yml
- name: Configurer les serveurs
hosts: all
vars_files:
- vars/all.yml
tasks:
- name: Afficher le nom de l'application
ansible.builtin.debug:
msg: "Déploiement de {{ app_name }} sur le port {{ app_port }}"Variables d'inventaire (host_vars et group_vars)
Ansible suit des conventions pour charger les variables automatiquement :
inventaire.ini
group_vars/
all.yml # Variables pour tous les hôtes
webservers.yml # Variables pour le groupe webservers
host_vars/
web1.yml # Variables spécifiques à web1
web2.yml # Variables spécifiques à web2# group_vars/webservers.yml
nginx_port: 80
nginx_worker_processes: 4
# host_vars/web1.yml
nginx_port: 8080 # web1 utilise un port différentVariables depuis la ligne de commande
# Passer une variable au moment du lancement
ansible-playbook playbook.yml -e "app_version=2.1.0"
# Passer un fichier de variables
ansible-playbook playbook.yml -e "@vars/custom.yml"Priorité des variables (de la plus faible à la plus forte)
| Source | Priorité |
|---|---|
Valeurs par défaut des rôles (defaults/main.yml) | La plus basse |
Variables d'inventaire (group_vars, host_vars) | Moyenne |
Variables du playbook (vars:) | Haute |
Variables en ligne de commande (-e) | La plus haute |
🧱 Les Facts : informations automatiques sur les serveurs
Ansible collecte automatiquement des informations sur chaque serveur au début d'un play. Ces informations s'appellent des facts.
- name: Utiliser les facts
hosts: all
tasks:
- name: Afficher le système d'exploitation
ansible.builtin.debug:
msg: "Ce serveur tourne sur {{ ansible_distribution }} {{ ansible_distribution_version }}"
- name: Afficher la mémoire disponible
ansible.builtin.debug:
msg: "Mémoire RAM : {{ ansible_memtotal_mb }} Mo"Les facts les plus utiles :
| Fact | Valeur typique |
|---|---|
ansible_hostname | web1 |
ansible_distribution | Ubuntu |
ansible_distribution_version | 22.04 |
ansible_os_family | Debian |
ansible_default_ipv4.address | 192.168.1.10 |
ansible_memtotal_mb | 2048 |
# Voir tous les facts d'un hôte
ansible web1 -i inventaire.ini -m ansible.builtin.setup📝 Les Templates Jinja2
Le problème des fichiers statiques
Avec ansible.builtin.copy, tu copies un fichier à l'identique. Mais si tu veux que le fichier de configuration de Nginx contienne le bon nom de domaine, la bonne adresse IP, le bon port... selon chaque serveur ? Il te faut un fichier dynamique.
La solution : le module ansible.builtin.template
Un template Jinja2, c'est un fichier texte avec des variables à l'intérieur. Ansible remplace les variables par leurs valeurs avant de copier le fichier sur le serveur.
# templates/nginx.conf.j2
server {
listen {{ nginx_port }};
server_name {{ ansible_hostname }};
root /var/www/{{ app_name }};
index index.html;
access_log /var/log/nginx/{{ app_name }}_access.log;
error_log /var/log/nginx/{{ app_name }}_error.log;
}# Dans le playbook
- name: Déployer la configuration Nginx
ansible.builtin.template:
src: templates/nginx.conf.j2
dest: /etc/nginx/sites-available/{{ app_name }}
owner: root
group: root
mode: '0644'Fonctionnalités Jinja2 utiles
{# Commentaire Jinja2 #}
{# Condition #}
{% if nginx_port == 443 %}
ssl on;
{% else %}
ssl off;
{% endif %}
{# Boucle #}
{% for server in upstream_servers %}
server {{ server }}:8080;
{% endfor %}
{# Filtre #}
{{ app_name | upper }} {# Mettre en majuscules #}
{{ app_name | default('app') }} {# Valeur par défaut si variable non définie #}
{{ liste | join(', ') }} {# Joindre une liste #}🔔 Les Handlers
Le problème sans handlers
Quand tu changes la configuration de Nginx, tu dois le recharger. Tu pourrais ajouter une tâche service: state: reloaded après chaque modification de config. Mais si la config n'a pas changé, tu rechargeras quand même Nginx - inutilement.
La solution : les handlers ne se déclenchent que si une tâche les a notifiés, et seulement si cette tâche a effectivement changé quelque chose.
Définir et utiliser un handler
---
- name: Configurer Nginx
hosts: webservers
become: true
tasks:
- name: Déployer la configuration Nginx
ansible.builtin.template:
src: templates/nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: Recharger Nginx # Notifie le handler
- name: Activer le site
ansible.builtin.file:
src: /etc/nginx/sites-available/monapp
dest: /etc/nginx/sites-enabled/monapp
state: link
notify: Recharger Nginx # Le même handler peut être notifié plusieurs fois
handlers:
- name: Recharger Nginx # Le nom doit correspondre exactement
ansible.builtin.service:
name: nginx
state: reloaded💡 Un handler est exécuté une seule fois à la fin du play, même s'il a été notifié plusieurs fois. C'est parfait pour éviter des rechargements inutiles.
Forcer l'exécution des handlers immédiatement
- name: Forcer les handlers maintenant
ansible.builtin.meta: flush_handlers🗂️ Les Rôles : organiser son code
Pourquoi les rôles ?
Un playbook avec 50 tâches, des templates, des variables et des handlers dans un seul fichier devient difficile à maintenir. Les rôles sont la façon Ansible d'organiser et de réutiliser le code.
L'analogie : un rôle, c'est comme un composant d'une application. Tu crées un rôle nginx, un rôle postgresql, un rôle app. Chacun est autonome et réutilisable dans différents projets.
Structure d'un rôle
roles/
nginx/
tasks/
main.yml # Les tâches du rôle
handlers/
main.yml # Les handlers
templates/
nginx.conf.j2 # Les templates
files/
index.html # Les fichiers statiques
vars/
main.yml # Variables internes (priorité haute)
defaults/
main.yml # Valeurs par défaut (priorité basse)
meta/
main.yml # Métadonnées et dépendances# roles/nginx/tasks/main.yml
---
- name: Installer Nginx
ansible.builtin.apt:
name: nginx
state: present
update_cache: true
- name: Déployer la configuration
ansible.builtin.template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: Recharger Nginx
- name: Démarrer Nginx
ansible.builtin.service:
name: nginx
state: started
enabled: true# roles/nginx/defaults/main.yml
---
nginx_port: 80
nginx_worker_processes: 1Utiliser un rôle dans un playbook
---
- name: Configurer les serveurs web
hosts: webservers
become: true
roles:
- nginx
- { role: nginx, nginx_port: 8080 } # Surcharger une variableCréer rapidement la structure d'un rôle
ansible-galaxy role init roles/nginxCette commande crée toute l'arborescence vide pour toi.
⚠️ Erreurs Courantes
| Erreur | Cause | Solution |
|---|---|---|
undefined variable | Variable non déclarée | Vérifier defaults/main.yml ou utiliser \| default('valeur') |
| Handler non déclenché | Nom du notify différent du name du handler | Les noms doivent être identiques |
| Template non trouvé | Chemin incorrect dans src | Le chemin est relatif au répertoire templates/ du rôle |
| Facts non disponibles | gather_facts: false dans le play | Retirer cette ligne ou activer les facts explicitement |
| YAML parsing error | Guillemets manquants autour de {{ }} | Toujours entourer "{{ variable }}" de guillemets |
📊 Récapitulatif
| Concept | Utilisation | Fichier |
|---|---|---|
| vars | Valeurs réutilisables dans un playbook | vars/all.yml |
| group_vars | Variables par groupe d'hôtes | group_vars/webservers.yml |
| host_vars | Variables par hôte | host_vars/web1.yml |
| facts | Infos collectées automatiquement | Accès via ansible_* |
| template | Fichier dynamique avec variables | templates/*.j2 |
| handler | Action déclenchée sur notification | handlers/main.yml |
| rôle | Organisation modulaire du code | roles/nom_role/ |
📚 Ressources
🚀 Prochaines étapes
Tu sais maintenant structurer et paramétrer tes playbooks. Passe au niveau confirmé pour sécuriser et industrialiser ton usage d'Ansible :
- Ansible Avancé : Vault, inventaires dynamiques et CI/CD - Secrets, production et automatisation avancée