Conditions
In event driven automation a condition determines if a rule fires (runs its action).
Example:
condition: event.status == "enabled"
- Each of the condition(s) can use information from
Event received
Previously saved events within the rule
Longer term facts about the system
Variables provided by the user at startup saved as vars
- When writing conditions
use the event prefix when accessing data from the current event in single condition rules
use the events prefix when accessing matched events in multi-condition rules (any/all)
use the fact prefix when accessing data from the set_facts actions in the rulebook
use the facts prefix when assigning variables and accessing data within the rule
use the vars prefix when accessing variables passed in via –vars and –env-vars
Important
The event vs events distinction is critical:
Single condition rules use
event(singular) to access the matching eventMulti-condition rules (any/all) use
events(plural) to access matched events
Even if an any condition matches only one event at a time, you must still use events
(not event) in the action. For example: {{ events.m_0 }} or {{ events.match }}
Note
A condition cannot contain Jinja style substitution when accessing variables passed in from the command line, we loose the data type information and the rule engine will not process the condition. Instead use the vars prefix to access the data passed in from the command line
- A condition can contain
One condition
Multiple conditions where all of them have to match
Multiple conditions where any one of them has to match
Multiple conditions where not all one of them have to match
Supported data types
The data type is of great importance for the rules engine. The following types are supported
integers
strings
booleans
floats (dot notation and scientific notation)
null
Supported Operators
Conditions support the following operators:
Name |
Description |
|---|---|
== |
The equality operator for strings and numbers |
!= |
The non equality operator for strings and numbers |
> |
The greater than operator for numbers |
< |
The less than operator for numbers |
>= |
The greater than equal to operator for numbers |
<= |
The less than equal to operator for numbers |
+ |
The addition operator for numbers |
- |
The subtraction operator for numbers |
* |
The multiplication operator for numbers |
and |
The conjunctive add, for making compound expressions |
or |
The disjunctive or |
in |
To check if a value in the left hand side exists in the list on the right hand side |
not in |
To check if a value in the left hand side does not exist in the list on the right hand side |
contains |
To check if the list on the left hand side contains the value on the right hand side |
not contains |
To check if the list on the left hand side does not contain the value on the right hand side |
is defined |
To check if a variable is defined |
is not defined |
To check if a variable is not defined, please see caveats listed below |
is match(pattern,ignorecase=true) |
To check if the pattern exists in the beginning of the string. Regex supported |
is not match(pattern,ignorecase=true) |
To check if the pattern does not exist in the beginning of the string. Regex supported |
is search(pattern,ignorecase=true) |
To check if the pattern exists anywhere in the string. Regex supported |
is not search(pattern,ignorecase=true) |
To check if the pattern does not exist anywhere in the string. Regex supported |
is regex(pattern,ignorecase=true) |
To check if the regular expression pattern exists in the string |
is not regex(pattern,ignorecase=true) |
To check if the regular expression pattern does not exist in the string |
is select(operator, value) |
To check if an item exists in the list, that satisfies the test defined by operator and value |
is not select(operator, value) |
To check if an item does not exist in the list, that does not satisfy the test defined by operator and value |
is selectattr(key, operator, value) |
To check if an object exists in the list, that satisfies the test defined by key, operator and value |
is not selectattr(key, operator, value) |
To check if an object does not exist in the list, that does not satisfy the test defined by key, operator and value |
<< |
Assignment operator, to save the matching events or facts with events or facts prefix |
not |
Negation operator, to negate boolean expression |
Examples
Single condition
name: An automatic remediation rule condition: event.outage == true action: run_playbook: name: remediate_outage.yml
When an event comes with outage attribute as true, the specified playbook is executed.
Single boolean
name: An automatic remediation rule condition: event.outage action: run_playbook: name: remediate_outage.yml
If the outage attribute is a boolean, you can use it
by itself in the condition. This is a shorter version of
the previous example. If the value is true the condition
passes and the action is triggered.
Multiple conditions where all of them have to match
Important
Critical: event vs events
Single condition rules: Access the matching event using
event(singular)Multi-condition rules (any/all): Access matching events using
events(plural)
For all conditions, when multiple events match different conditions, you must
use the events prefix (not event). Matched events are stored as events.m_0,
events.m_1, etc., based on the order of conditions, or you can assign custom names
using the << operator.
name: All conditions must match
condition:
all:
- event.target_os == "linux"
- event.tracking_id == 345
action:
debug:
msg: "First event: {{ events.m_0 }}, Second event: {{ events.m_1 }}"
As we receive events from the source plugins we send them to the appropriate
rule set sessions running in the rule engine.
With multiple conditions the rule engine will keep track of the conditions that
have matched and wait for the next event to come in which might match other conditions.
Once all the conditions have been met, it will return you all the events that matched,
which can be used in action via the events variable.
Warning
Note that in this case the engine will consider all the different events until the conditions are met, regardless of whether those events come from one or multiple sources. Multiple conditions with
allare not equivalent to a single condition with theandoperator.If you want to match only one event using multiple attributes the rule must use a single condition with the
andoperator:name: One condition combining attributes condition: event.target_os == "linux" and event.tracking_id == 345 action: debug:
Multiple conditions where all of them have to match with internal references
--- - name: Delayed comparison hosts: all sources: - eda.builtin.generic: payload: - friend_list: names: - fred - barney - request: type: Delete friend_name: fred rules: - name: r1 condition: all: - event.request.type == "Delete" - event.friend_list.names is select("search", events.m_0.request.friend_name) action: print_event: pretty: true
--- - name: multiple conditions caching hosts: all sources: - eda.builtin.generic: payload: - request: type: Delete friend_name: fred - request: type: Delete friend_name: wilma - friend_list: names: - fred - barney - friend_list: names: - betty - wilma rules: - name: r1 condition: all: - event.request.type == "Delete" - event.friend_list.names contains events.m_0.request.friend_name action: print_event:
Multiple conditions where any one of them has to match
Important
Critical: event vs events
Single condition rules: Access the matching event using
event(singular)Multi-condition rules (any/all): Access matching events using
events(plural)
For any conditions, even though only one event triggers the rule at a time, you must
still use the events prefix (not event). The matching event is stored as events.m_0,
events.m_1, etc., or you can assign it to a custom name like events.match using the
<< operator as shown below.
- Important:
Each condition in an any block evaluates different events, not different attributes from the same event
By default, matched events are stored as events.m_0, events.m_1, events.m_2, etc., based on which condition matched
You can override the default m_{n} naming using the << assignment operator
Example with default assignments (m_0, m_1, m_2):
--- - name: Any condition with default assignments hosts: localhost sources: - ansible.eda.generic: payload: - temperature: 85 location: "server-room" - user: "alice" action: "login" - inventory: 42 warehouse: "west" rules: - name: Monitor different event types condition: any: - event.temperature == 85 - event.user == "alice" - event.inventory == 42 action: debug: msg: "Matched event: {{ events }}"
temperature: 85,user: "alice", and the third has inventory: 42. As soon as the firstExample with custom assignment using the << operator:
--- - name: Any condition with custom assignment hosts: localhost sources: - ansible.eda.generic: payload: - temperature: 85 location: "server-room" - user: "alice" action: "login" - inventory: 42 warehouse: "west" rules: - name: Monitor different event types with custom name condition: any: - events.match << event.temperature == 85 - events.match << event.user == "alice" - events.match << event.inventory == 42 action: debug: msg: "Found a match: {{ events.match }}"
Warning
The engine considers distinct events (separate incoming events), not different attributes from a single event. Multiple conditions with
anyare not equivalent to a single condition with theoroperator.Wrong approach - This will NOT work as expected if you want to check multiple attributes from one event:
# This waits for DIFFERENT events, not one event with either attribute condition: any: - event.target_os == "linux" - event.target_os == "windows"Correct approach - To match one event using multiple possible attribute values, use a single condition with the
oroperator:# This checks ONE event for either attribute value name: One condition combining attributes from a single event condition: event.target_os == "linux" or event.target_os == "windows" action: debug:
Multiple conditions with facts and events and all of one of them have to match
name: Condition using both a fact and an event condition: all: - fact.meta.hosts == "localhost" - event.target_os == "windows" action: debug:
Condition with fact and event
name: Condition using a set_fact fact and an event condition: all: - facts.first << fact.custom.expected_index is defined - event.i == facts.first.custom.expected_index action: debug:
Condition with vars and event
name: Condition using a passed in variable and an event condition: all: - event.year == vars.person.year - event.age == vars.person.age action: debug:
--vars from the command line to ansible-rulebook. The usage of vars allows us toname: Single condition comparing vars and event condition: event.name == vars.name action: debug:
Logical and
name: Multiple Attribute match from a single event condition: event.target_os == "linux" and event.version == "1.1" action: debug:
Logical or
name: Match any one attribute from a single event condition: event.version == "2.0" or event.version == "1.1" action: debug:
Combining logical operators
You can combine multiple and operators:
name: Combining and operators condition: event.version == "2.0" and event.name == "example" and event.alert_count > 10 action: debug:
If you combine and and or operators they must be enclosed in parenthesis:
name: Combining and -and- or operators condition: ((event.i > 100 and event.i < 200) or (event.i > 500 and event.i < 600)) action: debug:name: Combining and -and- or operators condition: (event.i > 100 and event.i < 200) or event.i > 1000 action: debug:
Multiple conditions with assignment
When a condition is evaluated if the condition passes the matching event it is stored in well known attribute(s) called m_0, m_1, m_2….. The first condition will be stored in m_0 and the second condition in m_1 … Its based on the position of the condition in the list so you can predictably use it in other conditions. You can optionally alias these attribute(s) using the << operator. For example:
name: multiple conditions condition: all: - events.first << event.i == 0 - events.second << event.i == 1 - events.third << event.i == events.first.i + 2 action: debug: msg: - "first: {{ events.first }}" - "second: {{ events.second }}" - "third: {{ events.third }}"
name: multiple conditions using default assignments condition: all: - event.i == 0 - event.i == 1 - event.i == events.m_0.i + 2 action: debug: msg: - "first: {{ events.m_0 }}" - "second: {{ events.m_1 }}" - "third: {{ events.m_2 }}"
Multiple condition with default assignments
name: multiple conditions condition: all: - event.i == 1 - event.i == 2 - event.i == events.m_0.i + 3 action: debug: msg: - "first: {{ events.m_0 }}" - "second: {{ events.m_1 }}" - "third: {{ events.m_2 }}"
The first match is stored as m_0, and the subsequent ones are stored as m_1, m_2 …
Single condition assignment (Not supported)
Important
Remember: Single condition rules use event, not events
For single condition rules, the matching event is always accessed via event (singular).
You cannot use events or assignment operators in single condition rules.
name: assignment ignored
condition: event.first << event.i == 0
action:
debug:
msg:
- "event: {{event}}"
Negation Example
name: negation condition: not (event.i > 50 or event.i < 10) action: print_event:
Note
not operator can work without parenthesis when the value is a single logical statement
If there are multiple logical statements with or or and please use round brackets like shown above.
Adding time constraints for rules with multiple conditions
name: Condition with timeout condition: all: - event.x == 5 - event.y == 99 timeout: 10 seconds action: debug:
Adding time constraints for rules when “not all” conditions matched
name: Not all conditions met with timeout condition: not_all: - event.msg == "Applying Maintenance" - event.msg == "Server Rebooted" - event.msg == "Application Restarted" timeout: 5 minutes action: run_playbook: name: notify_delays.yml
Throttle actions to counter event storms: Reactive
name: Throttle example reactive condition: event.code == "error" throttle: once_within: 5 minutes group_by_attributes: - event.meta.hosts - event.code action: run_playbook: name: notify_outage.yml
Throttle actions to counter event storms: Passive
name: Throttle example passive condition: event.code == "warning" throttle: once_after: 5 minutes group_by_attributes: - event.meta.hosts - event.code action: run_playbook: name: notify_outage.yml
Throttle actions till a specific number of events has been received
name: Throttle with threshold condition: event.code == "warning" throttle: accumulate_within: 5 minutes threshold: 10 group_by_attributes: - event.meta.hosts - event.code action: run_playbook: name: notify_outage.yml
String search
name: string search example condition: event.url is search("example.com", ignorecase=true) action: print_event:
name: string not search example condition: event.url is not search("example.com", ignorecase=true) action: print_event:
String match
name: string match example condition: event.url is match("http://www.example.com", ignorecase=true) action: print_event:
name: string not search example condition: event.url is not match("http://www.example.com", ignorecase=true) action: print_event:
String regular expression
name: string regex example condition: event.url is regex("example\.com", ignorecase=true) action: print_event:
name: string not regex example condition: event.url is not regex("example\.com", ignorecase=true) action: print_event:
Check if an item exists in a list
# variables file expected_levels: - "WARNING" - "ERROR"name: check if an item exist in a list condition: event.level in vars.expected_levels action: debug: msg: matched!name: check if an item does no exist in a list condition: event.level not in ["INFO", "DEBUG"] action: debug: msg: matched!name: check if a list contains an item condition: event.affected_hosts contains "host1" action: debug: msg: matched!name: check if a list does not contain an item condition: vars.expected_levels not contains "INFO" action: debug: msg: This will match always for every event because INFO is not in the list!
Check if an item exists in a list based on a test
name: check if an item exist in list condition: event.levels is select('>=', 10) action: debug: msg: The list has an item with the value greater than or equal to 10
Check if an item does not exist in a list based on a test
name: check if an item does not exist in list condition: event.levels is not select('>=', 10) action: debug: msg: The list does not have item with the value greater than or equal to 10
You can find more information for the select condition also in the Ansible playbook documentation for Loops and list comprehensions.
Checking if an object exists in a list based on a test
name: check if an object exist in list condition: event.objects is selectattr('age', '>=', 20) action: debug: msg: An object with age greater than 20 found
Checking if an object does not exist in a list based on a test
name: check if an object does not exist in list condition: event.objects is not selectattr('age', '>=', 20) action: debug: msg: No object with age greater than 20 found
You can find more information for the selectattr condition also in the Ansible playbook documentation for Loops and list comprehensions.
FAQ
- Example:
name: send to debug condition: event.payload.eventType != 'GET' action: debug:
In the above case if any of the event.payload.eventType is undefined the condition is ignored and doesn’t match anything.
- Example:
name: rule1 condition: event.msg is defined action: retract_fact: fact: msg: "{{event.msg}}"
Warning
Important: Unlike traditional programming languages, the rule engine does not crash when an attribute is undefined. If an attribute doesn’t exist in an event, the condition is simply ignored and doesn’t match. This means is defined checks are usually unnecessary.
Special case - event.meta: Every event automatically has a meta field added by the system when the event is received. Using
event.meta is definedwill always match (equivalent to settingcondition: true), so this check is meaningless.- When is defined is actually useful:
Initializing variables with set_fact
After a retract_fact operation
When you explicitly want a rule to fire for any event (use
event.meta is defined)
Example of unnecessary is defined check:
# UNNECESSARY - the condition will be ignored if event.temperature doesn't exist name: Check temperature (bad) condition: event.temperature is defined and event.temperature > 85 action: debug: # BETTER - just check the value directly name: Check temperature (good) condition: event.temperature > 85 action: debug:
- Example:
name: rule2 condition: fact.msg is not defined action: set_fact: fact: msg: Hello World
- Example:
name: rule1 condition: 'event.abc == "test: 1"'
name: rule1 condition: > event.abc == "test: 1"
name: rule1 condition: | event.abc == "test: 1"