How to report an Ansible command/shell task as changed in check mode

ansibleansible-playbook

In my Ansible playbook I have this:

- name: "A: Check to see if we need to run task B"
  [... implementation omitted, not relevant ...]
  register: task_a_result
  check_mode: no  # even run in check mode
  changed_when: no  # this only reads/checks stuff

- name: "B: Write x to file"
  shell: "echo {{ my_var|quote }} > /path/to/file"
  when: task_a_result.stdout_lines[0].startswith('ABCDEF')
  changed_when: yes  # when run, it always changes the state

I do not want to run B in check mode (hence, no check_mode: no), but I want to report it as changed if it would have been run in non-check mode. I want this behaviour, because I do not want surprises when running in non-check mode. However, despite having changed_when: yes and having set a conditional myself, Ansible keeps showing the task as skipped and thus unchanged to me:

skipping: [myhost] => changed=false 
  msg: skipped, running in check mode

(The above in check mode, and it reports 'changed' in regular non-check mode.)

I found this bug report that appears misinterpreted and closed inappropriately: 14950, but I cannot comment on that any longer.

Am I overlooking something basic? Other modules usually report a "would have changed" status perfectly fine in Ansible, but is this also possible for shell/command?

Using Ansible 2.7.12 and 2.8.2 yields the same results.

I am hoping to avoid nasty hacks in the command itself like:

- name: "B: Write x to file"
  shell: "echo {{ my_var|quote }} {{ '>' if task_a_result.stdout_lines[0].startswith('ABCDEF') else '' }} /path/to/file"
  when: task_a_result.stdout_lines[0].startswith('ABCDEF')
  changed_when: yes

And yes I know I can write to a file with the copy/template modules, but I can't seem to overwrite files with it, e.g. echo 1234 > /sys/module/zfs/parameters/zfs_arc_max, because copy/template would attempt to replace the file it seems and setting a kernel parameter wouldn't work like that. And no, this kernel module parameter is not exposed through sysctl on Linux.

Best Answer

I know this is kind of old, but I'm looking for the solution to this problem. I'm also tired of surprises with command module as OP.

What if we change the 'command' part so that it executes a different command in check mode. And we run the task always, even in check mode.

Note: Before you complain, I know this is a bad example. I know there is a 'creates' option in 'command' module. This is just to give an example.

This is my solution so far:

- name: Get some info
  stat: {path: /tmp/somefile}
  register: file_info

- name: Run the command conditionally
  command:
    "{{ 'true' if (file_info['stat']['exists'] or ansible_check_mode)
    else 'touch /tmp/somefile' }}"
  changed_when: not file_info['stat']['exists']
  check_mode: false

It works, but is bloated.

Please tell me if there are better solutions.