2

I have some ansible tasks that are run in two parts. The first part sets a condition, and the second part uses when to conditionally execute. Pseudo-example:

- name: check if installed
  command: (...)
  register: is_installed

- name: run install script
  when: is_installed.stdout == "yes"
  command: (...)

The second task shows as "skipped". This is not really right, I did not forgo the installation, but confirmed that it was already done, so it should show as "OK". It should show "skipped" only if the given host doesn't need this step. I know this is just cosmetic, but I would still like to know if there is a way to get it to say "OK".

I tried setting changed_when to False. While this sets the result to OK, it still runs the command. Somebody asked for an ok_when setting, but it was declined and I'm not sure the developers understood the request.

jdm
  • 191
  • 1
  • 11
  • From your description I understand that you like to install or configure "something" on the remote node(s). You like to confirm on the next run of the task that that is still the case. For me it sound like idempotent installing a package and to achieve this goal no `skipped` check of `ok_when` would be necessary. – U880D Feb 17 '22 at 13:01
  • Right, but the installation is not via apt, pip and so on, so I can't use a specialized task. In the general case I have two shell commands, one tells me if an action is neccessary, and one takes the action. It seems like this would be one of the most basic things you can do in ansible. – jdm Feb 18 '22 at 07:05
  • Since no information is provided what the "something" might be, I can only guess some cases. Since Ansible is a configuration management tool with which one declare a state, tasks are usually organized to make sure that something is in a certain state. With this in mind an installer (script) could run twice and be idempotent. In almost all cases no check would be necessary before. – U880D Feb 18 '22 at 07:24
  • It might also be possible to have a check just in your shell command like for `needs-restarting -r || /usr/sbin/shutdown -r +1`, let the command run if necessary and providing feedback for the Ansible environment back via return codes, stderr, stdout. – U880D Feb 18 '22 at 07:27

3 Answers3

3

I couldn't come up with general solution here. But if tasks actually installs something you could remove first part and add creates to second part to implement check.

- name: run install script
  creates: /path/to/installation
  command: (...)
Vladimir
  • 576
  • 3
  • 5
  • Yes, that seems to be the ideomatic solution. The alternative would be to write a custom module which knows itself how to check for "OK". It's weird that there is no simple way to define it in YAML. – jdm Feb 12 '22 at 18:05
  • Right, that's what the [`command` module parameter `creates`](https://docs.ansible.com/ansible/latest/collections/ansible/builtin/command_module.html#parameter-creates) or [`shell` module parameter `creates`](https://docs.ansible.com/ansible/latest/collections/ansible/builtin/shell_module.html#parameter-creates) are for. See also in example [ansible/modules/command.py](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/command.py), "_while the command itself is arbitrary and cannot be subject to the check mode semantics it adds C(creats)/C(removes) options as a workaround_". – U880D Feb 18 '22 at 08:17
1

when: evaluating to false results in a status skipped on that task. Printing something on skipped comes from the standard out callback plugin. For default based callbacks, this can be disabled globally with the display_skipped_hosts configuration item. See the docs: ansible-doc -t callback default

An elegant solution could be to wrap this thing in a real package manager, and install that. Several idempotent package manager modules exist for Ansible.

Or, the install script could be made safe to re-run, and returns with a zero code on success.


While I don't speak for the developers, they are cautious about adding features to core. And possibly they don't see a problem with a task reported as skipped, not every task of every play gets run.

when: controls if a task runs. changed_when: and failed_when: modify the status of a task after it runs, such as based off return code or standard out. Together these give control over if a task is a status of changed or failed, even for generic command tasks.

For more control over idempotency, use a less generic module. And if you still are offended by what is printed, consider a custom callback plugin.

John Mahowald
  • 32,050
  • 2
  • 19
  • 34
0

How about modifying command that is executed in "check if installed" task to exit 0 when installation is needed and exit 1 when it is not and leveraging shell && to controll execution of chained commands.

You could also reverse the logic (check_script exit 0 means its already there, exit 1 means missing stuff, needs install) and use || to glue chained scripts together.

Then you can do single task in your playbook:

- name: install stuff where needed
  shell: <check_command> && <install_command>
  register: installtask
  changed_when: "<check_stdout_indicating_install_required>" in installtask.stdout

This task will always run but depending on exit status of check script, install part will or will not be executed on target host. changed_when can controll displaying "OK" or "Changed" as outcome of this task depending on actual installation taking place or not.

If there is at least one file added to the system as a result of <install_script> then 'creates: <file/path>' as in the other answer will take care of whole scenario.

Kamil S.
  • 1
  • 1