Aller au contenu
Infrastructure as Code medium

Style guide Ansible : conventions de nommage, FQCN, ansible-lint

11 min de lecture

Logo Ansible

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.

  • 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-lint en CI avec les profils production et safety.

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: webservers

Le 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: present

Verbe à l’infinitif, capitale en début, pas de point final. Le name: apparaît dans la sortie : TASK [Installer nginx] *******.

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ôles
port: 80
user: ansible
version: "1.26"
# ✅ Préfixées par le rôle/composant
nginx_port: 80
nginx_user: ansible
nginx_version: "1.26"
pg_port: 5432
pg_user: postgres

Snake_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 name
tags:
- tag1
- step2
# ✅ Tags descriptifs et orthogonaux
tags:
- nginx # service concerné
- firewall # type d'opération
- configuration # phase

Convention : nom de service, type d’opération, phase. Pas de pluriel (nginx pas nginxs).

Le FQCN (Fully Qualified Collection Name) prend la forme <namespace>.<collection>.<module> :

Forme courteFQCN
dnfansible.builtin.dnf
systemdansible.builtin.systemd
firewalldansible.posix.firewalld
selinuxansible.posix.selinux
timezonecommunity.general.timezone

Le FQCN est obligatoire :

  • Pour la RHCE EX294 depuis 2024 — perte de points si la forme courte est utilisée.
  • Pour ansible-lint profil production (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: enabled

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 :

  1. Existe-t-il un module dédié ? dnf, service, user, file, copy, lineinfile, template, git, unarchive. Privilégiez-les systématiquement.
  2. Si vous insistez sur shell : avez-vous mis un creates: ou removes: pour le rendre idempotent ?
  3. Avez-vous justifié dans un commentaire pourquoi shell est 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_VERSION

ansible-lint détecte les shell: sans garde via la règle command-instead-of-shell et no-changed-when.

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: present

Pour 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.

  • 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).

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 / null
exclude_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 erreur

Les profils disponibles :

ProfilStrictnessQuand l’utiliser
minPermissifDémarrage rapide, projets legacy
basicModéréProjets en cours de refacto
moderateStandardProjets matures
safetyStrict (sécurité)Audit sécurité avant prod
productionTrès strictProduction attendue à terme
sharedPour collections publiquesGalaxy, Automation Hub
Fenêtre de terminal
ansible-lint playbooks/site.yml
ansible-lint --profile production playbooks/

Sortie typique :

WARNING Listing 3 violation(s) that are fatal
fqcn[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 base

Chaque violation pointe la ligne et la règle — fix progressif.

Dans une CI GitHub Actions ou GitLab CI :

.github/workflows/ansible-lint.yml
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-patternPourquoi c’est fauxFix
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 RHCEUtiliser le FQCN (ansible.builtin.dnf)
shell: sans creates: ni removes:Non-idempotent, changed à chaque runUtiliser un module dédié, ou ajouter une garde
Variables génériques (port, user)Conflits entre rôlesPréfixer (nginx_port, pg_user)
Secrets en clair dans host_vars/Visibles dans git logansible-vault ou manager externe
Tags step1, step2Non descriptifTags orthogonaux : service + opération + phase
command: qui appelle un module Python (pip install)Bypass du module idempotentUtiliser le module dédié (ansible.builtin.pip)
ignore_errors: true partoutMasque les vraies erreursJustifier par un commentaire ; préférer failed_when:
  • 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/shell en dernier recours — toujours préférer un module dédié.
  • Commentaires expliquent le pourquoi, pas le quoi (le code est déjà documentation).
  • ansible-lint en CI : profil production, blocage des violations sur PR.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn