Passing condition to "failed_when" as a variable

I try to pass a condition to failed_when as a variable in a role:

my-role/defaults/main.yml:

# list of commands to execute in form [command, changed_when, failed_when]

my_commands:
  - ["myapp init", "cmd_result.rc == 0", "(cmd_result.rc != 0) and ('Non-critical error' not in cmd_result.stdout) and ('Non-critical error' not in cmd_result.stderr)"]
  - [...]

This would allow me to loop through the command list with custom changed_when and failed_when conditions:

my-role/tasks/main.yml:

- name: Execute commands
  ansible.builtin.include_tasks: my-task.yml
  loop: "{{ my_commands }}"
  loop_control:
    loop_var: my_command

my-role/tasks/my-task.yml:

- name: My task
 ansible.builtin.command: "{{ my_command[0] }}"
  register: cmd_result
  changed_when: my_command[1]
  failed_when: my_command[2]

But I face following behaviour:

  • task fails with non-zero return code :cross_mark:
  • if I copy the condition line into failed_when statement explicitly, like failed_when: "(cmd_result.rc != 0) and ('Non-critical error' not in cmd_result.stdout) and ('Non-critical error' not in cmd_result.stderr)", task works :white_check_mark:

I suppose it may be that

  • the statement is not evaluated properly
  • cmd_result variable is not injected into the conditional statement

Can anyone explain this, and maybe suggest an elegant and functional way to get this work?

Thank you!

failed_when is evaluated to literal string ā€œ(cmd_result.rc != 0) and (ā€˜Non-critical error’ not in cmd_result.stdout) and (ā€˜Non-critical error’ not in cmd_result.stderr)ā€

What you want is to have same literal string ā€œ(cmd_result.rc != 0) and (ā€˜Non-critical error’ not in cmd_result.stdout) and (ā€˜Non-critical error’ not in cmd_result.stderr)ā€ to be used as condition.

In your case there is only one evaluation - my_command[2] is evaluated to string, ansible does just one evaluation.

The solution is


failed_when: "{{ my_command[2] }}"

In this case {{ my _command[2] }} is evaluated to as string (variable evaluation) and when string (condition you wrote) is evaluated as condition.

1 Like

Create the files on the fly. For example,

    - name: Create my commands.
      run_once: true
      delegate_to: localhost
      block:

        - name: Create directory for my commands.
          file:
            state: directory
            path: /tmp/my_commands

        - name: Create my commands.
          copy:
            dest: "{{ my_task }}"
            content: |
              ---
              - name: My task
                ansible.builtin.command: "{{ item.0 }}"
                register: cmd_result
                changed_when: "{{ item.1 }}"
                failed_when: "{{ item.2 }}"
          loop: "{{ my_commands }}"
          loop_control: 
            extended: true

Declare the filename as appropriate

my_task: "/tmp/my_commands/{{ ansible_loop.index }}-{{ item.0 }}.yml"

Then, execute the commands

    - name: Execute my commands.
      ansible.builtin.include_tasks: "{{ my_task }}"
      loop: "{{ my_commands }}"
      loop_control: 
        extended: true

[source code]