Getting started
If ansible-rulebook
is not already installed, follow the installation guide to install it.
Hello world!
Now let’s get started with a simple hello world example to familiarize ourselves with the concepts. Save the
following to a simple-rulebook.yml
file:
---
- name: Hello Events
hosts: localhost
sources:
- ansible.eda.range:
limit: 5
rules:
- name: Say Hello
condition: event.i == 1
action:
run_playbook:
name: ansible.eda.hello
Events come from an event source and are then checked against rules to determine if an action should be taken. If the condition of a rule matches the event, it will run the action for that rule.
In this example, the event source (ansible.eda.range
) is the Python range function. It produces events that count from
i=0
to i=<limit>
.
When i
is equal to 1 the condition for the the Say Hello
rule matches and it runs a simple
hello
playbook available in the ansible.eda
collection.
And now you can run the rulebook to see the results:
$ ansible-rulebook -i inventory.yml -r simple-rulebook.yml
PLAY [hello] *******************************************************************
TASK [debug] *******************************************************************
ok: [localhost] => {
"msg": "hello"
}
PLAY RECAP *********************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
A webhook example
Normally events would come from monitoring and alerting systems or other software. The following is a more complete example that accepts alerts from Alertmanager:
---
- name: Automatic Remediation of a webserver
hosts: all
sources:
- name: listen for alerts
ansible.eda.alertmanager:
host: 0.0.0.0
port: 8000
rules:
- name: restart web server
condition: event.alert.labels.job == "fastapi" and event.alert.status == "firing"
action:
run_playbook:
name: ansible.eda.start_app
...
This example sets up a webhook to receive events from alertmanager and then matches events where the fastapi job alert has a status of firing. This runs a playbook that will remediate the issue.
Let’s build an example rulebook that will trigger an action from a
webhook. We will be looking for a specific payload from the webhook, and
if that condition is met from the webhook event, then ansible-rulebook
will trigger the desired action. Below is our example rulebook webhook-example.yml
:
---
- 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
The playbook say-what.yml
:
- hosts: localhost
connection: local
tasks:
- debug:
msg: "Thank you, my friend!"
If we look at this example, we can see the structure of the rulebook. Our sources, rules and actions are defined. We are using the webhook source plugin from our ansible.eda collection, and we are looking for a message payload from our webhook that contains “Ansible is super cool”. Once this condition has been met, our defined action will trigger which in this case is to trigger a playbook.
One important thing to take note of ansible-rulebook
is that it is not
like ansible-playbook
which runs a playbook and once the playbook has
been completed it will exit. With ansible-rulebook, it will continue to
run waiting for events and matching those events, it will only exit upon
a shutdown action or if there is an issue with the event source itself,
for example if a website you are watching with the ansible.eda.url-check
plugin
stops working.
With our rulebook built, we will simply tell ansible-rulebook
to use it
as a ruleset and wait for events:
root@ansible-rulebook:/root# ansible-rulebook --rulebook webhook-example.yml -i inventory.yml --verbose
INFO:ansible_events:Starting sources
INFO:ansible_events:Starting sources
INFO:ansible_events:Starting rules
INFO:root:run_ruleset
INFO:root:{'all': [{'m': {'payload.message': 'Ansible is super cool!'}}], 'run': <function make_fn.<locals>.fn at 0x7ff962418040>}
INFO:root:Waiting for event
INFO:root:load source
INFO:root:load source filters
INFO:root:Calling main in ansible.eda.webhook
Now, ansible-rulebook
is ready and it’s waiting for an event to match.
If a webhook is triggered but the payload does not match our condition
in our rule, we can see it in the ansible-rulebook
verbose output:
…
INFO:root:Calling main in ansible.eda.webhook
INFO:aiohttp.access:127.0.0.1 [14/Oct/2022:09:49:32 +0000] "POST /endpoint HTTP/1.1" 200 158 "-" "curl/7.61.1"
INFO:root:Waiting for event
But once our payload matches what we are looking for, that’s when the magic happens, so we will simulate a webhook with the correct payload:
curl -H 'Content-Type: application/json' -d "{\"message\": \"Ansible is super cool\"}" 127.0.0.1:5000/endpoint
INFO:root:Calling main in ansible.eda.webhook
INFO:aiohttp.access:127.0.0.1 [14/Oct/2022:09:50:28 +0000] "POST /endpoint HTTP/1.1" 200 158 "-" "curl/7.61.1"
INFO:root:calling Say Hello
INFO:root:call_action run_playbook
INFO:root:substitute_variables [{'name': 'say-what.yml'}] [{'event': {'payload': {'message': 'Ansible is super cool'}, 'meta': {'endpoint': 'endpoint', 'headers': {'Host': '127.0.0.1:5000', 'User-Agent': 'curl/7.61.1', 'Accept': '*/*', 'Content-Type': 'application/json', 'Content-Length': '36'}}}, 'fact': {'payload': {'message': 'Ansible is super cool'}, 'meta': {'endpoint': 'endpoint', 'headers': {'Host': '127.0.0.1:5000', 'User-Agent': 'curl/7.61.1', 'Accept': '*/*', 'Content-Type': 'application/json', 'Content-Length': '36'}}}}]
INFO:root:action args: {'name': 'say-what.yml'}
INFO:root:running Ansible playbook: say-what.yml
INFO:root:Calling Ansible runner
PLAY [say thanks] **************************************************************
TASK [debug] *******************************************************************
ok: [localhost] => {
"msg": "Thank you, my friend!"
}
PLAY RECAP *********************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
INFO:root:Waiting for event
We can see from the output above, that the condition was met from the
webhook and ansible-rulebook
then triggered our action which was to
run_playbook
. The playbook we defined is then triggered and once it
completes we can see we revert back to “Waiting for event”.
Event-Driven Ansible opens up the possibilities of faster resolution and greater automated observation of our environments. It has the possibility of simplifying the lives of many technical and sleep-deprived engineers.
Extras
Video: Writing Rulebooks