I’m working on an Ansible playbook that uses a when condition, and I was trying to test the scenario where there is a default value specified as a play variable, which can then be selectively overridden from the command line using the -e argument to ansible-playbook. But this is not working as I would expect, and I’m not seeing any obvious reason(s) why.
Here’s the setup (this is a super stripped-down sample, just to show the point, not my real playbook).
conditional_task.yml
- name: A playbook to conditionally print some debug messages hosts: localhost gather_facts: False vars: doomed: False tasks: - name: print the value of doomed debug: var=doomed - name: print the message if we're doomed debug: msg: "We're f*****g doomed!" when: doomed == True - name: print the message if we're not doomed debug: msg: "Glory be to FSM, we are saved!" when: doomed == False
If I run this with the ansible-playbook command with no arguments, I get exactly the expected output.
$ ansible-playbook conditional_task.yml PLAY [A playbook to conditionally print some debug messages] ************************************************************************************ TASK [print the value of doomed] **************************************************************************************************************** ok: [localhost] => { "doomed": true } TASK [print the message if we're doomed] ******************************************************************************************************** ok: [localhost] => { "msg": "We're f*****g doomed!" } TASK [print the message if we're not doomed] **************************************************************************************************** skipping: [localhost] PLAY RECAP ************************************************************************************************************************************** localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
And if I edit the playbook to change the value of the doomed var to False, and re-run this, I get the other message.
$ ansible-playbook conditional_task.yml PLAY [A playbook to conditionally print some debug messages] ************************************************************************************ TASK [print the value of doomed] **************************************************************************************************************** ok: [localhost] => { "doomed": false } TASK [print the message if we're doomed] ******************************************************************************************************** skipping: [localhost] TASK [print the message if we're not doomed] **************************************************************************************************** ok: [localhost] => { "msg": "Glory be to FSM, we are saved!" } PLAY RECAP ************************************************************************************************************************************** localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
Now, given all that, I would expect to be able to do something like this:
$ ansible-playbook -e doomed=False conditional_task.yml
and get the “not doomed” message. What I get instead is this:
$ ansible-playbook -e doomed=False conditional_task.yml PLAY [A playbook to conditionally print some debug messages] ************************************************************************************ TASK [print the value of doomed] **************************************************************************************************************** ok: [localhost] => { "doomed": false } TASK [print the message if we're doomed] ******************************************************************************************************** skipping: [localhost] TASK [print the message if we're not doomed] **************************************************************************************************** skipping: [localhost] PLAY RECAP ************************************************************************************************************************************** localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0
As you can see, neither when condition fires in this case. It’s almost like the variable isn’t there at all, or has some unexpected value, or something. But as the debug output shows, the variable is defined, and does have value “false” as expected. And yet the “when” conditionals ignore this.
It also doesn’t matter if I change the command line to
$ ansible-playbook -e doomed=True conditional_task.yml
In that case I still get neither conditional task executing. But again, the debug statement shows that doomed is present and has value of “true”.
I also tried re-writing my conditions as “doomed is true” and “doomed is false” respectively, instead of using the == operator and that didn’t make any difference. At this point I’m stuck. If anybody can explain this, it would be greatly appreciated.
Here’s my Ansible version information:
$ ansible-playbook --version ansible-playbook 2.10.8 config file = /extradrive1/development/experimental/ansible/ansible_experimental/ansible.cfg configured module search path = ['/home/prhodes/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules'] ansible python module location = /usr/lib/python3/dist-packages/ansible executable location = /usr/bin/ansible-playbook python version = 3.9.7 (default, Sep 10 2021, 14:59:43) [GCC 11.2.0]
This is all running on Pop! OS:
NAME="Pop!_OS" VERSION="21.10"
Advertisement
Answer
The problem is caused by the fact that the type of the extra variables passed to Ansible from the command line is always string. For example, the playbook below
shell> cat pb.yml - hosts: localhost vars: doomed: false tasks: - debug: var: doomed|type_debug - debug: msg: We are doomed when: doomed
works as expected. The type of the variable doomed is Boolean. Then, simply use the variable in the condition. No comparison is needed. The debug task will be skipped
shell> ansible-playbook pb.yml PLAY [localhost] ****************************************************************************** TASK [debug] ********************************************************************************** ok: [localhost] => doomed|type_debug: bool TASK [debug] ********************************************************************************** skipping: [localhost]
The thing will change when you pass the variable from the command line. Now, the type of the variable doomed is a string. A non-empty string will evaluate to True in the condition and the debug task will be executed
shell> ansible-playbook pb.yml -e doomed=false PLAY [localhost] ****************************************************************************** TASK [debug] ********************************************************************************** ok: [localhost] => doomed|type_debug: str TASK [debug] ********************************************************************************** ok: [localhost] => msg: We are doomed
There is a simple universal solution. Always cast such variables to bool. In addition to this, to simplify the code, default to false or true, depending on the use-case, instead of an explicit declaration. This way you write the code fast and the condition will always do what you want
shell> cat pb.yml - hosts: localhost tasks: - debug: msg: We are doomed when: doomed|d(false)|bool