Different messages for different conditions in one assert

There was discussion during CfgMgmtCamp that it is not possible to have different messages for different conditions in one assert action with ansible.

I do not agree with that!
Here is the code:

- name: Different messages for different conditions in one asserts
  hosts: localhost
  gather_facts: false
  vars:
    one: true
    two: false
  tasks:
    - name: Simple assert
      ansible.builtin.assert:
        that: conditions is ansible.builtin.all
        fail_msg: "One of the conditions failed"
        success_msg: "All of the conditions successful"
      failed_when: false
      vars:
        conditions:
          - "{{ one }}"
          - "{{ two }}"

    - name: Extended assert
      ansible.builtin.assert:
        that: extended_conditions | map(attribute='condition') is ansible.builtin.all
        fail_msg: "Following conditions have failed: {{ extended_conditions | rejectattr('condition') | map(attribute='msg') | join('; ') }}"
        success_msg: "Following conditions are succesful: {{ extended_conditions | selectattr('condition') | map(attribute='msg') | join('; ') }}"
      failed_when: false
      vars:
        extended_conditions:
          - msg: "message"
            condition: "{{ one or two }}"
          - msg: "this condition failed"
            condition: "{{ two }}"

This code work with both ansible-core 2.20 and 2.16 (I’ve also checked other versions, here just for brevity)

bash-5.2$ uv run --with ansible-core==2.16.16 ansible-playbook playbook.yml 
Installed 9 packages in 17ms
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

PLAY [Different messages for different conditions in one asserts] ***************************************************************************

TASK [Simple assert] ************************************************************************************************************************
ok: [localhost] => {
    "assertion": "conditions is ansible.builtin.all",
    "changed": false,
    "evaluated_to": false,
    "failed_when_result": false,
    "msg": "One of the conditions failed"
}

TASK [Extended assert] **********************************************************************************************************************
ok: [localhost] => {
    "assertion": "extended_conditions | map(attribute='condition') is ansible.builtin.all",
    "changed": false,
    "evaluated_to": false,
    "failed_when_result": false,
    "msg": "Following conditions have failed: this condition failed"
}

PLAY RECAP **********************************************************************************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

bash-5.2$ uv run --with ansible-core==2.20.2 ansible-playbook playbook.yml 
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

PLAY [Different messages for different conditions in one asserts] ***************************************************************************

TASK [Simple assert] ************************************************************************************************************************
ok: [localhost] => {
    "assertion": "conditions is ansible.builtin.all",
    "changed": false,
    "evaluated_to": false,
    "failed_when_result": false,
    "msg": "One of the conditions failed"
}

TASK [Extended assert] **********************************************************************************************************************
ok: [localhost] => {
    "assertion": "extended_conditions | map(attribute='condition') is ansible.builtin.all",
    "changed": false,
    "evaluated_to": false,
    "failed_when_result": false,
    "msg": "Following conditions have failed: this condition failed"
}

PLAY RECAP **********************************************************************************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   


There are some caveats of course, if you look closely, each condition is evaluated as variable (inside double curly brackets), not as condition (with out any curly brackets):

    extended_conditions:
      - msg: "message"
        condition: "{{ one or two }}"
      - msg: "this condition failed"
        condition: "{{ two }}"

But I am yet to see condition that cannot be written this way (as variable)

6 Likes

Thanks for your examples, good catch (and good Jinjas there) !

I was at that same talk, for anyone wondering here are the links to the presentation :

He has a survey up for feedbacks on the Github link above, you should consider linking that post there !

2 Likes