
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.