Session MCA — Ansible en pratique
Déploiement automatisé de GLPI 11 (outil ITSM/CMDB) sur Debian 13 et Rocky Linux 10.
Stack : Apache2 · MariaDB · PHP-FPM · Jinja2 templates · Variables externalisées
⚠️ Compatibilité PHP :
GLPI 10.x supporte PHP 7.4 à 8.3 max. GLPI 11.x supporte PHP 8.2 à 8.5.
Debian 13 embarquant PHP 8.4 natif, GLPI 11 est requis.
glpi-ansible/
├── deploy_glpi.yml # Playbook principal
├── vars/
│ └── main.yml # Toutes les variables
└── templates/
├── virtualhost.conf.j2 # VirtualHost Apache (template Jinja2)
└── glpi-cron.j2 # Crontab des tâches automatiques GLPI
Concept — pourquoi séparer variables et templates ?
Le playbook décrit quoi faire, les variables définissent avec quelles valeurs, les templates génèrent les fichiers de configuration. Cette séparation permet de réutiliser le même playbook pour déployer GLPI en dev, staging et prod en changeant uniquementvars/main.yml. C'est le principe DRY (Don't Repeat Yourself) appliqué à l'infrastructure.
---
# ============================================================
# GLPI — Version et téléchargement
# ============================================================
glpi_version: "11.0.4"
glpi_archive_url: "https://github.com/glpi-project/glpi/releases/download/{{ glpi_version }}/glpi-{{ glpi_version }}.tgz"
glpi_install_dir: "/var/www/glpi"
glpi_files_dir: "/var/lib/glpi" # Données hors webroot (sécurité)
glpi_log_dir: "/var/log/glpi"
# ============================================================
# Base de données MariaDB
# ============================================================
db_host: "localhost"
db_name: "glpidb"
db_user: "glpiuser"
db_password: "ChangeMe_S3cur3!" # À remplacer ou chiffrer avec ansible-vault
db_root_password: "R00t_ChangeMe!"
# ============================================================
# Apache / VirtualHost
# ============================================================
glpi_domain: "glpi.mondomaine.local"
glpi_admin_email: "admin@mondomaine.local"
apache_log_dir: "/var/log/apache2" # Debian
apache_log_dir_rh: "/var/log/httpd" # Rocky/RHEL
# ============================================================
# PHP
# ============================================================
# Stack PHP-FPM sur les deux distributions
# Debian 13 : PHP 8.4 — Rocky 10 : PHP 8.3
# PHP-FPM + proxy_fcgi : plus performant que mod_php, cohérent sur les deux distros
# ============================================================
# Système
# ============================================================
# Debian : www-data / Rocky : apache
apache_user: "{{ 'www-data' if ansible_facts['os_family'] == 'Debian' else 'apache' }}"
apache_group: "{{ 'www-data' if ansible_facts['os_family'] == 'Debian' else 'apache' }}"
# Socket PHP-FPM selon la distribution
php_fpm_socket: "{{ '/run/php/php8.4-fpm.sock' if ansible_facts['os_family'] == 'Debian' else '/run/php-fpm/www.sock' }}"
# ============================================================
# SSL — Certificat auto-signé
# ============================================================
ssl_cert_dir: "/etc/pki/tls/certs"
ssl_key_dir: "/etc/pki/tls/private"
ssl_cert: "{{ ssl_cert_dir }}/glpi.crt"
ssl_key: "{{ ssl_key_dir }}/glpi.key"
ssl_days: 3650Concept —
ansible-vaultpour les mots de passe :
Les mots de passe en clair dans un fichier versionné sous Git sont une faille de sécurité.ansible-vaultchiffre les valeurs sensibles avec AES-256.# Chiffrer une valeur ansible-vault encrypt_string 'ChangeMe_S3cur3!' --name 'db_password' # Lancer le playbook avec le vault ansible-playbook deploy_glpi.yml --ask-vault-pass
Concept — templates Jinja2 :
Ansible utilise le moteur de templates Jinja2 (le même que Flask/Django) pour générer des fichiers de configuration dynamiques. Les variables entre{{ }}sont remplacées par leurs valeurs au moment du déploiement. Les blocs{% if %}permettent des configurations conditionnelles selon la distribution. Un seul template couvre ainsi Debian et Rocky.
# Fichier généré par Ansible — ne pas modifier manuellement
# Template : templates/virtualhost.conf.j2
# Déployé le : {{ ansible_date_time.date }}
# Directives globales de sécurité (hors VirtualHost)
ServerTokens Prod
ServerSignature Off
# -------------------------------------------------------
# VirtualHost HTTP — redirection forcée vers HTTPS
# -------------------------------------------------------
<VirtualHost *:80>
ServerName {{ glpi_domain }}
RewriteEngine On
RewriteRule ^(.*)$ https://{{ glpi_domain }}$1 [R=301,L]
</VirtualHost>
# -------------------------------------------------------
# VirtualHost HTTPS — GLPI
# -------------------------------------------------------
<VirtualHost *:443>
ServerName {{ glpi_domain }}
ServerAdmin {{ glpi_admin_email }}
DocumentRoot {{ glpi_install_dir }}/public
# SSL
SSLEngine on
SSLCertificateFile {{ ssl_cert }}
SSLCertificateKeyFile {{ ssl_key }}
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
SSLCipherSuite HIGH:!aNULL:!MD5
<Directory {{ glpi_install_dir }}/public>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
# Routeur Symfony — toutes les requêtes vers index.php
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php [QSA,L]
</Directory>
# PHP-FPM via socket Unix
<FilesMatch \.php$>
SetHandler "proxy:unix:{{ php_fpm_socket }}|fcgi://localhost"
</FilesMatch>
# Logs
{% if ansible_facts['os_family'] == 'Debian' %}
ErrorLog {{ apache_log_dir }}/glpi_error.log
CustomLog {{ apache_log_dir }}/glpi_access.log combined
{% else %}
ErrorLog {{ apache_log_dir_rh }}/glpi_error.log
CustomLog {{ apache_log_dir_rh }}/glpi_access.log combined
{% endif %}
# Headers de sécurité
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-XSS-Protection "1; mode=block"
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
</VirtualHost>GLPI nécessite l'exécution régulière de tâches automatiques (notifications, inventaire, nettoyage) via son système de cron interne.
# Crontab GLPI — généré par Ansible
# Tâches automatiques : notifications, alertes, maintenance
# Exécuté en tant que : {{ apache_user }}
# Toutes les minutes — déclencheur principal des actions automatiques
* * * * * {{ apache_user }} php {{ glpi_install_dir }}/front/cron.php --force > /dev/null 2>&1
# Toutes les heures — nettoyage des sessions expirées
0 * * * * {{ apache_user }} php {{ glpi_install_dir }}/bin/console glpi:session:cleanup > /dev/null 2>&1
# Tous les jours à 2h — optimisation de la base de données
0 2 * * * {{ apache_user }} php {{ glpi_install_dir }}/bin/console glpi:database:optimize --no-interaction > /dev/null 2>&1---
- name: Déploiement GLPI — Apache + MariaDB + PHP
hosts: all_servers
become: true
gather_facts: true # Explicite : requis pour os_family avant toute tâche conditionnelle
vars_files:
- vars/main.yml
# ============================================================
# HANDLERS — déclenchés uniquement si une tâche notifie
# ============================================================
handlers:
- name: restart apache
ansible.builtin.service:
name: "{{ 'apache2' if ansible_facts['os_family'] == 'Debian' else 'httpd' }}"
state: restarted
- name: reload apache
ansible.builtin.service:
name: "{{ 'apache2' if ansible_facts['os_family'] == 'Debian' else 'httpd' }}"
state: reloaded
- name: restart mariadb
ansible.builtin.service:
name: mariadb
state: restarted
- name: restart php-fpm
ansible.builtin.service:
name: "{{ 'php8.4-fpm' if ansible_facts['os_family'] == 'Debian' else 'php-fpm' }}"
state: restarted
tasks:
# ----------------------------------------------------------
# ÉTAPE 1 — Collecte des facts système
# ----------------------------------------------------------
- name: Collecter les facts Ansible
ansible.builtin.setup:
gather_subset:
- distribution
- date_time
# ----------------------------------------------------------
# ÉTAPE 2 — Installation des paquets
# ----------------------------------------------------------
- name: "[Debian] Installer Apache, MariaDB, PHP et extensions"
ansible.builtin.apt:
name:
- apache2
- mariadb-server
- php
- php8.4-fpm
- php-mysql
- php-xml
- php-curl
- php-gd
- php-mbstring
- php-intl
- php-ldap
- php-zip
- php-bz2
- php-opcache
- php-bcmath
- python3-pymysql
- sudo
- tar
- curl
- acl
state: present
update_cache: yes
when: ansible_facts['os_family'] == 'Debian'
notify: restart apache
- name: "[Rocky] Installer Apache, MariaDB, PHP et extensions"
ansible.builtin.dnf:
name:
- httpd
- mariadb-server
- php
- php-fpm
- php-mysqlnd
- php-xml
- php-curl
- php-gd
- php-mbstring
- php-intl
- php-ldap
- php-zip
- php-bz2
- php-opcache
- php-bcmath
- python3-PyMySQL
- firewalld # ← Démon firewall (absent par défaut sur certaines images cloud)
- python3-firewall # ← FIX : requis par ansible.posix.firewalld
- sudo
- tar
- curl
- openssl
state: present
when: ansible_facts['os_family'] == 'RedHat'
notify: restart apache
# ----------------------------------------------------------
# ÉTAPE 3 — Activation des services
# ----------------------------------------------------------
- name: Activer et démarrer MariaDB
ansible.builtin.service:
name: mariadb
state: started
enabled: yes
ignore_errors: "{{ ansible_check_mode }}"
- name: "[Debian] Activer et démarrer Apache2"
ansible.builtin.service:
name: apache2
state: started
enabled: yes
when: ansible_facts['os_family'] == 'Debian'
- name: "[Debian] Activer et démarrer PHP-FPM"
ansible.builtin.service:
name: php8.4-fpm
state: started
enabled: yes
when: ansible_facts['os_family'] == 'Debian'
- name: "[Rocky] Activer et démarrer httpd"
ansible.builtin.service:
name: httpd
state: started
enabled: yes
when: ansible_facts['os_family'] == 'RedHat'
- name: "[Rocky] Activer et démarrer PHP-FPM"
ansible.builtin.service:
name: php-fpm
state: started
enabled: yes
when: ansible_facts['os_family'] == 'RedHat'
# ----------------------------------------------------------
# ÉTAPE 4 — Modules Apache
# ----------------------------------------------------------
- name: "[Debian] Activer les modules Apache nécessaires"
community.general.apache2_module:
name: "{{ item }}"
state: present
loop:
- rewrite
- proxy_fcgi
- setenvif
- headers
notify: restart apache
when: ansible_facts['os_family'] == 'Debian'
- name: "[Debian] Activer la configuration PHP-FPM dans Apache"
ansible.builtin.command:
cmd: a2enconf php8.4-fpm
notify: restart apache
when: ansible_facts['os_family'] == 'Debian'
changed_when: true
- name: "[Debian] Activer le module SSL Apache"
community.general.apache2_module:
name: ssl
state: present
notify: restart apache
when: ansible_facts['os_family'] == 'Debian'
- name: "Configurer session.cookie_secure dans PHP-FPM (HTTPS)"
ansible.builtin.lineinfile:
path: "{{ '/etc/php/8.4/fpm/php.ini' if ansible_facts['os_family'] == 'Debian' else '/etc/php.ini' }}"
regexp: '^;?session.cookie_secure'
line: 'session.cookie_secure = On'
notify: restart php-fpm
- name: "Configurer session.cookie_httponly dans PHP-FPM"
ansible.builtin.lineinfile:
path: "{{ '/etc/php/8.4/fpm/php.ini' if ansible_facts['os_family'] == 'Debian' else '/etc/php.ini' }}"
regexp: '^;?session.cookie_httponly'
line: 'session.cookie_httponly = On'
notify: restart php-fpm
- name: "Configurer session.cookie_samesite dans PHP-FPM"
ansible.builtin.lineinfile:
path: "{{ '/etc/php/8.4/fpm/php.ini' if ansible_facts['os_family'] == 'Debian' else '/etc/php.ini' }}"
regexp: '^;?session.cookie_samesite'
line: 'session.cookie_samesite = Lax'
notify: restart php-fpm
- name: "[Rocky] Installer mod_ssl"
ansible.builtin.dnf:
name: mod_ssl
state: present
when: ansible_facts['os_family'] == 'RedHat'
notify: restart apache
- name: "Créer les répertoires SSL"
ansible.builtin.file:
path: "{{ item }}"
state: directory
mode: '0755'
loop:
- "{{ ssl_cert_dir }}"
- "{{ ssl_key_dir }}"
- name: "Générer le certificat SSL auto-signé ({{ ssl_days }} jours)"
ansible.builtin.command:
cmd: >
openssl req -x509 -nodes
-days {{ ssl_days }}
-newkey rsa:2048
-keyout {{ ssl_key }}
-out {{ ssl_cert }}
-subj "/C=FR/ST=Local/L=Local/O=GLPI/CN={{ glpi_domain }}"
creates: "{{ ssl_cert }}"
notify: restart apache
- name: "Sécuriser la clé privée SSL"
ansible.builtin.file:
path: "{{ ssl_key }}"
mode: '0600'
# ----------------------------------------------------------
# ÉTAPE 5 — Firewall (Rocky uniquement)
# ← FIX : firewalld + python3-firewall installés à l'étape 2.
# Le service doit être démarré avant d'appliquer les règles.
# Le guard `when: os_family == 'RedHat'` évite tout appel
# sur les hôtes Debian (debian13-glpi ne jouera plus ces tâches).
# ----------------------------------------------------------
- name: "[Rocky] Activer et démarrer firewalld"
ansible.builtin.service:
name: firewalld
state: started
enabled: yes
when: ansible_facts['os_family'] == 'RedHat'
- name: "[Rocky] Ouvrir le port 80 dans firewalld"
ansible.posix.firewalld:
service: http
permanent: yes
state: enabled
immediate: yes
when: ansible_facts['os_family'] == 'RedHat'
- name: "[Rocky] Ouvrir le port 443 dans firewalld"
ansible.posix.firewalld:
service: https
permanent: yes
state: enabled
immediate: yes
when: ansible_facts['os_family'] == 'RedHat'
# ----------------------------------------------------------
# ÉTAPE 6 — Sécurisation MariaDB
# ----------------------------------------------------------
- name: Définir le mot de passe root MariaDB
community.mysql.mysql_user:
name: root
host: localhost
password: "{{ db_root_password }}"
login_unix_socket: "{{ '/var/run/mysqld/mysqld.sock' if ansible_facts['os_family'] == 'Debian' else '/var/lib/mysql/mysql.sock' }}"
state: present
- name: Supprimer les utilisateurs anonymes MariaDB
community.mysql.mysql_user:
name: ''
host_all: yes
state: absent
login_user: root
login_password: "{{ db_root_password }}"
login_unix_socket: "{{ '/var/run/mysqld/mysqld.sock' if ansible_facts['os_family'] == 'Debian' else '/var/lib/mysql/mysql.sock' }}"
- name: Supprimer la base de test MariaDB
community.mysql.mysql_db:
name: test
state: absent
login_user: root
login_password: "{{ db_root_password }}"
login_unix_socket: "{{ '/var/run/mysqld/mysqld.sock' if ansible_facts['os_family'] == 'Debian' else '/var/lib/mysql/mysql.sock' }}"
# ----------------------------------------------------------
# ÉTAPE 7 — Création de la base de données GLPI
# ----------------------------------------------------------
- name: Créer la base de données GLPI
community.mysql.mysql_db:
name: "{{ db_name }}"
encoding: utf8mb4
collation: utf8mb4_unicode_ci
state: present
login_user: root
login_password: "{{ db_root_password }}"
- name: Créer l'utilisateur MariaDB pour GLPI
community.mysql.mysql_user:
name: "{{ db_user }}"
password: "{{ db_password }}"
priv: "{{ db_name }}.*:ALL"
host: localhost
state: present
login_user: root
login_password: "{{ db_root_password }}"
# ----------------------------------------------------------
# ÉTAPE 8 — Téléchargement et déploiement de GLPI
# ----------------------------------------------------------
- name: Créer les répertoires GLPI
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: "{{ apache_user }}"
group: "{{ apache_group }}"
mode: '0755'
loop:
- "{{ glpi_install_dir }}"
- "{{ glpi_files_dir }}"
- "{{ glpi_log_dir }}"
- name: Télécharger l'archive GLPI {{ glpi_version }}
ansible.builtin.get_url:
url: "{{ glpi_archive_url }}"
dest: "/tmp/glpi-{{ glpi_version }}.tgz"
mode: '0644'
- name: Extraire l'archive GLPI
ansible.builtin.unarchive:
src: "/tmp/glpi-{{ glpi_version }}.tgz"
dest: "/var/www/"
remote_src: yes
owner: "{{ apache_user }}"
group: "{{ apache_group }}"
creates: "{{ glpi_install_dir }}/index.php"
- name: Appliquer les permissions sur les répertoires GLPI
ansible.builtin.file:
path: "{{ item }}"
owner: "{{ apache_user }}"
group: "{{ apache_group }}"
recurse: yes
loop:
- "{{ glpi_install_dir }}"
- "{{ glpi_files_dir }}"
- "{{ glpi_log_dir }}"
# ----------------------------------------------------------
# ÉTAPE 9 — Configuration downstream.php (chemins sécurisés)
# ----------------------------------------------------------
- name: Créer le fichier downstream.php pour sécuriser les chemins
ansible.builtin.copy:
dest: "{{ glpi_install_dir }}/config/downstream.php"
owner: "{{ apache_user }}"
group: "{{ apache_group }}"
mode: '0644'
content: |
<?php
define('GLPI_VAR_DIR', '{{ glpi_files_dir }}');
define('GLPI_LOG_DIR', '{{ glpi_log_dir }}');
define('GLPI_CACHE_DIR', '{{ glpi_files_dir }}/_cache');
- name: Créer les sous-répertoires GLPI dans files_dir
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: "{{ apache_user }}"
group: "{{ apache_group }}"
mode: '0755'
loop:
- "{{ glpi_files_dir }}/_cache"
- "{{ glpi_files_dir }}/_cron"
- "{{ glpi_files_dir }}/_dumps"
- "{{ glpi_files_dir }}/_graphs"
- "{{ glpi_files_dir }}/_lock"
- "{{ glpi_files_dir }}/_pictures"
- "{{ glpi_files_dir }}/_plugins"
- "{{ glpi_files_dir }}/_rss"
- "{{ glpi_files_dir }}/_sessions"
- "{{ glpi_files_dir }}/_tmp"
- "{{ glpi_files_dir }}/_uploads"
- "{{ glpi_files_dir }}/_log"
- name: Supprimer le répertoire files de l'archive (remplacé par un lien)
ansible.builtin.file:
path: "{{ glpi_install_dir }}/files"
state: absent
- name: Créer le lien symbolique files → glpi_files_dir dans le webroot
ansible.builtin.file:
src: "{{ glpi_files_dir }}"
dest: "{{ glpi_install_dir }}/files"
state: link
owner: "{{ apache_user }}"
group: "{{ apache_group }}"
force: yes
# ----------------------------------------------------------
# ÉTAPE 10 — Déploiement du VirtualHost via template Jinja2
# ----------------------------------------------------------
- name: "[Debian] Déployer le VirtualHost Apache depuis le template"
ansible.builtin.template:
src: templates/virtualhost.conf.j2
dest: "/etc/apache2/sites-available/glpi.conf"
owner: root
group: root
mode: '0644'
notify: reload apache
when: ansible_facts['os_family'] == 'Debian'
- name: "[Rocky] Déployer le VirtualHost Apache depuis le template"
ansible.builtin.template:
src: templates/virtualhost.conf.j2
dest: "/etc/httpd/conf.d/glpi.conf"
owner: root
group: root
mode: '0644'
notify: reload apache
when: ansible_facts['os_family'] == 'RedHat'
- name: "[Debian] Activer le site GLPI (a2ensite)"
ansible.builtin.command:
cmd: a2ensite glpi.conf
notify: reload apache
when: ansible_facts['os_family'] == 'Debian'
changed_when: true
- name: "[Debian] Désactiver le site par défaut Apache"
ansible.builtin.command:
cmd: a2dissite 000-default.conf
notify: reload apache
when: ansible_facts['os_family'] == 'Debian'
changed_when: true
# ----------------------------------------------------------
# ÉTAPE 11 — Crontab GLPI
# ----------------------------------------------------------
- name: Déployer la crontab GLPI depuis le template
ansible.builtin.template:
src: templates/glpi-cron.j2
dest: /etc/cron.d/glpi
owner: root
group: root
mode: '0644'
# ----------------------------------------------------------
# ÉTAPE 12 — SELinux (Rocky Linux uniquement)
# ----------------------------------------------------------
- name: "[Rocky] Autoriser Apache à se connecter à la BDD (SELinux)"
ansible.posix.seboolean:
name: httpd_can_network_connect_db
state: yes
persistent: yes
when: ansible_facts['os_family'] == 'RedHat'
- name: "[Rocky] Autoriser Apache à lire/écrire dans le webroot (SELinux)"
community.general.sefcontext:
target: "{{ glpi_install_dir }}(/.*)?"
setype: httpd_sys_rw_content_t
state: present
when: ansible_facts['os_family'] == 'RedHat'
- name: "[Rocky] Appliquer le contexte SELinux"
ansible.builtin.command:
cmd: "restorecon -Rv {{ glpi_install_dir }}"
when: ansible_facts['os_family'] == 'RedHat'
changed_when: true
# ----------------------------------------------------------
# ÉTAPE 13 — Installation CLI de GLPI (sans wizard web)
# ----------------------------------------------------------
- name: Vérifier si GLPI est déjà installé
ansible.builtin.stat:
path: "{{ glpi_install_dir }}/config/config_db.php"
register: glpi_config
- name: Vérifier les prérequis PHP pour GLPI
ansible.builtin.command:
cmd: php {{ glpi_install_dir }}/bin/console system:check_requirements --allow-superuser
register: glpi_requirements
failed_when: false
changed_when: false
- name: Afficher les prérequis PHP
ansible.builtin.debug:
msg: "{{ glpi_requirements.stdout_lines }}"
- name: Installer GLPI via la CLI (si pas encore installé)
ansible.builtin.command:
cmd: >
php {{ glpi_install_dir }}/bin/console db:install
--db-host={{ db_host }}
--db-name={{ db_name }}
--db-user={{ db_user }}
--db-password={{ db_password }}
--default-language=fr_FR
--no-interaction
--allow-superuser
environment:
GLPI_VAR_DIR: "{{ glpi_files_dir }}"
GLPI_LOG_DIR: "{{ glpi_log_dir }}"
when: not glpi_config.stat.exists
register: glpi_install_result
- name: Afficher le résultat de l'installation GLPI
ansible.builtin.debug:
msg: "{{ glpi_install_result.stdout_lines }}"
when: not glpi_config.stat.exists
- name: Définir l'URL de base GLPI en base de données
community.mysql.mysql_query:
login_user: "{{ db_user }}"
login_password: "{{ db_password }}"
login_db: "{{ db_name }}"
query: "UPDATE glpi_configs SET value='https://{{ glpi_domain }}' WHERE name='url_base'"
when: not glpi_config.stat.exists
- name: Corriger les permissions après installation (fichiers créés par root)
ansible.builtin.file:
path: "{{ item }}"
owner: "{{ apache_user }}"
group: "{{ apache_group }}"
recurse: yes
loop:
- "{{ glpi_files_dir }}"
- "{{ glpi_log_dir }}"
- "{{ glpi_install_dir }}/config"
when: not glpi_config.stat.exists
- name: Vider le cache GLPI après installation
ansible.builtin.command:
cmd: php {{ glpi_install_dir }}/bin/console cache:clear
become_user: "{{ apache_user }}"
when: not glpi_config.stat.exists
changed_when: true
# ----------------------------------------------------------
# ÉTAPE 14 — Sécurisation post-installation
# ----------------------------------------------------------
- name: Supprimer le répertoire d'installation GLPI (sécurité)
ansible.builtin.file:
path: "{{ glpi_install_dir }}/install"
state: absent
- name: Afficher l'URL d'accès GLPI
ansible.builtin.debug:
msg:
- "✅ GLPI {{ glpi_version }} déployé avec succès !"
- "🌐 URL : https://{{ glpi_domain }}"
- "👤 Identifiants par défaut : glpi / glpi"
- "⚠️ Changer les mots de passe par défaut immédiatement !"Concept — handlers :
Un handler est une tâche déclenchée uniquement si une autre tâche l'a notifiée vianotify:et si cette tâche a réellement effectué un changement (changed). Cela évite de redémarrer Apache inutilement à chaque exécution. Les handlers s'exécutent une seule fois à la fin du play, même s'ils sont notifiés plusieurs fois.
Concept — idempotence avec
creates:etstat::
creates:surunarchiveindique qu'Ansible ne ré-extrait pas si le fichier cible existe déjà.stat:+register+when: not x.stat.existspermet de conditionner une tâche à l'absence d'un fichier. Ces patterns garantissent qu'on peut relancer le playbook sans tout écraser.
# Vérifier la syntaxe avant tout
ansible-playbook deploy_glpi.yml -i inventory.ini --syntax-check
# Simuler sans rien modifier (dry-run)
ansible-playbook deploy_glpi.yml -i inventory.ini --check
# Déploiement réel sur tous les serveurs
ansible-playbook deploy_glpi.yml -i inventory.ini --ask-vault-pass
# -------------------------------------------------------
# Cibler uniquement Debian 13
# -------------------------------------------------------
ansible-playbook deploy_glpi.yml -i inventory.ini --ask-vault-pass \
--limit debian_servers
# Ou par nom d'hôte
ansible-playbook deploy_glpi.yml -i inventory.ini --ask-vault-pass \
--limit debian13-glpi
# -------------------------------------------------------
# Cibler uniquement Rocky Linux 10
# -------------------------------------------------------
ansible-playbook deploy_glpi.yml -i inventory.ini --ask-vault-pass \
--limit rocky_servers
# Ou par nom d'hôte
ansible-playbook deploy_glpi.yml -i inventory.ini --ask-vault-pass \
--limit rocky10-glpi
# -------------------------------------------------------
# Options utiles
# -------------------------------------------------------
# Relancer uniquement à partir d'une étape échouée
ansible-playbook deploy_glpi.yml -i inventory.ini --ask-vault-pass \
--start-at-task "Créer la base de données GLPI"
# Verbose pour déboguer
ansible-playbook deploy_glpi.yml -i inventory.ini -vvvConcept —
--limit:
--limitrestreint l'exécution à un sous-ensemble de l'inventaire. On peut passer un nom d'hôte, un groupe (debian_servers,rocky_servers), ou une combinaison (--limit "debian_servers,rocky10-glpi"). C'est indispensable pour déployer sur un seul nœud sans toucher les autres.
Concept —
--checkvs exécution réelle :
Le mode--checkest un dry-run : Ansible simule chaque tâche et indique ce qui serait modifié sans toucher au système. Certaines tâches (command,shell) ne peuvent pas être simulées et sont marquéesskippeden mode check. C'est un filet de sécurité précieux avant un déploiement en production.
[debian_servers]
debian13-glpi ansible_host=192.168.1.10 ansible_user=admin
[rocky_servers]
rocky10-glpi ansible_host=192.168.1.20 ansible_user=rocky
[all_servers:children]
debian_servers
rocky_servers
[all_servers:vars]
ansible_python_interpreter=/usr/bin/python3
ansible_ssh_private_key_file=~/.ssh/id_ed25519Ce playbook utilise des modules de collections tierces. À installer avant le premier run :
# Installer les collections nécessaires
ansible-galaxy collection install community.mysql
ansible-galaxy collection install community.general
ansible-galaxy collection install ansible.posix
# Ou via un fichier requirements.yml
cat > requirements.yml << 'EOF'
collections:
- name: community.mysql
- name: community.general
- name: ansible.posix
EOF
ansible-galaxy collection install -r requirements.ymlConcept — collections Ansible Galaxy :
Ansible Galaxy est le dépôt officiel de roles et collections communautaires, comparable à PyPI pour Python ou npm pour Node.js. Les collections regroupent des modules, plugins et roles autour d'un domaine (icicommunity.mysqlpour tout ce qui concerne MySQL/MariaDB). Les modulesbuiltin(préfixeansible.builtin) sont inclus nativement sans installation.
| Compte | Login | Mot de passe par défaut |
|---|---|---|
| Super-Admin | glpi |
glpi |
| Admin | tech |
tech |
| Post-only | post-only |
postonly |
| Normal | normal |
normal |
⚠️ Changer tous ces mots de passe immédiatement après la première connexion.
- Documentation GLPI
- GLPI sur GitHub
- Documentation modules Ansible builtin
- Collection community.mysql
- Jinja2 — documentation
- ansible-vault