Skip to content

Instantly share code, notes, and snippets.

@marcusphi
Created October 2, 2013 09:48
Show Gist options
  • Save marcusphi/6791404 to your computer and use it in GitHub Desktop.
Save marcusphi/6791404 to your computer and use it in GitHub Desktop.
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'"
@grahamashby
Copy link

@spikerobinson +1
I'd also like to add that the examples in the modules section are simplistic. I'm trying to abort a playbook if some of the parameters don't match up. I'm finding that the conditionals in the 'assert' and 'fail' modules evaluate differently.

@palanisu
Copy link

palanisu commented Mar 1, 2021

If the value is empty, then how we can check conditions

@andrew-curtis
Copy link

andrew-curtis commented Sep 17, 2023

@spikerobinson +1 I'd also like to add that the examples in the modules section are simplistic. I'm trying to abort a playbook if some of the parameters don't match up. I'm finding that the conditionals in the 'assert' and 'fail' modules evaluate differently.

we're 3/4 of the way through 2023 and this still seems to be an issue.

The exact same when: condition that I can use to trigger a debug: output, doesn't work on a fail: task. 🤯This is egregiously bad.

What I have found after lots and lots of testing and before I found this page which works for me is setting a 'Boolean' fact for my when: clause like the following, so I can plug all the holes in the leaky conditional logic that I wasn't able to get working for me.

- name: Set Change State Valid fact
      ansible.builtin.set_fact:
        change_state_valid_fact: "{{ 'false' if (change_state_fact != '-1' and change_state_fact != '-2') else 'false' if change_end_date_gmt_fact < current_date_gmt_fact else 'true' if change_end_date_gmt_fact > current_date_gmt_fact else 'true' if change_state_fact == '-1' else 'true' if change_state_fact == '-2' }}"

Then i just used a simple when: in my fail: task.

    - name: Checking if change window has lapsed or if the change is not in an executable state
      fail:
        msg: "FAILED: This change cannot be executed against in its current state."
      when: not change_state_valid_fact

This way I was able to be explicit about all the outcomes and control the result.
Well, it seems to be working so far at least! Please feel free to poke holes in it and point out any oversights on my part. (I'm fried). 😅

EDIT: just found some errors with my logic in my fact. Something just evaluated to false when it shouldn't have. Back to the drawing board! 😫

EDIT2: So I've reworked my code and discovered epoch time which has vastly improved the reliability of time comparisons and this seems to work. I still have to set this as a fact separately from the fail task because I can't use jinja2 in a when and unwrapping it gets different results so i'm sticking with a separate fact. Here's my updated code.

    - name: Set Change State Valid fact
      ansible.builtin.set_fact:
        change_state_valid_fact: "{{ (change_state_fact in ['-1', '-2']) and (change_end_date_epoch_fact > current_date_epoch_fact) }}"

Hope it helps someone else. (Maybe?) If nothing else, as a cautionary tale of what not to do. 🤪

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