
Un style guide Ansible n’est pas du purisme — c’est ce qui rend un projet relisible six mois plus tard et reviewable par un collègue. Cette page synthétise les conventions imposées en 2026 sur le code Ansible : FQCN obligatoire, tâches toujours nommées, modules command/shell à proscrire en priorité, tags orthogonaux, variables préfixées. Toutes ces règles sont vérifiables automatiquement via ansible-lint que vous intégrerez dans votre CI.
Le style guide ci-dessous est compatible RHCE EX294 et avec les profils ansible-lint modernes (production, safety, min). Adopter ce référentiel dès vos premiers playbooks vous épargne la dette technique d’une refacto massive plus tard.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Les conventions de nommage des tâches, plays, variables, tags ;
- L’usage du FQCN (Fully Qualified Collection Name) — obligatoire RHCE et recommandé ailleurs ;
- Les modules à éviter (
command,shell) et quand ils sont quand même légitimes ; - Les commentaires utiles vs inutiles dans un playbook ;
- L’usage d’
ansible-linten CI avec les profilsproductionetsafety.
Conventions de nommage
Section intitulée « Conventions de nommage »Chaque play a un name: qui décrit l’objectif business, pas la liste des modules :
# ❌ Trop technique, paraphrase les modules- name: Run dnf install nginx and systemd start hosts: webservers
# ✅ Décrit l'objectif business- name: Déployer nginx sur les serveurs web hosts: webserversLe name: apparaît dans le PLAY RECAP et dans les sorties — il doit aider à diagnostiquer en lecture rapide.
Chaque tâche a un name: obligatoire. C’est l’une des règles strictes d’ansible-lint (name[missing]) :
# ❌ Pas de name → ansible-lint refuse- ansible.builtin.dnf: name: nginx state: present
# ✅ Name explicite- name: Installer nginx ansible.builtin.dnf: name: nginx state: presentVerbe à l’infinitif, capitale en début, pas de point final. Le name: apparaît dans la sortie : TASK [Installer nginx] *******.
Variables
Section intitulée « Variables »Les variables sont préfixées par le rôle ou le composant — évite les collisions sur une équipe :
# ❌ Variables génériques, conflits garantis entre rôlesport: 80user: ansibleversion: "1.26"
# ✅ Préfixées par le rôle/composantnginx_port: 80nginx_user: ansiblenginx_version: "1.26"pg_port: 5432pg_user: postgresSnake_case pour les variables (jamais kebab-case ni camelCase, c’est la convention Ansible).
Tags orthogonaux et descriptifs. Le rôle d’un tag est de pouvoir cibler un sous-ensemble de tâches via --tags ou les ignorer via --skip-tags :
# ❌ Tags non descriptifs, redondants avec le nametags: - tag1 - step2
# ✅ Tags descriptifs et orthogonauxtags: - nginx # service concerné - firewall # type d'opération - configuration # phaseConvention : nom de service, type d’opération, phase. Pas de pluriel (nginx pas nginxs).
FQCN : obligatoire en 2026
Section intitulée « FQCN : obligatoire en 2026 »Le FQCN (Fully Qualified Collection Name) prend la forme <namespace>.<collection>.<module> :
| Forme courte | FQCN |
|---|---|
dnf | ansible.builtin.dnf |
systemd | ansible.builtin.systemd |
firewalld | ansible.posix.firewalld |
selinux | ansible.posix.selinux |
timezone | community.general.timezone |
Le FQCN est obligatoire :
- Pour la RHCE EX294 depuis 2024 — perte de points si la forme courte est utilisée.
- Pour
ansible-lintprofilproduction(fqcn[action-core]). - Pour éviter les collisions : deux collections peuvent fournir un module du même nom court.
# ❌ Forme courte, accepte uniquement ansible.builtin.* et écarte les autres- name: Ouvrir SSH dans firewalld firewalld: # ← ambiguë, peut casser si ansible.posix vs autre service: ssh state: enabled
# ✅ FQCN explicite- name: Ouvrir SSH dans firewalld ansible.posix.firewalld: service: ssh state: enabledModules command et shell : derniers recours
Section intitulée « Modules command et shell : derniers recours »Les modules command et shell sont non-idempotents par défaut et constituent un anti-pattern dès qu’un module dédié existe. Avant d’écrire un shell:, posez-vous trois questions :
- Existe-t-il un module dédié ?
dnf,service,user,file,copy,lineinfile,template,git,unarchive. Privilégiez-les systématiquement. - Si vous insistez sur
shell: avez-vous mis uncreates:ouremoves:pour le rendre idempotent ? - Avez-vous justifié dans un commentaire pourquoi
shellest nécessaire ?
# ❌ shell sans garde, non-idempotent- name: Initialiser la base ansible.builtin.shell: pg_ctl initdb -D /var/lib/pgsql/data
# ✅ shell avec creates: (idempotent)- name: Initialiser la base si pas déjà fait ansible.builtin.shell: pg_ctl initdb -D /var/lib/pgsql/data args: creates: /var/lib/pgsql/data/PG_VERSIONansible-lint détecte les shell: sans garde via la règle command-instead-of-shell et no-changed-when.
Commentaires : pourquoi, pas quoi
Section intitulée « Commentaires : pourquoi, pas quoi »Les commentaires Ansible (lignes commençant par #) servent à expliquer le pourquoi d’une décision non-évidente, pas à paraphraser le code :
# ❌ Commentaire qui paraphrase le code- name: Installer nginx ansible.builtin.dnf: name: nginx # On installe nginx state: present # avec l'état présent
# ✅ Commentaire qui explique le pourquoi- name: Installer nginx (version pinned car 1.27 casse notre conf TLS) ansible.builtin.dnf: name: "nginx-1.26.*" state: presentPour un fichier de config qui sera lu par les futurs collègues : commentaire d’en-tête qui explique le rôle global du playbook, et c’est tout.
Indentation et formatage
Section intitulée « Indentation et formatage »- 2 espaces d’indentation (jamais de tab) — convention Ansible.
---au début de chaque fichier YAML.- Une ligne vide entre les tâches d’un même play pour aérer.
- Quotes systématiques sur les valeurs ambiguës (cf. YAML pour Ansible).
L’outil de garde-fou : ansible-lint
Section intitulée « L’outil de garde-fou : ansible-lint »ansible-lint est l’outil de référence pour faire respecter le style guide automatiquement. Configuration via .ansible-lint à la racine du projet :
---profile: production # production / safety / min / shared / nullexclude_paths: - .ansible/ - tests/
skip_list: - meta-runtime # règles que vous décidez de skipper
warn_list: - experimental # règles en warning plutôt qu'en erreurLes profils disponibles :
| Profil | Strictness | Quand l’utiliser |
|---|---|---|
min | Permissif | Démarrage rapide, projets legacy |
basic | Modéré | Projets en cours de refacto |
moderate | Standard | Projets matures |
safety | Strict (sécurité) | Audit sécurité avant prod |
production | Très strict | Production attendue à terme |
shared | Pour collections publiques | Galaxy, Automation Hub |
Lancer ansible-lint
Section intitulée « Lancer ansible-lint »ansible-lint playbooks/site.ymlansible-lint --profile production playbooks/Sortie typique :
WARNING Listing 3 violation(s) that are fatalfqcn[action-core]: Use FQCN for builtin module actions (dnf).playbooks/site.yml:8 Task/Handler: Installer nginx
name[missing]: All tasks should be named.playbooks/site.yml:15 Task/Handler: ansible.builtin.systemd
no-changed-when: Commands should not change things if nothing needs doing.playbooks/site.yml:22 Task/Handler: Initialiser la baseChaque violation pointe la ligne et la règle — fix progressif.
Intégration en CI
Section intitulée « Intégration en CI »Dans une CI GitHub Actions ou GitLab CI :
on: [push, pull_request]jobs: lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: "3.12" - run: pip install ansible-lint==26.1.1 - run: ansible-lint --profile production playbooks/Bloque tout merge qui introduit des violations — la dette technique reste contenue.
Anti-patterns récurrents
Section intitulée « Anti-patterns récurrents »| Anti-pattern | Pourquoi c’est faux | Fix |
|---|---|---|
Tâche sans name: | Sortie illisible (PLAY RECAP affiche [ansible.builtin.dnf]) | Ajouter un name: qui décrit l’objectif |
Forme courte de module (dnf:) | Ambigu, incompatible RHCE | Utiliser le FQCN (ansible.builtin.dnf) |
shell: sans creates: ni removes: | Non-idempotent, changed à chaque run | Utiliser un module dédié, ou ajouter une garde |
Variables génériques (port, user) | Conflits entre rôles | Préfixer (nginx_port, pg_user) |
Secrets en clair dans host_vars/ | Visibles dans git log | ansible-vault ou manager externe |
Tags step1, step2 | Non descriptif | Tags orthogonaux : service + opération + phase |
command: qui appelle un module Python (pip install) | Bypass du module idempotent | Utiliser le module dédié (ansible.builtin.pip) |
ignore_errors: true partout | Masque les vraies erreurs | Justifier par un commentaire ; préférer failed_when: |
À retenir
Section intitulée « À retenir »name:obligatoire sur chaque tâche — décrit l’objectif business, pas le module utilisé.- FQCN obligatoire depuis 2024 (RHCE EX294 + ansible-lint production).
- Variables préfixées par le rôle/composant pour éviter les collisions.
- Modules
command/shellen dernier recours — toujours préférer un module dédié. - Commentaires expliquent le pourquoi, pas le quoi (le code est déjà documentation).
ansible-linten CI : profilproduction, blocage des violations sur PR.