Grand TP final : Nordika ouvre un nouveau site — provisionner toute l'infra en un seul run

Le scénario

Nordika vient de signer avec un nouveau client, et ouvre un site dédié pour l’héberger. Il vous faut, en une seule intervention : provisionner l’accès de deux administrateurs, déployer un serveur web avec la configuration propre à ce client, mettre en place la base de données applicative avec ses secrets protégés, et vous assurer que tout ça est parfaitement rejouable si jamais un troisième serveur rejoint l’infrastructure plus tard.

C’est l’occasion de mobiliser, dans un seul projet structuré, tout ce que vous avez appris depuis la première leçon de cette formation.

Environnement disponible

Vous disposez de :

  • 1 serveur de contrôle avec Ansible installé
  • 2 clients Debian
    • nrd-web1
    • nrd-db1

Informations de connexion

  • Utilisateur : root
  • Mot de passe : formation

⚠️ Les conteneurs de ce lab repartent de zéro à chaque session. Prévoyez donc le bootstrap complet (clé SSH, inventaire) en tout début de TP.

Cahier des charges

  1. Accès : deux comptes administrateurs (admin1, admin2) doivent exister sur les deux machines, avec leur propre clé SSH, membres d’un groupe admin, et un droit sudo complet.
  2. Web : Apache doit être installé sur nrd-web1, avec un VirtualHost templatisé au nom du nouveau client (server_domain: "client-nouveausite.nordika.local"), redémarré uniquement si la configuration change.
  3. Base de données : MariaDB doit être installé sur nrd-db1, avec un mot de passe root et un mot de passe applicatif tous deux protégés par Vault, une base client_nouveausite_db et un utilisateur applicatif dédié.
  4. Structure : le projet doit être organisé en rôles (utilisateurs, apache, mariadb), avec un inventaire propre séparant webservers et databases.
  5. Rejouabilité : l’ensemble du playbook, rejoué une seconde fois sans aucune modification, doit afficher changed=0 sur toutes les tâches.
  6. Bonus : le playbook doit permettre de ne redéployer que la partie web via un tag, sans toucher aux comptes ni à la base de données.

Conseils

  • Vous avez déjà écrit chacun de ces trois rôles séparément dans les TP précédents (6, 8 et 9) — l’exercice ici consiste à les assembler proprement pour un cas concret complet, pas à repartir de zéro.
  • Pensez à l’ordre des plays : la vérification de connectivité d’abord, puis les comptes (qui concernent les deux machines), puis le web et la base de données chacun sur leur groupe.
  • N’oubliez pas validate: "visudo -cf %s" sur le fichier sudoers des administrateurs.

🧪 Lab · membre

🧪 Démarrer — 30 min
Voir la solutionMasquer la solution

Installer Ansible

apt update
apt install ansible

Bootstrap complet

ssh-keygen -t ed25519 -C "ansible@control-serveur" -f ~/.ssh/id_ed25519 -N ""

ssh root@nrd-web1
nano .ssh/authorized_keys
# Coller la clé publique

ssh root@nrd-db1
nano .ssh/authorized_keys
# Coller la même clé publique

ping nrd-web1
ping nrd-db1

Structure du projet et inventaire

mkdir -p ansible/{roles,group_vars,files} && cd ansible
nano inventaire.yaml
# inventaire.yaml
all:
  hosts:
    nrd-web1:
      ansible_host: <IP>
    nrd-db1:
      ansible_host: <IP>
  children:
    webservers:
      hosts:
        nrd-web1:
    databases:
      hosts:
        nrd-db1:
ansible all -i inventaire.yaml -m ping

Générer les clés SSH des deux administrateurs

ssh-keygen -t ed25519 -C "admin1@nordika" -f files/admin1_id_ed25519 -N ""
ssh-keygen -t ed25519 -C "admin2@nordika" -f files/admin2_id_ed25519 -N ""

Le rôle utilisateurs

cd roles && ansible-galaxy init utilisateurs && cd ..
nano roles/utilisateurs/templates_sudoers_admin.j2
# roles/utilisateurs/templates_sudoers_admin.j2
{{ item.nom }} ALL=(ALL) NOPASSWD: ALL
nano roles/utilisateurs/tasks/main.yml
# roles/utilisateurs/tasks/main.yml
---
- name: Créer le groupe admin
  ansible.builtin.group:
    name: admin
    state: present

- name: Créer les comptes administrateurs
  ansible.builtin.user:
    name: "{{ item.nom }}"
    groups: admin
    append: yes
    create_home: yes
    state: present
  loop: "{{ administrateurs }}"
  loop_control:
    label: "{{ item.nom }}"

- name: Déployer la clé publique de chaque administrateur
  ansible.posix.authorized_key:
    user: "{{ item.nom }}"
    state: present
    key: "{{ lookup('file', 'files/' + item.cle) }}"
  loop: "{{ administrateurs }}"
  loop_control:
    label: "{{ item.nom }}"

- name: Accorder les droits sudo complets à chaque administrateur
  ansible.builtin.template:
    src: templates_sudoers_admin.j2
    dest: "/etc/sudoers.d/{{ item.nom }}"
    validate: "visudo -cf %s"
    owner: root
    group: root
    mode: "0440"
  loop: "{{ administrateurs }}"
  loop_control:
    label: "{{ item.nom }}"
# group_vars/all.yml
administrateurs:
  - { nom: "admin1", cle: "admin1_id_ed25519.pub" }
  - { nom: "admin2", cle: "admin2_id_ed25519.pub" }

Le rôle apache

cd roles && ansible-galaxy init apache && cd ..
nano roles/apache/templates/nordika.conf.j2
# roles/apache/templates/nordika.conf.j2
<VirtualHost *:80>
    ServerName {{ server_domain }}
    DocumentRoot /var/www/html
</VirtualHost>
nano roles/apache/tasks/main.yml
# roles/apache/tasks/main.yml
---
- name: Installer Apache
  ansible.builtin.apt:
    name: apache2
    state: present
    update_cache: yes
  tags: install

- name: Déployer le VirtualHost depuis le template
  ansible.builtin.template:
    src: nordika.conf.j2
    dest: /etc/apache2/sites-available/nordika.conf
  notify: Redémarrer Apache
  tags: config

- name: Activer le site
  ansible.builtin.command: a2ensite nordika.conf
  args:
    creates: /etc/apache2/sites-enabled/nordika.conf
  notify: Redémarrer Apache
  tags: config
# roles/apache/handlers/main.yml
---
- name: Redémarrer Apache
  ansible.builtin.service:
    name: apache2
    state: restarted
# host_vars/nrd-web1.yml
server_domain: "client-nouveausite.nordika.local"

Le rôle mariadb, avec Vault

cd roles && ansible-galaxy init mariadb && cd ..

echo "MonMotDePasseVault123" > .vault_pass.txt
chmod 600 .vault_pass.txt
nano ansible.cfg
# ansible.cfg
[defaults]
vault_password_file = .vault_pass.txt
ansible-vault encrypt_string 'RootDB123!' --name 'mysql_root_password' --vault-password-file .vault_pass.txt
ansible-vault encrypt_string 'AppDB123!' --name 'app_password' --vault-password-file .vault_pass.txt
nano host_vars/nrd-db1.yml
# host_vars/nrd-db1.yml
app_db: client_nouveausite_db
app_user: client_nouveausite_user
mysql_root_password: !vault |
          $ANSIBLE_VAULT;1.1;AES256
          <collez le bloc généré>
app_password: !vault |
          $ANSIBLE_VAULT;1.1;AES256
          <collez le bloc généré>
nano roles/mariadb/tasks/main.yml
# roles/mariadb/tasks/main.yml
---
- name: Installer MariaDB
  ansible.builtin.apt:
    name:
      - mariadb-server
      - python3-pymysql
    state: present
    update_cache: yes
  tags: install

- name: Démarrer MariaDB
  ansible.builtin.service:
    name: mariadb
    state: started
    enabled: yes
  tags: install

- name: Définir le mot de passe root
  community.mysql.mysql_user:
    name: root
    host: localhost
    password: "{{ mysql_root_password }}"
    login_unix_socket: /run/mysqld/mysqld.sock
    check_implicit_admin: true
    priv: "*.*:ALL,GRANT"
  tags: config

- name: Créer la base applicative
  community.mysql.mysql_db:
    name: "{{ app_db }}"
    state: present
    login_user: root
    login_password: "{{ mysql_root_password }}"
  tags: config

- name: Créer l'utilisateur applicatif
  community.mysql.mysql_user:
    name: "{{ app_user }}"
    password: "{{ app_password }}"
    priv: "{{ app_db }}.*:ALL"
    host: "%"
    state: present
    login_user: root
    login_password: "{{ mysql_root_password }}"
  tags: config

Le playbook d’orchestration final

nano playbook_nouveau_site.yaml
# playbook_nouveau_site.yaml
---
- name: Vérifier la connectivité avant tout déploiement
  hosts: webservers:databases
  gather_facts: yes
  tasks:
    - name: Ping de toute la flotte concernée
      ansible.builtin.ping:
      tags: always

- name: Provisionner les administrateurs
  hosts: webservers:databases
  become: yes
  roles:
    - role: utilisateurs
      tags: users

- name: Déployer le serveur web du nouveau site
  hosts: webservers
  become: yes
  roles:
    - role: apache
      tags: apache

- name: Déployer la base de données du nouveau site
  hosts: databases
  become: yes
  roles:
    - role: mariadb
      tags: mariadb

Exécuter le tout

ansible-playbook playbook_nouveau_site.yaml -i inventaire.yaml -v

Valider la rejouabilité

ansible-playbook playbook_nouveau_site.yaml -i inventaire.yaml -v

changed=0 sur l’ensemble des tâches, sur les deux machines : l’infrastructure du nouveau site est entièrement provisionnée, de façon idempotente.

Valider le bonus : redéployer uniquement la partie web

ansible-playbook playbook_nouveau_site.yaml -i inventaire.yaml --tags apache

Seul le play ciblant webservers s’exécute — comptes et base de données restent intouchés.

🎉 Formation terminée. Vous êtes parti d’un simple ansible -m ping sans inventaire, vous terminez avec une infrastructure complète, sécurisée et rejouable, provisionnée en une seule commande. C’est exactement la compétence qui aurait évité, chez Nordika, la nuit où tout a mal tourné.