In my previous blogs about Ansible, I covered a few use cases:

All these still require a manual trigger to run. With Ansible Event Driven (AED), we can be pro-active by running playbooks automatically based on events.

Installation

To install, it is just a few commands:

  • Install pip3 and a JDK:
 sudo apt install python3-pip openjdk-17-jdk maven
  • Set variables (or add them in ~/.bash_profile to make them permanent):
export PATH=$PATH:$HOME/.local/bin
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
export PIP_NO_BINARY=jpy
export PATH=/home/vagrant/.local/bin:$PATH
  • lnstall required Python packages:
pip install wheel ansible-rulebook ansible ansible-runner
  • Finally, install eda collection:
ansible-galaxy collection install community.general ansible.eda

Now, we are ready to run our first rulebook.

Rulebook

AED introduce a new tool as well as a new type of file: The rulebook which is associated with ansible-rulebook command. The rulebook is a yaml file, so we are in a known environment.

This file consists of three sections:

  • Sources: It defined the event used as input.
  • Rules: A filter on event to determine when to apply actions.
  • Actions: Playbooks, modules or tasks.

Here is simple example from the official GitHub repository with a webhook:

---
- name: Listen for events on a webhook
  hosts: all

  ## Define our source for events

  sources:
    - ansible.eda.webhook:
        host: 0.0.0.0
        port: 5000

  ## Define the conditions we are looking for

  rules:
    - name: Say Hello
      condition: event.payload.message == "Ansible is super cool"

  ## Define the action we should take should the condition be met

      action:
        run_playbook:
          name: say-what.yml

Create a simple inventory (for testing purpose only):

all:
  hosts:
    localhost:
      ansible_connection: local

As well as the playbook (say-what.yml) we will run:

- hosts: localhost
  connection: local
  tasks:
    - debug:
        msg: "Thank you, my friend!"

To run it (with verbosity so we can see something):

ansible-rulebook --rulebook webhook.yml -i inventory.yml --verbose

Output:

2023-02-03 16:15:30,048 - ansible_rulebook.app - INFO - Starting sources
2023-02-03 16:15:30,048 - ansible_rulebook.app - INFO - Starting rules
2023-02-03 16:15:30,048 - ansible_rulebook.engine - INFO - run_ruleset
2023-02-03 16:15:30,791 - ansible_rulebook.engine - INFO - ruleset define: {"name": "Listen for events on a webhook", "hosts": ["all"], "sources": [{"EventSource": {"name": "ansible.eda.webhook", "source_name": "ansible.eda.webhook", "source_args": {"host": "0.0.0.0", "port": 5000}, "source_filters": []}}], "rules": [{"Rule": {"name": "Say Hello", "condition": {"AllCondition": [{"EqualsExpression": {"lhs": {"Event": "payload.message"}, "rhs": {"String": "Ansible is super cool"}}}]}, "action": {"Action": {"action": "run_playbook", "action_args": {"name": "say-what.yml"}}}, "enabled": true}}]}
2023-02-03 16:15:30,793 - ansible_rulebook.engine - INFO - load source
2023-02-03 16:15:31,294 - ansible_rulebook.engine - INFO - load source filters
2023-02-03 16:15:31,296 - ansible_rulebook.engine - INFO - Calling main in ansible.eda.webhook
2023-02-03 16:15:31,298 - ansible_rulebook.engine - INFO - Start a playbook action runner for Listen for events on a webhook
2023-02-03 16:15:31,299 - ansible_rulebook.engine - INFO - Waiting for actions on events from Listen for events on a webhook
2023-02-03 16:15:31,302 - ansible_rulebook.engine - INFO - Waiting for events from Listen for events on a webhook

Then, with a simple curl command, we will trigger the web hook:

curl -H 'Content-Type: application/json' -d '{"message": "Ansible is super cool"}' 127.0.0.1:5000/endpoint

In the output of the running command, we will see some new logs as well as something familiar to any ansible developer: The playbook running.

2023-02-03 16:24:53,960 - aiohttp.access - INFO - 127.0.0.1 [03/Feb/2023:16:24:53 +0000] "POST /endpoint HTTP/1.1" 200 158 "-" "curl/7.74.0"
2023-02-03 16:24:53 962 [main] INFO org.drools.ansible.rulebook.integration.api.rulesengine.RegisterOnlyAgendaFilter - Activation of effective rule "Say Hello" with facts: [Event DROOLS_PROTOTYPE with values = {meta.headers.Accept=*/*, meta.headers.Host=127.0.0.1:5000, payload={message=Ansible is super cool}, payload.message=Ansible is super cool, meta={headers={Accept=*/*, User-Agent=curl/7.74.0, Host=127.0.0.1:5000, Content-Length=36, Content-Type=application/json}, endpoint=endpoint}, meta.headers.Content-Type=application/json, meta.headers.User-Agent=curl/7.74.0, meta.endpoint=endpoint, meta.headers.Content-Length=36, meta.headers={Accept=*/*, User-Agent=curl/7.74.0, Host=127.0.0.1:5000, Content-Length=36, Content-Type=application/json}}]
2023-02-03 16:24:53,964 - ansible_rulebook.rule_generator - INFO - calling Say Hello
2023-02-03 16:24:53,964 - ansible_rulebook.engine - INFO - call_action run_playbook
2023-02-03 16:24:53,965 - ansible_rulebook.engine - INFO - substitute_variables [{'name': 'say-what.yml'}] [{'event': {'payload': {'message': 'Ansible is super cool'}, 'meta': {'headers': {'Accept': '*/*', 'User-Agent': 'curl/7.74.0', 'Host': '127.0.0.1:5000', 'Content-Length': '36', 'Content-Type': 'application/json'}, 'endpoint': 'endpoint'}}, 'fact': {'payload': {'message': 'Ansible is super cool'}, 'meta': {'headers': {'Accept': '*/*', 'User-Agent': 'curl/7.74.0', 'Host': '127.0.0.1:5000', 'Content-Length': '36', 'Content-Type': 'application/json'}, 'endpoint': 'endpoint'}}}]
2023-02-03 16:24:53,966 - ansible_rulebook.engine - INFO - action args: {'name': 'say-what.yml'}
2023-02-03 16:24:53,967 - ansible_rulebook.engine - INFO - Queue playbook/module/job template {'say-what.yml'} for running later
2023-02-03 16:24:53,967 - ansible_rulebook.builtin - INFO - running Ansible playbook: say-what.yml
2023-02-03 16:24:53,970 - ansible_rulebook.builtin - INFO - ruleset: Listen for events on a webhook, rule: Say Hello
2023-02-03 16:24:53,971 - ansible_rulebook.builtin - INFO - Calling Ansible runner

PLAY [localhost] ***************************************************************

TASK [Gathering Facts] *********************************************************
ok: [localhost]

TASK [debug] *******************************************************************
ok: [localhost] => {
    "msg": "Thank you, my friend!"
}

PLAY RECAP *********************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Conclusion

We confirmed that our Ansible Event Driven installation is working. On my next blog, I will integrate it with Apache Kafka, the well know event store and stream-processing platform.