ITSkillsCenter
Blog

Event-Driven Ansible pas à pas — réagir à un webhook ou une alerte

13 min de lecture

📍 Article principal : Ansible 2026 : la stack pratique pour automatiser Linux et Windows

Vos rôles sont testés, vos serveurs durcis. On change maintenant la nature même de l’automatisation : au lieu de lancer des playbooks manuellement, on déclare des règles qui les déclenchent automatiquement quand un événement survient.

Ce que vous aurez à la fin

Un service ansible-rulebook qui écoute un webhook HTTP, applique des règles à chaque événement reçu, et déclenche un playbook ciblé selon des conditions précises. Le cas d’usage qu’on construit : Prometheus Alertmanager émet une alerte de saturation disque sur un serveur web — le rulebook reçoit le webhook, identifie la cible, lance un playbook de purge des anciens logs et notifie l’équipe via Slack. Tout cela en moins de trente secondes, sans intervention humaine.

Prérequis

  • ansible-core 2.20 et un projet structuré en rôles, idéalement avec inventaire dynamique.
  • Python 3.11 minimum sur la machine qui hébergera ansible-rulebook.
  • Java 17+ installé — ansible-rulebook embarque Drools (moteur de règles Java) en arrière-plan.
  • Niveau attendu : avoir manipulé webhooks et endpoints HTTP, lu une fois la documentation rulebook.
  • Temps : 90 minutes en lecture active.

Étape 1 — Le modèle EDA en trois objets

Event-Driven Ansible repose sur trois primitives qu’il faut tenir en tête avant la première ligne de code.

Le premier objet est la source d’événements. C’est un plugin Python qui produit un flux d’événements ; chaque source connaît son protocole d’arrivée — webhook HTTP, topic Kafka, polling AWS CloudWatch, alertes Alertmanager, journaux Loki, fichier surveillé. Le projet officiel ansible.eda en livrait une dizaine, et beaucoup ont migré comme builtins du paquet ansible-rulebook lui-même (notamment eda.builtin.webhook). On les invoque depuis un rulebook.

Le deuxième objet est la condition. Chaque règle dans un rulebook a une condition qui filtre les événements pertinents. La syntaxe est inspirée des langages de moteur de règles : on peut tester l’égalité d’un champ, sa correspondance avec une regex, sa présence ou son absence, et combiner avec and, or, not.

Le troisième objet est l’action. Quand une condition matche, l’action s’exécute. Les actions courantes sont run_playbook (lance un playbook avec le contexte de l’événement), run_module (un module unique), print_event (utile pour le debug), set_fact et post_event (pour des chaînages internes), shutdown (arrête le rulebook proprement).

Étape 2 — Installer ansible-rulebook

L’installation se fait via pipx, comme pour ansible-core. Java 17 doit être installé d’abord — sur Debian/Ubuntu :

sudo apt update
sudo apt install -y openjdk-17-jdk-headless
java -version

On installe explicitement OpenJDK 17 (le minimum requis par ansible-rulebook) plutôt que default-jdk-headless qui pointe vers la version par défaut de la distribution — sur Ubuntu 24.04 c’est OpenJDK 21, qui fonctionne aussi mais consomme plus de mémoire au démarrage. La sortie de java -version doit afficher openjdk version "17.0.x" ou supérieur. On installe ensuite ansible-rulebook et la collection EDA officielle :

pipx install ansible-rulebook
pipx inject ansible-rulebook ansible-core
ansible-galaxy collection install ansible.eda

L’injection d’ansible-core dans l’environnement d’ansible-rulebook est ce qui permet aux actions de type run_playbook et run_module de fonctionner — sans elle, le rulebook reçoit les événements mais ne sait pas exécuter de playbook.

Vérification :

ansible-rulebook --version

La version 1.2.x (1.2.2 en mars 2026) est la cible. Si la commande échoue avec un message JAVA_HOME not set, on l’expose explicitement : export JAVA_HOME=$(dirname $(dirname $(readlink -f $(which java)))).

Étape 3 — Le premier rulebook

On crée un répertoire rulebooks/ à la racine du projet Ansible et on y dépose disk-pressure.yml :

---
- name: Réagir aux alertes Alertmanager de saturation disque
  hosts: all
  sources:
    - name: webhook Alertmanager
      eda.builtin.webhook:
        host: 0.0.0.0
        port: 5000

  rules:
    - name: Alerte DiskFull -> purge des logs
      condition: event.payload.alerts is defined and event.payload.alerts[0].labels.alertname == "DiskFull"
      action:
        run_playbook:
          name: playbooks/purge-logs.yml
          extra_vars:
            target_host: "{{ event.payload.alerts[0].labels.instance }}"

    - name: Alerte ServiceDown -> tentative de restart
      condition: event.payload.alerts is defined and event.payload.alerts[0].labels.alertname == "ServiceDown"
      action:
        run_playbook:
          name: playbooks/restart-service.yml
          extra_vars:
            target_host: "{{ event.payload.alerts[0].labels.instance }}"
            service_name: "{{ event.payload.alerts[0].labels.service }}"

Lisons ce fichier. La directive sources liste les sources d’événements ; ici, une seule, le plugin webhook qui ouvre un serveur HTTP sur le port 5000. La directive rules liste les règles ; chaque règle a une condition Jinja-style qui filtre l’événement, et une action qui s’exécute si la condition est vraie.

La syntaxe event.payload.alerts[0].labels.alertname == "DiskFull" reflète la structure d’un payload Alertmanager : un objet JSON avec une liste alerts, chacune ayant des labels. C’est le moteur Drools sous-jacent qui parse cette expression et la matche contre le payload reçu.

Étape 4 — Les playbooks réactifs

Le rulebook ne fait qu’orchestrer. Le travail réel est dans les playbooks qu’il déclenche. playbooks/purge-logs.yml :

---
- name: Purger les anciens logs sur la cible alertée
  hosts: "{{ target_host | default('localhost') }}"
  become: true
  tasks:
    - name: Vérifier que la cible existe dans l'inventaire
      ansible.builtin.assert:
        that: target_host is defined
        fail_msg: "target_host n'a pas été passé par le rulebook"

    - name: Trouver les logs gzippés de plus de 14 jours
      ansible.builtin.find:
        paths: /var/log
        patterns: "*.gz"
        age: "14d"
        recurse: true
      register: old_logs

    - name: Supprimer les logs trouvés
      ansible.builtin.file:
        path: "{{ item.path }}"
        state: absent
      loop: "{{ old_logs.files }}"
      loop_control:
        label: "{{ item.path }}"

    - name: Vacuum journal systemd
      ansible.builtin.command:
        cmd: journalctl --vacuum-time=14d
      changed_when: false

    - name: Notifier Slack (succès)
      ansible.builtin.uri:
        url: "{{ slack_webhook_url }}"
        method: POST
        body_format: json
        body:
          text: ":broom: Purge logs sur *{{ target_host }}* terminée — {{ old_logs.matched }} fichiers supprimés."
        status_code: 200

Trois patterns à noter. La directive hosts: "{{ target_host | default('localhost') }}" rend le playbook utilisable manuellement aussi (sur localhost) si jamais on veut le tester. L’assert en première tâche échoue tôt si la variable n’a pas été passée — éviter de courir le playbook entier avant de découvrir un bug d’orchestration. La notification Slack est une URL Webhook chiffrée par Vault, jamais en dur.

Étape 5 — Lancer le rulebook

On exécute :

ansible-rulebook --rulebook rulebooks/disk-pressure.yml \
  --inventory inventory/ \
  --vault-password-file ~/.ansible/vault-pass-dev \
  -v

Le drapeau -v affiche le démarrage de la source webhook, l’écoute sur le port 5000, et l’attente d’événements. Pour tester, on simule un payload Alertmanager depuis un autre terminal :

curl -X POST http://localhost:5000/endpoint \
  -H "Content-Type: application/json" \
  -d '{
    "alerts": [{
      "labels": {
        "alertname": "DiskFull",
        "instance": "web-fra1"
      },
      "status": "firing"
    }]
  }'

Le rulebook reçoit l’événement, matche la première règle, lance purge-logs.yml avec target_host=web-fra1. La sortie affiche en temps réel le déclenchement, l’exécution du playbook, le résultat. Tout cela sans intervention humaine.

Étape 6 — Configurer Alertmanager pour pousser vers le rulebook

L’autre moitié du dispositif vit côté monitoring. Dans la configuration Alertmanager (alertmanager.yml), on ajoute un receiver de type webhook :

route:
  receiver: 'eda'
  group_by: ['alertname', 'instance']
  routes:
    - match:
        alertname: 'DiskFull'
      receiver: 'eda'
    - match:
        alertname: 'ServiceDown'
      receiver: 'eda'

receivers:
  - name: 'eda'
    webhook_configs:
      - url: 'http://eda-host.internal:5000/endpoint'
        send_resolved: false

L’option send_resolved: false est importante pour ce premier tour — Alertmanager n’envoie que les événements de déclenchement, pas de résolution. Sinon, à chaque retour à la normale, le rulebook recevrait un événement de fin et pourrait déclencher des actions parasites si les conditions ne discriminent pas le statut.

Étape 7 — Plusieurs sources et routage

La force d’EDA arrive quand plusieurs sources cohabitent dans le même rulebook. Un rulebook peut écouter à la fois un webhook Alertmanager et une source générique qui consomme un flux JSON arbitraire :

sources:
  - name: webhook Alertmanager
    eda.builtin.webhook:
      host: 0.0.0.0
      port: 5000
  - name: générateur de tests
    eda.builtin.generic:
      payload:
        - alertname: SyntheticHealth
          source: probe
      loop_delay: 60
  - name: notifications PostgreSQL
    eda.builtin.pg_listener:
      dsn: "postgresql://eda@db.internal/events"
      channels:
        - eda_events

Les règles peuvent ensuite filtrer par source d’origine via event.meta.source.name == "notifications PostgreSQL". Les sources builtin couvertes officiellement par ansible-rulebook en 2026 sont webhook, generic, range et pg_listener. Pour les autres canaux (Kafka, AWS CloudTrail, Azure Service Bus, surveillance de fichier, journald), on installe la collection externe correspondante — ansible.eda a éclaté ces plugins en collections dédiées (ansible.eda.kafka est devenu un plugin distribué via la collection thématique correspondante, à vérifier sur Galaxy au moment de l’install).

Étape 8 — Faire tourner ansible-rulebook en service systemd

Pour la production, on encapsule ansible-rulebook dans une unité systemd avec les protections habituelles. /etc/systemd/system/ansible-rulebook.service :

[Unit]
Description=Event-Driven Ansible Rulebook
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=eda
Group=eda
WorkingDirectory=/opt/eda
Environment=ANSIBLE_VAULT_PASSWORD_FILE=/etc/eda/vault-pass
ExecStart=/usr/local/bin/ansible-rulebook --rulebook rulebooks/disk-pressure.yml --inventory inventory/ -v
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
SyslogIdentifier=ansible-rulebook

NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ReadWritePaths=/opt/eda /tmp

[Install]
WantedBy=multi-user.target

Le déploiement de cette unité est lui-même un playbook Ansible — la boucle est bouclée. On crée un rôle eda_runner qui installe Java, ansible-rulebook, déploie le rulebook, dépose la passphrase Vault, configure systemd. Le rulebook tournant lance ensuite des playbooks qui peuvent reconfigurer le rulebook lui-même si on est prudent.

Étape 9 — Sécurité du dispositif

Un endpoint webhook ouvert au monde est une cible. Trois précautions sont non négociables.

Ne pas exposer publiquement. Le port 5000 du rulebook reste sur un réseau privé ou est accessible uniquement via un load balancer interne. L’unité systemd peut binder sur l’IP interne plutôt que 0.0.0.0 : host: 10.0.0.42.

Authentification de l’appelant. Le plugin webhook officiel supporte un token partagé que l’appelant doit fournir. Dans le rulebook :

sources:
  - name: webhook
    ansible.eda.webhook:
      host: 0.0.0.0
      port: 5000
      token: "{{ webhook_shared_secret }}"

L’appelant inclut alors un en-tête Authorization: Bearer <token>. Côté Alertmanager, on l’ajoute via http_config.

Rate limiting. Un load balancer en frontal (HAProxy, Caddy, Nginx) limite le débit de requêtes vers le webhook. Sans cela, un attaquant qui découvre l’endpoint peut le saturer.

Étape 10 — Audit et observabilité du dispositif

Un système événementiel autonome qui prend des décisions sans humain doit laisser une piste d’audit complète. Trois niveaux de log sont attendus en production.

Le journal du rulebook. Avec StandardOutput=journal dans l’unité systemd, on récupère tout ce que ansible-rulebook écrit sur stdout via journalctl -u ansible-rulebook. Pour chaque événement reçu, on voit l’origine, le payload partiel (champs pertinents), la règle qui a matché et l’action lancée.

Le journal des playbooks déclenchés. Chaque run_playbook produit une sortie ansible standard. On la capture en activant le callback json ou yaml via la variable d’environnement ANSIBLE_STDOUT_CALLBACK dans l’unité, et on redirige vers un répertoire d’archive. Pour la traçabilité forensique, on conserve ces fichiers 90 jours minimum.

L’export vers un SIEM. En entreprise, on pousse les événements et les actions vers un SIEM (Wazuh, Splunk, Loki/Grafana). La méthode la plus directe consiste à ajouter une règle print_event en parallèle des règles d’action, qui écrit chaque événement dans un fichier que Filebeat ou un agent SIEM ramasse. On conserve ainsi la chaîne complète : alerte d’origine → décision EDA → action exécutée → résultat.

Erreurs fréquentes

Erreur Cause Solution
JAVA_HOME not set Java non installé ou variable absente apt install default-jdk-headless puis exporter la variable
Webhook reçu mais aucune règle ne matche Structure du payload différente de la condition Activer print_event comme première action de debug pour voir la structure réelle
Playbook lancé mais target_host vide Champ Alertmanager non passé en extra_vars Vérifier que extra_vars consomme la bonne clé (labels.instance, pas juste instance)
Le rulebook tombe sur OOMKilled Drools accumule les facts sans purge Ajouter throttle ou utiliser retract_fact dans les règles à haute fréquence
Slack notification échoue silencieusement slack_webhook_url non chiffré ou mal référencé Stocker dans Vault, charger via vars_files du playbook

Tutoriels frères

Sur un angle proche

FAQ

EDA remplace-t-il Ansible Tower ou AWX ?
Non. AWX est une interface de gestion (UI, RBAC, scheduling) au-dessus d’Ansible. EDA est un mécanisme de déclenchement événementiel. Les deux peuvent cohabiter : AWX gère les jobs manuels et planifiés, EDA déclenche d’autres jobs sur événements. La page Red Hat sur EDA détaille l’intégration des deux.

Pourquoi Drools pour le moteur de règles ?
Drools apporte un moteur de pattern matching haute performance hérité des années 90, capable de gérer des milliers d’événements par seconde et des règles avec dépendances temporelles complexes. Le coût est l’introduction de Java dans la stack Python, accepté par le projet pour la robustesse.

Comment tester un rulebook avant de le mettre en prod ?
On le lance localement avec ansible-rulebook --print-events qui affiche les événements reçus sans déclencher d’actions. Une fois la condition validée, on remplace la directive action par un print_event temporaire pour valider le routage. Ensuite seulement on bascule sur run_playbook.

Que se passe-t-il si plusieurs événements arrivent en même temps ?
Les événements sont traités en file d’attente par le moteur Drools. Une règle à throttle: 10 ignore les déclenchements supplémentaires pendant dix secondes après le premier match — utile pour éviter de lancer le même playbook quinze fois sur une rafale d’alertes identiques.

Peut-on utiliser EDA sans Red Hat AAP ?
Oui. ansible-rulebook est open source, distribué via PyPI, fonctionne en standalone. AAP apporte une interface web, un planificateur, des intégrations supplémentaires, mais n’est pas nécessaire pour démarrer.

EDA est-il adapté à un parc de moins de dix serveurs ?
Le bénéfice est plus limité, mais valide. Sur un petit parc, l’humain peut intervenir rapidement. EDA brille quand le volume d’événements ou la fréquence empêchent la réaction humaine — typiquement une centaine de serveurs et plus, ou des micro-services à forte densité d’alertes.

Sponsoriser ce contenu

Cet emplacement est à vous

Position premium en fin d'article — c'est l'instant où les lecteurs sont le plus engagés. Réservez cet espace pour votre marque, votre formation ou votre offre.

Recevoir nos tarifs
Publicité