Create a gist now

Instantly share code, notes, and snippets.

Ansible 1.3 Conditional Execution -- Very complete example with comments -- I find the conditional expressions to be ridiculously hard to get right in Ansible. I don't have a good model of what's going on under the surface so I often get it wrong. What makes it even harder is that there has been at least three different variants over the course …
---
# This has been tested with ansible 1.3 with these commands:
# ansible-playbook -i hosts ansible_conditionals_examples.yaml --extra-vars="hosts=myhosts isFirstRun=false"
# ansible-playbook -i hosts ansible_conditionals_examples.yaml --extra-vars="hosts=myhosts isFirstRun=true"
# ansible-playbook -i hosts ansible_conditionals_examples.yaml --extra-vars="hosts=myhosts"
# NB: The type of the variable is crucial!
- name: Ansible Conditionals Examples
hosts: $hosts
vars_files:
- vars.yml
tasks:
########### Correct COMPLEX expressions ###################
# Need parentheses around $is_live, because it (may) contain an _or_, and _and_ has higher prio than _or_;
- name: ($is_live) and (isFirstRun == 'true') # OK only if isFirstRun is defined.
command: echo hello
when: ($is_live) and (isFirstRun == 'true')
# isFirstRun is a string var, whereas is_mgmt is an expression evaluating to a string, so it seems we need the $ to dereference it
- name: $is_mgmt and (isFirstRun == 'true') # OK only if isFirstRun is defined
command: echo hello
when: $is_mgmt and (isFirstRun == 'true')
- name: $is_mgmt and (isFirstRun != 'false') # OK only if isFirstRun is defined
command: echo hello
when: $is_mgmt and (isFirstRun != 'false')
- name: $is_mgmt and (not isFirstRun == 'false') # OK only if isFirstRun is defined
command: echo hello
when: $is_mgmt and (not isFirstRun == 'false')
########### SIMPLE expressions ############################
- name: is_mgmt # OK
command: echo hello
when: is_mgmt
- name: $is_mgmt # OK
command: echo hello
when: $is_mgmt
- name: ($is_mgmt) # OK
command: echo hello
when: ($is_mgmt)
- name: (is_mgmt) # WRONG - Always runs
command: echo hello
when: (is_mgmt)
######
- name: isFirstRun == 'true' # OK
command: echo hello
when: isFirstRun == 'true'
- name: not isFirstRun != 'true' # OK
command: echo hello
when: not isFirstRun != 'true'
- name: not isFirstRun == 'false' # OK
command: echo hello
when: not isFirstRun == 'false'
- name: isFirstRun != 'false' # OK
command: echo hello
when: isFirstRun != 'false'
- name: $isFirstRun == 'true' # WRONG - $isFirstRun is interpreted as true and true is not equal to 'true' so only works for negative match
command: echo hello
when: $isFirstRun == 'true'
########### All wrong #####################################
- name: ($is_live == True) and (isFirstRun == 'true') # WRONG - Only first live node on true (none of false (correct))
command: echo hello
when: ($is_live == True) and (isFirstRun == 'true')
- name: $is_live and (isFirstRun == 'true') # WRONG - Correct when true, but only first live when false
# ['$inventory_hostname' == 'tsrvuweblive71' or '$inventory_hostname' == 'tsrvuweblive72' and (isFirstRun == 'true')]
command: echo hello
when: $is_live and (isFirstRun == 'true')
- name: is_live and (isFirstRun == 'true') # WRONG - All nodes run on true (none of false (correct))
command: echo hello
when: is_live and (isFirstRun == 'true')
- name: ($is_live == 'True') and (isFirstRun == 'true') # WRONG - Only first live node on true (none on false (correct))
command: echo hello
when: ($is_live == 'True') and (isFirstRun == 'true')
- name: (is_live == 'True') and (isFirstRun == 'true') # WRONG - No nodes run on niether true nor false - is_live not dereferenced
command: echo hello
when: (is_live == 'True') and (isFirstRun == 'true')
######
- name: is_mgmt and (isFirstRun == 'true')
# WRONG - All run on true
command: echo hello
when: is_mgmt and (isFirstRun == 'true')
- name: is_mgmt and (isFirstRun != 'false')
# WRONG - All run on true
command: echo hello
when: is_mgmt and (isFirstRun != 'false')
- name: is_mgmt and (not isFirstRun == 'false')
# WRONG - All run on true
command: echo hello
when: is_mgmt and (not isFirstRun == 'false')
- name: mgmt and isFirstRun equal true with ()
# WRONG - Does not run when isFirstRun is 'false' (which is correct), but runs all nodes when 'true'
command: echo hello
when: ((is_mgmt) and (isFirstRun == 'true'))
- name: mgmt and isFirstRun not_equal true with $
# WRONG - Always runs mgmt irrespective of value of isFirstRun
command: echo hello
when: $is_mgmt and $isFirstRun != 'true'
- name: mgmt and not isFirstRun equal true with $
# # WRONG - Always runs mgmt irrespective of value of isFirstRun
command: echo hello
when: $is_mgmt and not $isFirstRun == 'true'
- name: mgmt and isFirstRun equal true with $
# WRONG - Never runs any node
command: echo hello
when: $is_mgmt and $isFirstRun == 'true'
- name: mgmt and not isFirstRun equal true with only_if and $
# WRONG - All nodes run
command: echo hello
only_if: "'$is_mgmt' and not '$isFirstRun' == 'true'"
- name: mgmt and isFirstRun not_equal true with only_if and $
# WRONG - All nodes run
command: echo hello
only_if: "'$is_mgmt' and '$isFirstRun' != 'true'"
- name: mgmt and isFirstRun not_equal true with ()
# WRONG - All nodes run when isFirstRun is false and no nodes run when isFirstRun is true
command: echo hello
when: ((is_mgmt) and (isFirstRun != 'true'))
- name: mgmt and isFirstRun not_equal true
# WRONG - All nodes run when isFirstRun is false and no nodes run when isFirstRun is true
command: echo hello
when: is_mgmt and isFirstRun != 'true'
- name: mgmt and not isFirstRun equal true
# WRONG - All nodes run when isFirstRun is false and no nodes run when isFirstRun is true
command: echo hello
when: is_mgmt and not isFirstRun == 'true'
- name: mgmt and not isFirstRun equal true with ()
# WRONG - All nodes run when isFirstRun is false and no nodes run when isFirstRun is true
command: echo hello
when: ((is_mgmt) and (not isFirstRun == 'true'))
- name: mgmt and not isFirstRun equal true with only_if, $ and ()
# ERROR Conditional expression must evaluate to True or False: ($is_mgmt) and (not '$isFirstRun' == 'true')
command: echo hello
only_if: "($is_mgmt) and (not '$isFirstRun' == 'true')"
---
# The "" makes this evaluate to a string. Which is good in some cases and bad in other...
is_mgmt: "'$inventory_hostname' == 'webmgmt'"
is_live: "'$inventory_hostname' == 'weblive1' or '$inventory_hostname' == 'weblive2'"
@FlorianHeigl

The syntax is (imho) shitty and has too many patches (in the literal sense, not in sense of code patches), or to stay in our terms, too much duct tape.

Thanks a lot for this comprehensive overview.

@matteomelani

+1
At least the Ansible people should improve the docs.

@mschurenko

Thanks for this. The syntax is very inconsistent and horribly confusing.

@mkempster

Thanks! This is a great reference.

@mpdehaan
mpdehaan commented Dec 3, 2014

This is a 1.3 example of a very out of date syntax from early in the evolution of Ansible, please refer to docs.ansible.com for all of the latest -- which does NOT use the $foo stuff anywhere.

@cvvs
cvvs commented Dec 17, 2014

Conditionals are still a pain in Ansible 1.7.2. Sometimes, one uses when, other times, one has to use group_by. This is complicated by the fact there seems to be no way to see why something was skipped. For those of us who can't seem to find the documentation that tells you, "If you apply a 'when' to an include, then the conditional applies to the contents of the include... but not really, because even if they meet the condition required by the 'when' condition, they'll still be skipped," this is quite painful, and clear as mud.

Update: After a lot of searching, and clearly weak search-fu, I finally found a decent solution in the best practices documentation. include_var: {{ something }}.yml was the ticket for me.

@biggers
biggers commented Oct 23, 2015

Would someone update this? We have found the "compound conditional YAML" to be a real PITA for Ansible -- better examples (using group vars) would be cool. Ansible docs need more examples on this.

@Constantin07
Constantin07 commented Sep 16, 2016 edited

Even in Ansible 2.1.1 the compound conditions in when statement behave quite strange.

@ssbarnea

@Constantin07 It seems that I am hitting the same problem. Maybe someone from this thread could find a solution for http://stackoverflow.com/questions/39779168/how-to-use-string-concatenations-in-ansible-conditional-checks

@spikerobinson
spikerobinson commented Mar 6, 2017 edited

Here we are at Ansible 2.3, and the conditionals are still incredibly hard to get right, and the documentation still seems to be completely unhelpful. If anyone would like to say the documentation is helpful, please post a link to the specific piece of documentation that explains the syntax of conditional expressions. All I can find is some vague high level comments and one or two examples. I have had to drill into the Jinja2 documentation to make any headway at all. Otherwise it is pure trial and error - like the author of this Gist clearly had to do.

To elaborate, the Ansible documentation on conditionals, here:
http://docs.ansible.com/ansible/playbooks_conditionals.html

Does not actually in any way describe or define a conditional expression. It's a list of Ansible functions which take conditional expressions as arguments. The difficulty is not (so much) in using failed_when etc. The difficulty is in predicting how a given conditional expression will evaluate, so you can reverse-engineer a conditional expression that will give as-expected behaviour. The two things that are completely missing from the Ansible documentation (referenced above), are:

  1. what are the syntax and semantics of permitted conditional expressions
  2. what are the data types permitted as operands for conditional expressions, and how does the data type affect the evaluation

Point 1 is absent from the Ansible documentation, but can be mined out of the Jinja2 documentation (and then fingers crossed that nothing significant changes between Jinja2 and Ansible's implementation).
Point 2 is just absent, and turns out being the main reason for conditional expressions not behaving as expected - as can be seen from the long list of 'unexpectedly right' and 'unexpectedly wrong' examples helpfully given above.

Furthermore, actually Ansible's conditional expression syntax is not == Jinja2's conditional expression syntax. Ansible conditional expression syntax is templated and converted/expanded into much more verbose Jinja2 conditional expressions, behind the scenes.

In conclusion, Ansible's conditional expression syntax is both precarious and undocumented. Thanks again to the author of this post for trying to remedy that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment