Aller au contenu
Infrastructure as Code medium

Aide-mémoire RHCE EX294, Ansible en une page (commandes, modules, pièges)

25 min de lecture

Logo Ansible

Cette page condense en une seule lecture tout ce qu'il faut avoir en tête le jour de l'EX294. Commandes ansible-*, modules essentiels par catégorie, ansible.cfg minimal, syntaxes Vault, FQCN, pièges YAML qui font perdre des points. À lire la veille de l'examen et à garder ouverte pendant les mocks (lab rhce/mock-ex294).

L'EX294 est performance-based : aucun QCM, vous écrivez des playbooks sur un environnement RHEL/AlmaLinux live. Le score se calcule sur des objectifs vérifiés automatiquement. Un playbook qui ne passe pas l'objectif = 0 sur cet objectif. Pas de point partiel.

Ce tableau se lit par la colonne de gauche : en examen, on part d'un besoin formulé dans l'énoncé, jamais d'un nom de commande. Les lignes ansible-vault et ansible-galaxy sont les plus vite oubliées, parce qu'elles ne servent qu'une ou deux fois sur les quatre heures. Les options --check, --limit et --tags ne rapportent aucun point par elles-mêmes, mais elles réduisent le temps de vérification d'une tâche déjà écrite : c'est là que se gagne la marge finale.

BesoinCommande
Tester la connexion Ansibleansible all -m ansible.builtin.ping
Lister inventaireansible-inventory --graph
Voir variables d'un hostansible-inventory --host db1.lab
Doc d'un moduleansible-doc ansible.builtin.copy
Lister tous les modules d'une collectionansible-doc -l community.general
Lancer un playbookansible-playbook -i inventory.yml site.yml
Mode dry-runansible-playbook --check --diff site.yml
Limiter à un hostansible-playbook --limit web1.lab site.yml
Filtrer par tagsansible-playbook --tags config site.yml
Vault encrypt fichieransible-vault encrypt secret.yml
Vault encrypt variable inlineansible-vault encrypt_string 'value' --name 'key'
Vault editansible-vault edit secret.yml
Vault rekeyansible-vault rekey secret.yml
Lancer playbook avec vaultansible-playbook --vault-password-file=.vault_pass site.yml
Inspecter config activeansible-config dump --only-changed
Lister tâches sans exécuteransible-playbook --list-tasks site.yml
Lister hosts ciblésansible-playbook --list-hosts site.yml
Lint qualité productionansible-lint --profile production site.yml
Installer collectionansible-galaxy collection install -r requirements.yml
Installer rôle Galaxyansible-galaxy role install geerlingguy.docker

Ce fichier se pose avant même la lecture complète des tâches. Chaque ligne supprime une perte de temps identifiée : host_key_checking = False évite les invites de validation de clé SSH sur des hôtes fraîchement provisionnés, become = True dispense d'écrire become: dans chaque play, et callback_result_format = yaml rend les erreurs lisibles au lieu d'une ligne JSON compacte. roles_path et collections_path pointent vers le répertoire de travail : sans eux, un rôle créé pour une tâche ne sera pas trouvé par le playbook de la tâche suivante.

[defaults]
inventory = inventory/hosts.yml
remote_user = ansible
host_key_checking = False
roles_path = ./roles
collections_path = ./collections
forks = 10
callback_result_format = yaml
deprecation_warnings = False
[privilege_escalation]
become = True
become_method = sudo
become_user = root
[ssh_connection]
pipelining = True

À placer dans le répertoire de travail au début de l'examen, précédence locale > home > /etc.

Les tâches de l'examen sont formulées en langage naturel : « assurez-vous que le service démarre au boot », « créez un utilisateur avec l'UID 2001 ». Le premier geste consiste à traduire cette phrase en module, et c'est là que le temps se perd quand l'inventaire mental est flou. Les tableaux qui suivent regroupent les modules par domaine et les donnent sous leur FQCN complet : écrire ansible.builtin.copy plutôt que copy supprime toute ambiguïté quand deux collections exposent un module de même nom.

Le choix se joue sur la portée de l'écriture : copy et template produisent le fichier entier, lineinfile et blockinfile modifient un fichier existant. Dès qu'un énoncé mentionne une valeur issue d'une variable, la réponse attendue est template avec un fichier .j2.

ModuleUsage type
ansible.builtin.copyDéposer un fichier (avec content: ou src:)
ansible.builtin.templateDéposer fichier paramétré Jinja2 (src: file.j2)
ansible.builtin.fileÉtat d'un fichier/dossier (state: directory|absent|touch)
ansible.builtin.lineinfileUne seule ligne dans un fichier (regexp: obligatoire)
ansible.builtin.blockinfileBloc de lignes (marker: configurable)
ansible.builtin.fetchRécupérer un fichier de la cible vers le control node
community.general.archive / ansible.builtin.unarchivetar.gz, zip

Sur un service, le point noté est presque toujours double : state: started pour l'exécution immédiate et enabled: true pour le démarrage au boot. Un énoncé qui précise « et au redémarrage » vérifie explicitement le second.

ModuleUsage
ansible.builtin.dnfRHEL/AlmaLinux paquets (name:, state: present|latest|absent)
ansible.builtin.systemd_serviceActiver + démarrer (state: started, enabled: true)
ansible.builtin.cronTâche planifiée

Le paramètre password: du module user attend un hash, jamais un mot de passe en clair : il se produit avec le filtre password_hash('sha512'). Attention aussi au FQCN, authorized_key vient de ansible.posix et sudoers de community.general, ce ne sont pas des modules ansible.builtin.

ModuleUsage
ansible.builtin.userCréer user (name:, uid:, group:, shell:, password:)
ansible.builtin.groupCréer group (name:, gid:)
ansible.posix.authorized_keyDéposer clé SSH (user:, key:)
community.general.sudoersRègle sudo dédiée

C'est la catégorie qui distingue l'EX294 d'un examen Ansible générique. Deux réflexes à garder : firewalld demande permanent: true et immediate: true ensemble, sinon la règle disparaît au redémarrage ou ne s'applique pas tout de suite ; les modules SELinux et LVM exigent des paquets Python côté cible, à installer dans une tâche préalable.

ModuleUsage
ansible.posix.firewalldpermanent: true, immediate: true
ansible.posix.sebooleanActiver un booléen SELinux (state: true, persistent: true)
ansible.builtin.sefcontext (community.general)Contexte SELinux
community.general.lvolCréer un LV
community.general.filesystemFormater (xfs, ext4)
ansible.posix.mountMonter + fstab

command et shell sont les seuls modules non idempotents de cette liste : ils rapportent changed à chaque passage et font échouer la vérification du second run, d'où le changed_when: false obligatoire sur toute lecture. assert sert à contrôler soi-même l'état obtenu avant la fin du temps imparti.

ModuleUsage
ansible.builtin.command / shellCommandes (changed_when: false pour lecture)
ansible.builtin.assertVérification d'une condition
ansible.builtin.failÉchec explicite
ansible.builtin.debugAffichage de variables (var: ou msg:)
ansible.builtin.uriHTTP request
ansible.builtin.wait_forAttendre un port / état

Ces sept formes ne s'improvisent pas sous chronomètre. Elles ont en commun d'être des syntaxes exactes : un guillemet ou un deux-points de travers, et la tâche ne s'exécute pas, ou s'exécute en produisant un état différent de celui attendu. Les relire la veille est plus rentable que de réviser un module de plus, car un module inconnu se retrouve avec ansible-doc pendant l'épreuve, alors qu'une erreur de syntaxe silencieuse ne se voit qu'à la correction.

Sans guillemets, la valeur est laissée à l'interprétation du parseur YAML, et un mode: 644 écrit sans zéro initial part en décimal vers des permissions incohérentes, sans le moindre message d'erreur. Quoter supprime cette dépendance : "0644" désigne toujours le même mode.

mode: "0644" # ← TOUJOURS quoté : un 644 sans zéro donnerait 0o1204

Deux niveaux coexistent et le plus proche l'emporte : un become_user posé sur une tâche prend le pas sur le become du play. C'est la façon d'écrire un fichier au nom d'un compte applicatif dans un play qui tourne par ailleurs en root ; sans become: true sur la tâche, become_user reste sans effet.

- name: Déployer la configuration applicative
hosts: db1.lab
become: true # ← niveau play : toutes les tâches en sudo
tasks:
- name: Poser le fichier de configuration au nom du compte applicatif
ansible.builtin.copy:
src: app.cfg
dest: /opt/app/app.cfg
mode: "0640"
become: true
become_user: appuser # ← override niveau task

Une variable enregistrée n'est pas une chaîne mais un dictionnaire exposant stdout, stderr, rc et changed, d'où les suffixes de l'exemple. Un when: qui teste la variable sans suffixe est toujours vrai, puisque le dictionnaire n'est jamais vide : il faut viser .rc ou .stdout.

- name: Relever la version de l'application
ansible.builtin.command: /usr/local/bin/myapp --version
register: app_version
changed_when: false # ← lecture seule, pas idempotent
- name: Afficher la version relevée
ansible.builtin.debug:
var: app_version.stdout
when: app_version.rc == 0

loop remplace les anciennes formes with_* et expose chaque élément dans item, adressable par item.name quand l'élément est un dictionnaire. Le loop_control avec label n'est pas cosmétique en examen : sans lui, une boucle sur des variables volumineuses noie la sortie et rend la relecture des erreurs impraticable.

- name: Gérer le compte système
ansible.builtin.user:
name: "{{ item.name }}"
uid: "{{ item.uid }}"
loop:
- { name: alice, uid: 2001 }
- { name: bob, uid: 2002 }
loop_control:
label: "{{ item.name }}" # ← masque le contenu détaillé en sortie

Une liste sous when: vaut un ET logique entre toutes les lignes, ce qui dispense d'écrire and. Le filtre | int de la deuxième ligne est indispensable : distribution_major_version est une chaîne, et la comparer à un nombre lève une erreur de type au lieu de renvoyer un booléen.

when:
- ansible_facts.os_family == "RedHat"
- ansible_facts.distribution_major_version | int >= 9
- inventory_hostname in groups['webservers']

encrypt_string chiffre une valeur unique au lieu d'un fichier entier, ce qui laisse le reste des variables lisible. La sortie se colle telle quelle dans un fichier de variables : le marqueur !vault et le bloc indenté qui suit font partie de la syntaxe et ne se réalignent pas.

Fenêtre de terminal
ansible-vault encrypt_string 'SuperSecret' --name 'admin_password'
admin_password: !vault |
$ANSIBLE_VAULT;1.1;AES256
39373335366336323033666336323535...

Chaque --vault-id associe une étiquette à un fichier de mot de passe. L'étiquette est inscrite dans l'en-tête du contenu chiffré, ce qui permet à Ansible d'essayer en priorité la bonne clé et de faire cohabiter dans un même dépôt des secrets de développement et de production.

Fenêtre de terminal
ansible-playbook --vault-id dev@.vault_pass_dev --vault-id prod@.vault_pass_prod site.yml

Priorité des variables (descendante, RHCE objectif explicite)

Section intitulée « Priorité des variables (descendante, RHCE objectif explicite) »

Cet ordre tombe rarement en question directe : il se présente sous forme d'une tâche qui définit la même variable à deux endroits et attend une valeur précise. Les deux extrémités sont les seules à connaître par cœur, --extra-vars gagne toujours, defaults/main.yml perd toujours. Entre les deux, le couple à retenir est celui d'un même rôle : vars/main.yml ne peut pas être surchargé par un group_vars, alors que defaults/main.yml le peut. C'est ce qui décide où placer une valeur que l'inventaire doit pouvoir modifier.

  1. --extra-vars (CLI), prioritaire absolue
  2. Block vars
  3. Task vars
  4. include_vars
  5. set_fact / registered vars
  6. Play vars (vars:)
  7. vars/main.yml d'un rôle
  8. Host facts
  9. host_vars/
  10. group_vars/ (du plus spécifique au plus général)
  11. defaults/main.yml d'un rôle, prioritaire la plus basse
  12. Command line role default

Mémoire pratique : --extra-vars écrase tout le reste. defaults/main.yml se fait écraser par tout le reste.

La notation vérifie le comportement du rôle, pas son arborescence, mais cette structure reste payante pour une raison précise : chaque répertoire porte une sémantique de recherche. Un fichier déposé dans files/ est trouvé par copy: sans indiquer de chemin, un .j2 placé dans templates/ est trouvé par template: de la même façon. Se tromper de répertoire produit une erreur de fichier introuvable pénible à diagnostiquer sous chronomètre. La commande ansible-galaxy role init génère l'ensemble en une fois.

roles/myrole/
├── defaults/main.yml # variables par défaut (faible précédence)
├── vars/main.yml # variables internes (haute précédence)
├── tasks/main.yml # tâches principales
├── handlers/main.yml # handlers (notifiés par notify:)
├── templates/ # *.j2
├── files/ # fichiers à copy:
├── meta/main.yml # dependencies, supported_platforms
└── meta/argument_specs.yml # validation des inputs (Ansible 2.11+)

Ces pièges se répartissent en deux familles d'inégale gravité. Les erreurs de syntaxe YAML, comme un name: qui se termine par deux-points, arrêtent le playbook avec un message explicite et se corrigent en une minute. Les autres coûtent bien plus cher : un permanent: true oublié sur firewalld laisse l'exécution se terminer en ok, alors que la règle disparaîtra au redémarrage. L'état final ne correspond pas à l'attendu et l'objectif est noté zéro, sans qu'aucune ligne de la sortie n'ait signalé quoi que ce soit.

ErreurConséquence
mode: 644 (sans zéro initial)YAML lit 644 en décimal, soit 0o1204 en octal : permissions silencieusement fausses. Toujours mode: "0644"
name: Migration with_* vers loop: (terminé par :)YAML 1.2 lit comme mapping. Quoter : name: "..."
dnf: state=present name=httpd (vieux style)Toujours YAML structuré : dnf: { state: present, name: httpd } ou multi-ligne
Oublier permanent: true sur firewalldRègle non persistante après reboot, permanent: true, immediate: true systématique
Oublier python3-libselinux côté cibletemplate:/copy: plantent si SELinux=enforcing
name: Démarrer httpd: finit par :YAML mapping, quoter
Confondre import_* (static) et include_* (dynamic)Tags/conditions ne se propagent pas pareillement, voir Import vs Include

Ce découpage découle d'une contrainte du format : chaque objectif est noté séparément et il n'existe pas de point partiel. Terminer huit tâches simples rapporte donc davantage que d'en entamer quinze. D'où l'ordre proposé, lecture intégrale de l'énoncé, puis les tâches rapides, puis les longues. La demi-heure de marge finale n'est pas du confort : elle absorbe la tâche qui refuse de passer et qu'il faut reprendre depuis le début.

ÉtapeTempsAction
Lecture des consignes5-10 minLire les 12-15 tâches en entier avant d'écrire une ligne
Setup ansible.cfg + inventaire5-10 minPoser le ansible.cfg minimal + tester ansible all -m ping
Tâches faciles1hFaire les 8-9 tâches simples d'abord (validation rapide)
Tâches complexes1h30Rôles, Vault, SELinux, LVM/XFS, les plus longues
Vérification30 minRe-lancer chaque playbook, vérifier l'idempotence (changed=0 au 2e run)
Buffer30 minMarge pour les pièges de dernière minute

Règle d'or : vérifier au fur et à mesure. Ne pas attendre la fin pour relancer tous les playbooks, un bug détecté à la 4e heure coûte 30 min de panique.

La vérification porte sur deux propriétés distinctes. L'idempotence d'abord : un second passage doit afficher changed=0, faute de quoi la tâche modifie l'état à chaque exécution. L'état réel de la cible ensuite, contrôlé par des commandes ad hoc et non en relisant son propre playbook, ce qui reviendrait à vérifier une intention plutôt qu'un résultat. Les modules systemd_service, stat et command en mode ad hoc interrogent la machine sans rien y modifier.

Fenêtre de terminal
# Pour chaque playbook
ansible-playbook playbook.yml # 1er run
ansible-playbook playbook.yml | grep -E 'changed=[1-9]' # 2e run : doit être vide
# Vérifier qu'un service est bien actif après reboot
ansible <host> -m ansible.builtin.systemd_service -a 'name=httpd' -b
# → "ActiveState": "active"
# → "UnitFileState": "enabled"
# Vérifier un fichier déposé
ansible <host> -m ansible.builtin.stat -a 'path=/tmp/file.txt' -b
# Vérifier un user
ansible <host> -m ansible.builtin.command -a 'id appuser' -b

Aucun accès internet n'est fourni, mais la documentation locale est complète et autorisée. Savoir la parcourir vite est un acquis à travailler avant le jour J : ansible-doc affiche les paramètres d'un module et, en fin de sortie, des exemples directement copiables. C'est le réflexe qui remplace la recherche web, à condition d'être déjà familier de son format de sortie.

  • ansible-doc <module>, la doc complète est embarquée dans l'EE de l'examen.
  • ansible-doc -l | grep <mot>, retrouver le FQCN d'un module par mot-clé.
  • man ansible-playbook, options CLI rapides.
  • /usr/share/doc/ansible*/, docs locales sur RHEL.

L'examen vous autorise la documentation locale (ansible-doc, man). Aucun accès internet.

  • Performance-based : on écrit des playbooks. Pas de QCM.
  • ansible.cfg projet en premier, précédence locale > home > /etc.
  • FQCN systématique : ansible.builtin.copy, jamais copy.
  • mode: toujours quoté ("0644").
  • changed_when: false sur toute commande de lecture.
  • firewalld : permanent: true, immediate: true.
  • python3-libselinux côté cible si SELinux enforcing.
  • Vérifier l'idempotence : 2e run doit avoir changed=0.
  • ansible-doc est votre meilleur ami pendant l'examen.

Ce site vous est utile ?

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

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn