Aller au contenu
Réussir mon BTS SIO

Tape une notion, une commande ou le nom d'une épreuve.

Par exemple

Chapitre 1623 minIntermédiaire

Exercices Ansible corrigés type E6 SISR

Par Josselyn C., prof de BTS SIO. Mis à jour le .

À la fin de ce chapitre, tu sauras

  • écrire un inventaire et un playbook à partir d'un besoin ;
  • trouver les erreurs d'un playbook qui ne marche pas ;
  • traiter un cas de synthèse de bout en bout ;
  • expliquer ses choix comme devant la commission de l'E6.

7 exercices corrigés

Ce chapitre rassemble des exercices Ansible corrigés de difficulté croissante, dans l'esprit des tâches demandées pendant l'heure de réalisation de l'E6 SISR et des questions de l'entretien : écrire un inventaire, corriger un playbook, construire un rôle, gérer des serveurs Windows, lire un résultat, puis un cas de synthèse complet. Chaque exercice se suffit à lui-même ; les rappels de cours renvoient aux chapitres concernés.

Exercice 1 : l'inventaire de l'agence de Marseille

Exercice 1 sur 7 Écrire l'inventaire et ses variables Réussi À revoir

L'agence de Marseille a quatre serveurs dans son réseau des serveurs, 10.30.1.160/28 (passerelle 10.30.1.174) : deux serveurs web Debian 13, mrs-web1 (.161) et mrs-web2 (.162), un serveur de base de données Debian 13, mrs-bdd (.163), et un serveur de fichiers Windows Server 2025, mrs-fichiers (.164), membre du domaine ad.sorbier.example.

  1. Écris l'inventaire YAML inventaire/marseille.yml, avec un groupe marseille qui contient les groupes marseille_linux (lui-même découpé en marseille_web et marseille_bdd) et marseille_windows.
  2. Écris les fichiers de variables nécessaires pour que les serveurs Linux utilisent le compte ansible et que le serveur Windows utilise WinRM en HTTPS avec Kerberos et le compte svc-ansible.
  3. Donne la commande qui vérifie la structure, et celle qui liste les hôtes de marseille qui ne sont pas des serveurs web.
Voir la réponseMasquer la réponse de l'exercice 1
  1. inventaire/marseille.yml :
yaml
---
all:
  children:
    marseille:
      children:
        marseille_linux:
          children:
            marseille_web:
              hosts:
                mrs-web1:
                  ansible_host: 10.30.1.161
                mrs-web2:
                  ansible_host: 10.30.1.162
            marseille_bdd:
              hosts:
                mrs-bdd:
                  ansible_host: 10.30.1.163
        marseille_windows:
          hosts:
            mrs-fichiers:
              ansible_host: mrs-fichiers.ad.sorbier.example

Le serveur Windows est désigné par son nom complet : Kerberos a besoin d'un nom, pas d'une adresse.

  1. inventaire/group_vars/marseille_linux.yml :
yaml
---
ansible_user: ansible

inventaire/group_vars/marseille_windows/vars.yml :

yaml
---
ansible_connection: winrm
ansible_port: 5986
ansible_winrm_transport: kerberos
ansible_user: svc-ansible@AD.SORBIER.EXAMPLE
ansible_password: "{{ vault_mdp_svc_ansible }}"

et vault_mdp_svc_ansible dans inventaire/group_vars/marseille_windows/vault.yml, chiffré par ansible-vault. Si Marseille fait partie du même inventaire que le siège, on peut aussi placer marseille_windows comme groupe enfant de windows et réutiliser ses variables, sans les recopier.

  1. ansible-inventory -i inventaire/marseille.yml --graph et ansible -i inventaire/marseille.yml 'marseille:!marseille_web' --list-hosts, qui affiche mrs-bdd et mrs-fichiers.

Tu avais trouvé ?

Exercice 2 : le playbook qui ne marche pas

Exercice 2 sur 7 Six erreurs Réussi À revoir

Moussa a écrit ce playbook pour durcir SSH sur les serveurs Linux. Il contient six erreurs ou mauvaises pratiques, dont certaines empêchent l'exécution avec ansible-core 2.22. Trouve-les, explique-les et donne la version corrigée.

yaml
---
- name: Durcir SSH
  hosts: linux
  become: true
  vars:
    interdire_root: "yes"
  tasks:
    - name: Interdire la connexion de root
      ansible.builtin.lineinfile:
        path: /etc/ssh/sshd_config
        regexp: "^#?PermitRootLogin"
        line: PermitRootLogin no
      when: interdire_root

    - name: Afficher la distribution
      ansible.builtin.debug:
        msg: "Distribution : {{ ansible_distribution }}"

    - name: Redémarrer SSH
      ansible.builtin.service:
        name: sshd
        state: restarted

    - name: Déposer la bannière
      ansible.builtin.copy:
        src: files/banniere.txt
        dest: /etc/issue.net
        mode: 644
Voir la réponseMasquer la réponse de l'exercice 2
  1. when: interdire_root avec une chaîne : depuis ansible-core 2.19, une condition doit être un booléen ; "yes" est une chaîne, la tâche échoue (« Conditionals must have a boolean result »). Écrire interdire_root: true, ou when: interdire_root | bool.
  2. ansible_distribution en variable de premier niveau : l'injection des facts est dépréciée depuis 2.20 et disparaîtra en 2.24. Écrire ansible_facts['distribution'].
  3. Le redémarrage dans une tâche ordinaire : SSH est redémarré à chaque exécution, même sans changement, et la tâche n'est jamais idempotente. Il faut un handler, notifié par la modification du fichier.
  4. Le nom du service : sur Debian, le service s'appelle ssh, pas sshd (le nom sshd existe comme alias sur certaines versions, mais ssh est le bon nom). Et reloaded suffit : il relit la configuration sans couper les sessions ouvertes.
  5. Pas de validation : une erreur dans sshd_config empêcherait SSH de redémarrer, et Ansible ne pourrait plus se connecter. Ajouter validate: /usr/sbin/sshd -t -f %s.
  6. mode: 644 : sans guillemets ni zéro initial, c'est le nombre décimal 644, soit les droits octaux 1204. Écrire mode: "0644".
yaml
---
- name: Durcir SSH
  hosts: linux
  become: true
  vars:
    interdire_root: true

  tasks:
    - name: Interdire la connexion de root
      ansible.builtin.lineinfile:
        path: /etc/ssh/sshd_config
        regexp: "^#?PermitRootLogin"
        line: PermitRootLogin no
        validate: /usr/sbin/sshd -t -f %s
      when: interdire_root
      notify: Recharger SSH

    - name: Afficher la distribution
      ansible.builtin.debug:
        msg: "Distribution : {{ ansible_facts['distribution'] }}"

    - name: Déposer la bannière
      ansible.builtin.copy:
        src: files/banniere.txt
        dest: /etc/issue.net
        owner: root
        group: root
        mode: "0644"

  handlers:
    - name: Recharger SSH
      ansible.builtin.service:
        name: ssh
        state: reloaded

Pour aller plus loin, le rôle socle du chapitre 8 dépose plutôt un fichier dans /etc/ssh/sshd_config.d/, ce qui évite de modifier le fichier du paquet ligne par ligne. La bannière n'est affichée que si sshd_config contient Banner /etc/issue.net.

Tu avais trouvé ?

Exercice 3 : un premier rôle réutilisable

Exercice 3 sur 7 Écrire un rôle Réussi À revoir

Rappel : un rôle range ses variables réglables dans defaults/ et ses tâches dans tasks/ (chapitre 8). Écris le rôle outils (fichiers defaults/main.yml et tasks/main.yml) qui installe une liste de paquets réglable, par défaut tcpdump, dnsutils et rsync, et dépose le message d'accueil /etc/motd dont le texte est réglable. Puis écris le play qui l'applique aux serveurs Linux en ajoutant apachetop à la liste sur le seul srv-web, sans retirer les autres.

Voir la réponseMasquer la réponse de l'exercice 3

roles/outils/defaults/main.yml :

yaml
---
outils_paquets:
  - tcpdump
  - dnsutils
  - rsync
outils_paquets_hote: []
outils_motd: "Serveur de Sorbier Logistique : accès réservé au service informatique."

roles/outils/tasks/main.yml :

yaml
---
- name: Installer les outils
  ansible.builtin.apt:
    name: "{{ outils_paquets + outils_paquets_hote }}"
    state: present
    update_cache: true
    cache_valid_time: 3600

- name: Déposer le message d'accueil
  ansible.builtin.copy:
    content: "{{ outils_motd }}\n"
    dest: /etc/motd
    owner: root
    group: root
    mode: "0644"

Le play :

yaml
---
- name: Installer les outils sur les serveurs Linux
  hosts: linux
  become: true
  roles:
    - outils

et host_vars/srv-web.yml reçoit outils_paquets_hote: [apachetop]. Comme Ansible remplace les listes au lieu de les fusionner, on sépare la liste commune et la liste propre à l'hôte, et la tâche les additionne.

Tu avais trouvé ?

Exercice 4 : un rôle de sauvegarde

Exercice 4 sur 7 Le rôle sauvegarde Réussi À revoir

Chaque serveur Linux doit sauvegarder chaque nuit son dossier /etc avec un script fourni par l'équipe, files/sauvegarde-etc.sh. Écris le rôle sauvegarde (defaults/main.yml et tasks/main.yml) qui :

  1. installe rsync ;
  2. crée le dossier /var/backups/sorbier (propriétaire root, droits 0700) ;
  3. dépose le script dans /usr/local/sbin/sauvegarde-etc.sh (droits 0750) ;
  4. planifie son exécution chaque nuit, l'heure étant réglable (par défaut 2 h 15), avec la sortie dans /var/log/sauvegarde-etc.log.

Puis écris le play qui l'applique aux serveurs Linux, en décalant l'heure à 3 h 15 pour srv-journaux.

Voir la réponseMasquer la réponse de l'exercice 4

roles/sauvegarde/defaults/main.yml :

yaml
---
sauvegarde_dossier: /var/backups/sorbier
sauvegarde_heure: 2
sauvegarde_minute: 15

roles/sauvegarde/tasks/main.yml :

yaml
---
- name: Installer rsync
  ansible.builtin.apt:
    name: rsync
    state: present

- name: Créer le dossier des sauvegardes
  ansible.builtin.file:
    path: "{{ sauvegarde_dossier }}"
    state: directory
    owner: root
    group: root
    mode: "0700"

- name: Déposer le script de sauvegarde
  ansible.builtin.copy:
    src: sauvegarde-etc.sh
    dest: /usr/local/sbin/sauvegarde-etc.sh
    owner: root
    group: root
    mode: "0750"

- name: Planifier la sauvegarde chaque nuit
  ansible.builtin.cron:
    name: Sauvegarde de /etc
    user: root
    hour: "{{ sauvegarde_heure }}"
    minute: "{{ sauvegarde_minute }}"
    job: /usr/local/sbin/sauvegarde-etc.sh >> /var/log/sauvegarde-etc.log 2>&1

Le script va dans roles/sauvegarde/files/sauvegarde-etc.sh : copy le trouve sans chemin. Le name de la tâche cron sert d'identifiant : Ansible l'écrit en commentaire au-dessus de la ligne de la crontab et s'en sert pour la retrouver, ce qui rend la tâche idempotente. Le play :

yaml
---
- name: Planifier les sauvegardes de /etc
  hosts: linux
  become: true
  roles:
    - sauvegarde

et host_vars/srv-journaux.yml reçoit sauvegarde_heure: 3. Une sauvegarde qui reste sur le serveur ne protège pas d'une perte du serveur : une copie hors de la machine (vers un NAS, un serveur de sauvegarde) est indispensable, comme l'explique le cours Sauvegarde et PRA.

Tu avais trouvé ?

Exercice 5 : les serveurs Windows

Exercice 5 sur 7 Un socle Windows Réussi À revoir

Écris le playbook socle_windows.yml qui, sur tous les serveurs Windows de Sorbier :

  1. règle le fuseau horaire de Paris (Romance Standard Time) ;
  2. crée le dossier C:\Outils ;
  3. installe les fonctionnalités RSAT-AD-Tools et Windows-Server-Backup ;
  4. sur les contrôleurs de domaine seulement, arrête et désactive le service Spooler ;
  5. redémarre le serveur si l'installation des fonctionnalités l'exige.
Voir la réponseMasquer la réponse de l'exercice 5
yaml
---
- name: Appliquer le socle aux serveurs Windows
  hosts: windows

  tasks:
    - name: Régler le fuseau horaire de Paris
      ansible.windows.win_timezone:
        timezone: Romance Standard Time

    - name: Créer le dossier des outils
      ansible.windows.win_file:
        path: C:\Outils
        state: directory

    - name: Installer les outils AD et la sauvegarde Windows
      ansible.windows.win_feature:
        name:
          - RSAT-AD-Tools
          - Windows-Server-Backup
        state: present
      register: fonctionnalites

    - name: Arrêter et désactiver le spouleur sur les contrôleurs
      ansible.windows.win_service:
        name: Spooler
        state: stopped
        start_mode: disabled
      when: "'controleurs' in group_names"

    - name: Redémarrer si nécessaire
      ansible.windows.win_reboot:
      when: fonctionnalites.reboot_required

Le chemin C:\Outils est écrit sans guillemets : entre guillemets doubles, \O serait interprété par YAML. La condition 'controleurs' in group_names vise srv-ad et srv-ad2. Le redémarrage est en dernier : si plusieurs tâches peuvent l'exiger, on enregistre chacune et on combine les conditions, ou on utilise ansible.windows.win_reboot_info pour demander à Windows s'il attend un redémarrage. Sur des contrôleurs de domaine, ajoute serial: 1 au play pour ne jamais redémarrer les deux en même temps.

Tu avais trouvé ?

Exercice 6 : lire le résultat d'une exécution

Exercice 6 sur 7 Analyser un récapitulatif Réussi À revoir

Chloé lance ansible-playbook site.yml et obtient ce récapitulatif :

text
PLAY RECAP *********************************************************************
srv-journaux               : ok=12   changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0
srv-recette                : ok=0    changed=0    unreachable=1    failed=0    skipped=0    rescued=0    ignored=0
srv-web                    : ok=15   changed=3    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0
  1. Que s'est-il passé sur chaque hôte ?
  2. Quel est le code de retour de la commande ?
  3. Quelles commandes lances-tu pour enquêter et reprendre ?
Voir la réponseMasquer la réponse de l'exercice 6
  1. srv-journaux : tout s'est bien passé, rien n'a changé (configuration déjà conforme), une tâche sautée par une condition. srv-recette : injoignable, aucune tâche n'a tourné (VM arrêtée, réseau, SSH, clé). srv-web : 15 tâches réussies dont 3 modifications, puis un échec, qui a arrêté le playbook pour cet hôte : les tâches suivantes n'ont pas été exécutées, et les handlers notifiés avant l'échec non plus (sauf --force-handlers).
  2. 4 : ansible-core renvoie 4 dès qu'un hôte est injoignable, ce code passant avant le 2 d'un hôte en échec. Un script qui teste seulement « différent de 0 » détecte les deux cas.
  3. Remonter dans la sortie jusqu'à la tâche fatal: [srv-web] pour lire le message. Relancer en détail sur ce seul hôte : ansible-playbook site.yml --limit srv-web -v, voire --start-at-task "<nom de la tâche>" après correction. Pour srv-recette : ansible srv-recette -m ansible.builtin.ping -vvv, qui montre la commande SSH et l'erreur exacte. Une fois corrigé, relancer tout le playbook : il est idempotent.

Tu avais trouvé ?

Exercice 7 : cas de synthèse, le site de suivi en recette

Exercice 7 sur 7 Industrialiser la recette Réussi À revoir

Antoine veut tester chaque évolution du site de suivi des livraisons sur la VM srv-recette (VM 186, 192.168.21.186, créée au chapitre 13) avant de l'appliquer à srv-web. Le site de recette doit répondre au nom suivi-recette.sorbier.example, avec la racine /var/www/suivi-recette. Tout doit passer par le dépôt sorbier-infra.

  1. Quelles modifications fais-tu dans l'inventaire ? Écris-les.
  2. Écris la tâche qui déclare dans le DNS de l'annuaire un alias suivi-recette vers srv-recette.ad.sorbier.example, et dis sur quel hôte elle s'exécute.
  3. Le playbook site.yml applique socle au groupe linux et web au groupe serveurs_web. Que faut-il changer pour que srv-recette reçoive les deux ? Donne la commande qui applique la configuration à la seule VM de recette.
  4. Comment prouves-tu à Antoine que la configuration est idempotente et que rien n'a changé sur srv-web ?
  5. Le certificat TLS du site de recette doit venir de l'AC interne. Où ranges-tu sa clé privée dans le dépôt, et comment ?
  6. La commission de l'E6 te demande : « Pourquoi ne pas avoir simplement copié la configuration de srv-web à la main ? » Réponds en quelques phrases.
Voir la réponseMasquer la réponse de l'exercice 7
  1. Dans inventaire/hosts.yml, ajouter srv-recette aux groupes concernés :
yaml
        serveurs_web:
          hosts:
            srv-web:
              ansible_host: 192.168.10.225
            srv-recette:
              ansible_host: 192.168.21.186

Comme serveurs_web est un enfant de linux, srv-recette est aussi dans linux. Puis inventaire/host_vars/srv-recette.yml :

yaml
---
web_nom_site: suivi-recette.sorbier.example
web_racine: /var/www/suivi-recette
web_certificat: /etc/ssl/certs/suivi-recette.sorbier.example.pem
web_cle_privee: /etc/ssl/private/suivi-recette.sorbier.example.key

Une organisation plus propre, à terme, sépare les environnements dans deux inventaires (inventaire/recette/ et inventaire/production/, chapitre 15).

  1. Sur un contrôleur de domaine, srv-ad (la zone est répliquée par l'annuaire) :
yaml
- name: Déclarer l'alias du site de recette
  hosts: srv-ad
  tasks:
    - name: Créer l'alias suivi-recette
      ansible.windows.win_dns_record:
        name: suivi-recette
        type: CNAME
        value: srv-recette.ad.sorbier.example
        zone: ad.sorbier.example
        state: present

Attention : suivi-recette.sorbier.example est dans la zone sorbier.example, pas dans ad.sorbier.example. Si le DNS interne ne porte pas la zone sorbier.example, il faut soit créer l'enregistrement dans cette zone là où elle est hébergée, soit nommer le site suivi-recette.ad.sorbier.example. C'est le genre de détail que la commission apprécie de te voir relever.

  1. Rien dans site.yml : il vise des groupes, et srv-recette est maintenant dans les deux. Pour l'appliquer à la seule recette : ansible-playbook site.yml --limit srv-recette.

  2. Deux exécutions de suite sur la recette : la seconde affiche changed=0 pour srv-recette dans le récapitulatif. Pour la production : ansible-playbook site.yml --limit srv-web --check --diff, qui doit annoncer changed=0 ; et le diff Git de la demande de fusion montre que seuls l'inventaire et host_vars/srv-recette.yml ont changé. Le pipeline vert (--syntax-check et ansible-lint) complète la preuve.

  3. La clé privée est un secret : elle va dans un fichier chiffré, par exemple inventaire/host_vars/srv-recette/vault.yml (avec les autres variables dans host_vars/srv-recette/vars.yml), sous forme de variable multiligne vault_cle_tls_recette, chiffrée par ansible-vault. Une tâche ansible.builtin.copy avec content: "{{ vault_cle_tls_recette }}", mode: "0600" et no_log: true la dépose sur le serveur. Le certificat, public, peut rester en clair dans files/.

  4. « Une copie à la main donne un serveur identique le jour de la copie, mais rien ne garantit qu'il le reste, ni que la copie était complète. Avec Ansible, la recette et la production sont construites par le même code, avec des variables différentes : ce que je teste en recette est exactement ce qui sera appliqué en production. Chaque modification est tracée dans Git, relue et contrôlée par le pipeline, et je peux reconstruire la recette en quelques minutes si la VM est perdue. Enfin, relancer le playbook corrige toute dérive : la deuxième exécution qui n'affiche aucun changement le prouve. »

Tu avais trouvé ?

Chapitre terminé !

Fais les 7 exercices avant de passer à la suite : c'est en t'entraînant que tout se fixe.

chapitres
–
de série
–
du cours
–
Chapitre suivant : Aide-mémoire : les commandes AnsibleRevoir le sommaire du cours