Aller au contenu
Infrastructure as Code medium

Cycle TDD complet : développer un rôle users en TDD avec Molecule

11 min de lecture

Logo Ansible

Ce guide vous fait écrire un rôle Ansible users en TDD complet — chaque feature commence par un test qui échoue, puis le code minimal pour le faire passer, puis un refactor. Le rôle créera des utilisateurs Linux avec shell custom et appartenance à des groupes.

À la fin, vous aurez expérimenté le cycle red-green-refactor sur Ansible et compris pourquoi cette discipline change votre pratique au long terme.

  • Écrire verify.yml AVANT tasks/main.yml (TDD pur).
  • Voir 3 cycles red-green-refactor consécutifs.
  • Implémenter le rôle users qui supporte shell:, groups:, default fallback.
  • Refactorer sans peur grâce à la couverture de tests.
  • Combiner avec argument_specs.yml pour valider les entrées.

Le rôle doit :

  1. Créer une liste d’utilisateurs Linux (variable users_to_create).
  2. Chaque utilisateur a optionnellement un shell: (default = /bin/bash).
  3. Chaque utilisateur peut appartenir à des groups: (liste).
  4. Si pas de shell explicite, fallback sur users_default_shell.
Fenêtre de terminal
mkdir -p roles/users
cd roles/users
ansible-galaxy role init . --offline
molecule init scenario

Cycle 1 — Créer les utilisateurs (RED → GREEN → REFACTOR)

Section intitulée « Cycle 1 — Créer les utilisateurs (RED → GREEN → REFACTOR) »
molecule/default/verify.yml
---
- name: Verify
hosts: all
become: true
tasks:
- name: Vérifier que alice existe
ansible.builtin.user:
name: alice
state: present
check_mode: true
register: alice_check
failed_when: alice_check is changed
- name: Vérifier le shell d'alice
ansible.builtin.command: getent passwd alice
register: alice_passwd
changed_when: false
- name: Assertion shell d'alice = /bin/zsh
ansible.builtin.assert:
that:
- "'/bin/zsh' in alice_passwd.stdout"
fail_msg: "alice doit avoir /bin/zsh"

Le test décrit : « alice doit exister et avoir /bin/zsh ». Ce test échoue car le rôle est vide.

molecule/default/converge.yml
---
- name: Converge
hosts: all
become: true
vars:
users_to_create:
- name: alice
shell: /bin/zsh
groups:
- wheel
- name: bob
- name: carol
roles:
- role: users
Fenêtre de terminal
molecule converge # n'arrive pas — pas de tasks à jouer
molecule verify # ROUGE : "alice doit exister"
# tasks/main.yml — minimum vital pour passer le test 1
---
- name: Créer les utilisateurs
ansible.builtin.user:
name: "{{ item.name }}"
state: present
loop: "{{ users_to_create }}"
Fenêtre de terminal
molecule converge # crée alice (et bob, carol)
molecule verify # ROUGE — alice existe mais pas de shell
- name: Créer les utilisateurs
ansible.builtin.user:
name: "{{ item.name }}"
shell: "{{ item.shell | default('/bin/bash') }}"
state: present
loop: "{{ users_to_create }}"
Fenêtre de terminal
molecule converge # change le shell d'alice
molecule verify # VERT
# verify.yml (suite)
- name: Vérifier alice membre de wheel
ansible.builtin.command: id alice
register: alice_id
changed_when: false
- name: Assertion alice in wheel
ansible.builtin.assert:
that:
- "'wheel' in alice_id.stdout"
fail_msg: "alice doit être dans wheel"
Fenêtre de terminal
molecule verify # ROUGE — alice n'est pas dans wheel
- name: Créer les utilisateurs
ansible.builtin.user:
name: "{{ item.name }}"
shell: "{{ item.shell | default('/bin/bash') }}"
groups: "{{ item.groups | default([]) }}"
append: true
state: present
loop: "{{ users_to_create }}"

append: true est critique : sans lui, groups: [wheel] remplacerait les groupes existants de l’utilisateur. Avec append: true, on ajoute.

Fenêtre de terminal
molecule converge # alice rejoint wheel
molecule verify # VERT
- name: Vérifier carol shell = /bin/bash (default)
ansible.builtin.command: getent passwd carol
register: carol_passwd
changed_when: false
- name: Assertion carol shell = /bin/bash
ansible.builtin.assert:
that:
- "'/bin/bash' in carol_passwd.stdout"

carol n’a pas de shell: dans users_to_create. Le test passe déjà car notre default('/bin/bash') fonctionne — mais le hard-coding /bin/bash n’est pas idéal.

defaults/main.yml
users_default_shell: /bin/bash
users_create_home: true
users_to_create: []
# tasks/main.yml refactoré
- name: Créer les utilisateurs
ansible.builtin.user:
name: "{{ item.name }}"
shell: "{{ item.shell | default(users_default_shell) }}"
groups: "{{ item.groups | default([]) }}"
append: true
create_home: "{{ users_create_home }}"
state: present
loop: "{{ users_to_create }}"
loop_control:
label: "{{ item.name }}"
Fenêtre de terminal
molecule verify # VERT — refactor n'a rien cassé

C’est le bénéfice du TDD : on refactor avec confiance car les tests garantissent qu’on n’a pas cassé le comportement.

Ajouter la validation des entrées une fois le code stable :

meta/argument_specs.yml
argument_specs:
main:
options:
users_to_create:
type: list
elements: dict
options:
name:
type: str
required: true
shell:
type: str
choices:
- /bin/bash
- /bin/zsh
- /bin/sh
- /sbin/nologin
groups:
type: list
elements: str
default: []
users_default_shell:
type: str
default: /bin/bash

Re-tester :

Fenêtre de terminal
molecule verify # toujours VERT

Maintenant, si un user du rôle passe shell: /bin/csh, Ansible refuse d’exécuter — pas de demi-déploiement.

Cette page a un lab d’accompagnement : labs/molecule/tdd-cycle/ dans stephrobert/ansible-training.

Le lab fournit le rôle users complet avec 4 assertions dans verify.yml (alice exists, alice shell, alice wheel, carol default shell). 6 tests structure validés.

Fenêtre de terminal
cd ~/Projets/ansible-training/labs/molecule/tdd-cycle/
cat roles/users/tasks/main.yml
cat molecule/default/verify.yml
pytest -v challenge/tests/

Pour lancer Molecule réellement :

Fenêtre de terminal
molecule test # cycle complet TDD
  • TDD = un test rouge avant chaque ligne de code.
  • 3 cycles minimum par feature (RED, GREEN, REFACTOR).
  • append: true mandatory sur groups: pour ne pas écraser les autres groupes.
  • default(users_default_shell) pour fallback sur une valeur configurable.
  • Refactor sans peur grâce aux tests existants.
  • argument_specs.yml vient après la stabilité, pour verrouiller l’API publique.

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