Ever wanted to make your command or shell call in Ansible playbook idempotent and avoid those nasty yellow changed lines in run logs? Wait no more! Here's bash trick for you:

- name: Put docker package on hold
  shell: >
         apt-mark showholds | grep -q docker-ce
         && echo -n HOLDED
         || apt-mark hold docker-ce
  register: docker_hold
  changed_when: docker_hold.stdout != 'HOLDED'

What do we do here?

  1. We execute "check" command apt-mark showholds | grep -q docker-ce.
    Because of quiet grep, this command does not print anything, but exits with zero-code if docker-ce is already on hold or with non-zero-code if docker-ce is not found in the list of frozen packages.

  2. Next we use shell control operator && – this ensures that echo -n HOLDED is executed if and only if previous command has zero exit code (package is already on hold).

  3. Next we use shell control operator || – this ensures that apt-mark hold docker-ce is executed if and only if previous command has non-zero exit code (package not found in a list of frozen packaged).

So it's a kind of if (test) then (skip) else (do things) statement.

This will prevent unnecessary command execution if test passes, and also signals to Ansible that nothing changed.