Introduction
Juniper & Oat doit appliquer les mêmes prix de boissons approuvés dans deux cafés, sans perdre leur identité, leurs horaires ni leurs spécialités locales. Vous utiliserez un modèle et un playbook Ansible pour actualiser deux véritables cibles gérées par SSH, puis corrigerez une dérive volontaire du menu.
Vous devez savoir naviguer dans un terminal et avoir déjà lu un petit inventaire et un playbook Ansible. Les variables d’hôte, la génération JSON, la prévisualisation sûre et la différence entre contenu géré et local sont expliquées lors de leur utilisation. Les outils et les sites préparés permettent de vous concentrer sur l’automatisation plutôt que sur l’installation.
Séparer les prix communs des informations locales
Dans cette étape, vous identifierez ce que les deux cafés partagent et ce qui doit rester différent, puis déclarerez les données approuvées pour un modèle réutilisable.
Juniper & Oat possède deux cafés de quartier. Ils utilisent les mêmes boissons et prix habituels, mais chacun dispose de ses propres horaires, spécialité et avis local. Vous devez actualiser le menu commun sans remplacer ces particularités. Les hôtes préparés web1 et web2 sont deux conteneurs gérés par SSH qui partagent le noyau de la VM de formation, et non deux VM indépendantes. Controller exécute Ansible ; chaque cible exécute son propre service réel de menu.
Ouvrez Menu Preview, sélectionnez First branch, puis lisez le menu, les horaires et la spécialité. Utilisez le bouton Retour du navigateur pour revenir à la liste, puis sélectionnez Second branch. Les deux affichent un ancien prix pour Flat white ; Park Café affiche aussi des horaires incorrects. Les spécialités et les avis locaux sont déjà corrects. Visit ouvre les informations de visite du même café ; Download menu télécharge son véritable CSV actuel. Ce sont des liens de page, pas des onglets LabEx.
Passez à l’onglet Controller. Le terminal commence dans le répertoire du projet ; cette commande explicite permet aussi d’y revenir après une erreur de répertoire :
cd /home/labex/project
Les noms web1 et web2 de l’inventaire désignent des cibles SSH préparées distinctes. Consultez l’inventaire et les données locales approuvées avec cat, qui lit les fichiers sans les modifier :
cat inventory.ini
cat host_vars/web1.yml host_vars/web2.yml
Harbor Café doit ouvrir de 07:00–16:00 et Park Café de 08:00–18:00. Le répertoire host_vars fournit les variables de l’hôte d’inventaire portant le même nom. Conservez ces données et les réglages SSH préparés. Leur différence est voulue, pas une erreur à éliminer.
Le menu commun approuvé est fourni comme donnée du scénario. Lisez-le avant de rédiger les variables :
cat approved-menu.json
Il indique Espresso à 3.20, Flat white à 4.20 et Tea à 3.00. Un prix est une chaîne entre guillemets pour conserver ses deux décimales lors du formatage. menu_items sera une liste YAML d’objets nom/prix partagée par les deux hôtes.
Les répertoires préparés group_vars et templates sont vides. Le fichier group_vars/all.yml fournit des variables à tous les hôtes. Le here-document suivant écrit un fichier complet : > remplace cette destination précise, et le délimiteur YAML entre apostrophes empêche le shell de développer le contenu. Le délimiteur final doit être seul au début d’une ligne. En cas de faute, relancez le bloc complet ; il ne remplace que votre fichier de variables.
cat > group_vars/all.yml <<'YAML'
menu_items:
- name: Espresso
price: "3.20"
- name: Flat white
price: "4.20"
- name: Tea
price: "3.00"
YAML
Un modèle Jinja insère les valeurs résolues dans un fichier géré. Les doubles accolades évaluent une expression ; la barre verticale applique un filtre. Ici, to_nice_json sérialise l’objet entier en JSON valide et lisible, caractères non ASCII compris. Vous évitez ainsi de placer manuellement chaque valeur entre guillemets. Le modèle est identique pour les deux cafés : branch_name et opening_hours proviennent de leurs données d’hôte respectives, tandis que menu_items est commun.
cat > templates/menu.json.j2 <<'JINJA'
{{ {'branch': branch_name, 'hours': opening_hours, 'items': menu_items} | to_nice_json }}
JINJA
Relisez les deux sources rédigées pour repérer un guillemet manquant, une mauvaise indentation ou un délimiteur incorrect avant d’exécuter Ansible :
cat group_vars/all.yml templates/menu.json.j2
Le site n’a pas encore changé. Vous avez déclaré les données souhaitées, sans les appliquer. À l’étape suivante, Ansible générera le fichier dans le contexte de variables de chaque hôte.
Appliquer un playbook aux deux cafés
Dans cette étape, vous prévisualiserez et déploierez la même déclaration sur les deux vraies cibles SSH, puis utiliserez leurs menus et téléchargements réels.
Restez dans Controller, sous /home/labex/project. Un play sélectionne ses hôtes ; les tâches déclarent l’état géré. become: true demande l’élévation de privilèges autorisée sur la cible pour que les fichiers puissent appartenir à root. Cela ne change pas le compte de connexion LabEx. Le module file déclare le répertoire ; template traite la source commune et remplace uniquement menu.json. Une propriété explicite et des permissions entre guillemets rendent prévisibles la traversée du répertoire et la lecture publique : 0755 pour le répertoire, 0644 pour le menu.
Les fichiers locaux local-special.txt, local-notice.txt et le fichier privé staff-notes.txt sont hors de votre déclaration de menu. Ne remplacez pas le contenu du répertoire et ne modifiez pas ces fichiers. Conservez leurs octets, leur propriété root et leurs permissions initiales : 0644 pour la spécialité et l’avis, 0600 pour les notes privées. Le service lit menu.json à chaque requête ; une mise à jour de données ne nécessite donc ni redémarrage artificiel ni handler.
Écrivez un playbook complet. Les éléments de liste YAML utilisent -, et les espaces imbriquent les paramètres des modules. Les chemins de src désignent les sources du contrôleur ; dest se trouve sur chaque cible gérée.
cat > menus.yml <<'YAML'
- name: Keep both branch menus current
hosts: all
gather_facts: false
become: true
tasks:
- name: Keep the menu directory available
ansible.builtin.file:
path: /srv/branch-menu
state: directory
owner: root
group: root
mode: "0755"
- name: Render the shared menu with each branch's inputs
ansible.builtin.template:
src: templates/menu.json.j2
dest: /srv/branch-menu/menu.json
owner: root
group: root
mode: "0644"
YAML
Utilisez d’abord --syntax-check pour valider la syntaxe sans appliquer le playbook :
ansible-playbook menus.yml --syntax-check
Si une erreur YAML apparaît, lisez le fichier et la ligne indiqués. Comparez l’indentation avec le bloc complet ci-dessus, réécrivez ce bloc, puis répétez la vérification. Une erreur de syntaxe Ansible ne nécessite pas de réinitialiser la VM.
--check prédit les changements des modules compatibles ; --diff montre le contenu proposé avant et après. C’est utile pour ces fichiers sans secrets, mais une prévisualisation n’est pas un test du service réel. Utilisez les deux options ensemble :
ansible-playbook menus.yml --check --diff
Les deux hôtes doivent prévoir une modification de menu. Le diff doit corriger les prix et les horaires de Park Café en conservant les identités distinctes. Aucun fichier cible n’a changé. Passez à Menu Preview, ouvrez ou actualisez les cafés et confirmez que les anciennes valeurs restent visibles. Revenez à Controller pour appliquer la même déclaration sans les options de prévisualisation :
ansible-playbook menus.yml
Le récapitulatif de chaque hôte ne doit montrer ni cible injoignable ni tâche en échec. Le nombre exact de changed peut varier car chaque hôte nécessitait des corrections différentes. Lisez les vraies données avec une commande ad hoc : all sélectionne les deux hôtes d’inventaire, -m nomme le module et -a transmet ses arguments. Le module command exécute cat sur chaque cible, pas sur le contrôleur.
ansible all -m ansible.builtin.command -a 'cat /srv/branch-menu/menu.json'
Les deux doivent contenir les boissons et prix approuvés. Harbor Café et Park Café doivent garder leurs noms et horaires respectifs.
Passez maintenant à Menu Preview et actualisez chaque café. Lisez les trois prix, le nom, les horaires, la spécialité et l’avis. Ouvrez Visit, puis revenez par Menu. Sélectionnez Download menu et ouvrez le CSV : il doit contenir les trois articles approuvés du café avec son propre nom sur chaque ligne. Une page d’accueil fonctionnelle ne suffit pas.

Comparez les prix communs à gauche avec la spécialité locale à droite. Park Café garde ses horaires et sa spécialité tout en utilisant la même liste de boissons approuvée.
Si le navigateur signale un menu manquant ou invalide, revenez dans Controller, lisez la sortie et comparez votre modèle JSON à celui de l’étape 1. Corrigez la source du contrôleur et relancez le playbook. Si une cible est injoignable, lisez inventory.ini et comparez-le à la sortie initiale ; ne désactivez pas la vérification de confiance SSH et ne changez pas les comptes de la plateforme.
Revenez à Controller. Une répétition avec les mêmes entrées ne doit prévoir aucun changement :
ansible-playbook menus.yml
Repérez les deux lignes du récapitulatif :
web1 ... changed=0 ... unreachable=0 ... failed=0 ...
web2 ... changed=0 ... unreachable=0 ... failed=0 ...
C’est une configuration idempotente : les fichiers souhaités correspondent déjà, donc Ansible ne les réécrit pas. Cela ne signifie pas que les requêtes habituelles ou les journaux du service cessent.
Corriger la dérive sans effacer le contenu local
Dans cette étape, vous altérerez volontairement une valeur du menu géré, puis utiliserez la source inchangée du contrôleur pour la restaurer sans toucher au contenu local.
Restez dans Controller. Une dérive de configuration signifie que l’état géré réel ne correspond plus à l’intention déclarée. Des horaires différents mais approuvés ne constituent pas une dérive ; un prix commun modifié à la main en est une.
L’opération suivante est une panne volontaire limitée à web1. -b demande become pour cette tâche ad hoc. Le paramètre content de copy remplace exactement le JSON indiqué, pas son répertoire. Sa structure est correcte, mais le prix d’Espresso, 0.01, n’est pas approuvé. Ce fichier temporaire est volontairement faux ; ne recopiez pas son prix dans group_vars.
ansible web1 -b -m ansible.builtin.copy -a 'dest=/srv/branch-menu/menu.json content={"branch":"Harbor Café","hours":"07:00–16:00","items":[{"name":"Espresso","price":"0.01"},{"name":"Flat white","price":"4.20"},{"name":"Tea","price":"3.00"}]} owner=root group=root mode=0644'
Passez à Menu Preview, actualisez First branch et observez le mauvais prix d’Espresso. Le second café et les deux spécialités locales doivent rester intacts. Il s’agit d’une vraie modification de fichier cible, pas d’un voyant rouge simulé.
Revenez à Controller. Ne modifiez pas les sources initiales. Prévisualisez l’état souhaité existant :
ansible-playbook menus.yml --check --diff
Seul web1 doit prévoir un changement. Web2 correspond déjà à son état approuvé. Appliquez le même playbook pour rétablir le menu géré :
ansible-playbook menus.yml
Passez à Menu Preview et actualisez les deux cafés. Espresso doit de nouveau coûter 3.20. Les spécialités, avis, horaires et téléchargements doivent rester corrects. Votre automatisation gère un fichier de configuration, pas tous les fichiers locaux du répertoire.
Revenez à Controller et effectuez une dernière répétition :
ansible-playbook menus.yml
Les deux récapitulatifs doivent de nouveau indiquer changed=0, unreachable=0 et failed=0. La réparation repose sur une intention réutilisable dans les données du contrôleur et un modèle commun, pas sur une correction manuelle ponctuelle. Le même modèle doit rester utilisable lorsqu’un article commun approuvé changera à l’avenir.
Résumé
Vous avez séparé les prix communs, les données des cafés et le contenu local, prévisualisé et appliqué un même modèle à deux cibles SSH, utilisé de vraies pages et des téléchargements CSV, puis corrigé une dérive réelle sans effacer les différences légitimes. La répétition sans changement a confirmé la convergence des menus déclarés.



