Score:0

How to force Ansible 'shell' task to change state with 'changed_when' condition?

ng flag

I am running script with Ansible task which installs some repositories and my goal is to show changed state when running change mode to notify, that script will run if check mode will not be activated.

My first task is to check existence of this repository:

- name: Check repository existence
  become: yes
  shell: yum repolist -v | grep some-repository
  register: repo_exists
  check_mode: false
  failed_when: false
  changed_when: false

This task should run even in check mode, so I can operate with repo_exists variable in next tasks. This works fine when I run Ansible in check mode, this is the output:

"msg": {
    "changed": false,
    "cmd": "yum repolist -v | grep some-repository",
    "delta": "0:00:02.022965",
    "end": "2023-06-12 13:06:54.611504",
    "failed": false,
    "failed_when_result": false,
    "msg": "non-zero return code",
    "rc": 1,
    "start": "2023-06-12 13:06:52.588539",
    "stderr": "",
    "stderr_lines": [],
    "stdout": "",
    "stdout_lines": []
}

My script should run only when repository is missing:

- name: Add repository
  become: yes
  shell: echo "install repository"
  changed_when: "repo_exists.rc != 0"
  when: "repo_exists.rc != 0"

when condition should ensure, task will run only if rc is not zero, which means, repository was not in repolist. changed_when should show state CHANGED even in check mode rc is not zero.

Instead of expected result, I can see state skipping:

{
  "changed": false,
  "cmd": "echo \"install repository\"",
  "delta": null,
  "end": null,
  "msg": "Command would have run if not in check mode",
  "rc": 0,
  "start": null,
  "stderr": "",
  "stderr_lines": [],
  "stdout": "",
  "stdout_lines": []
}

I do not understand, why task is in skipping state and what I do wrong? Is this right way how to see shell task in change state when running check mode?

Score:0
ca flag

I do not understand, why task is in skipping state

As already mentioned within the Return Values

"msg": "Command would have run if not in check mode"

it is because you are Using check mode. If you really want to prevent check mode on tasks you would need to use

---
- hosts: localhost
  become: false
  gather_facts: false

  tasks:

  - name: Check repository existence
    shell:
      cmd: yum repolist -v | grep not-existing
    register: repo_exists
    # Since it is a reporting task
    # which needs to deliver a result in any case
    failed_when: repo_exists.rc != 0 and repo_exists.rc != 1
    changed_when: false
    check_mode: false

  - name: Add repository
    shell:
      cmd: "echo {{ repo_exists.stdout_lines }}"
    when: repo_exists.rc != 0
    changed_when: repo_exists.rc != 0
    check_mode: false
ansible-playbook main.yml --check -v

resulting into an output of

TASK [Check repository existence] ************
ok: [localhost] => changed=false
  cmd: yum repolist -v | grep not-existing
  delta: '0:00:05.598043'
  end: '2023-06-13 18:00:31.862501'
  failed_when_result: false
  msg: non-zero return code
  rc: 1
  start: '2023-06-13 18:00:26.264458'
  stderr: ''
  stderr_lines: <omitted>
  stdout: ''
  stdout_lines: <omitted>

TASK [Add repository] ************************
changed: [localhost] => changed=true
  cmd: echo []
  delta: '0:00:00.014928'
  end: '2023-06-13 18:00:32.318558'
  msg: ''
  rc: 0
  start: '2023-06-13 18:00:32.303630'
  stderr: ''
  stderr_lines: <omitted>
  stdout: '[]'
  stdout_lines: <omitted>

PLAY RECAP ***********************************
localhost                  : ok=2    changed=1

But keep in mind, that wouldn't be a Dry Run for the shell module anymore.


This will run it every time - not required. My goal is to show if script will run out of check mode - so change state will be shown.

What you are requesting is that the shell command or script itself needs to know first if it is running in check_mode: true and second behave different if so. In other words, your script would need to support check_mode. Whereby this is something which is the shell module doing partially, it can't act for or on behalf of your commands and scripts.

Maybe I understand it wrongly and my defined output is not possible to implement?

You would need to implement at least something like

  - name: Add repository
    shell:
      cmd: "{{ ansible_check_mode | lower }} || echo {{ repo_exists.stdout_lines }}"
    changed_when: "repo_exists.rc != 0"
    when: repo_exists.rc != 0
    check_mode: false

to make the command aware of check_mode, let it run or not and resulting into an output of

TASK [Add repository] ***************
changed: [localhost] => changed=true
  cmd: true || echo []
  delta: '0:00:00.023104'
  end: '2023-06-15 11:00:00.550097'
  msg: ''
  rc: 0
  start: '2023-06-15 11:00:00.526993'
  stderr: ''
  stderr_lines: <omitted>
  stdout: ''
  stdout_lines: <omitted>

TASK [Add repository] ***************
changed: [localhost] => changed=true
  cmd: false || echo []
  delta: '0:00:00.014626'
  end: '2023-06-15 11:00:00.098413'
  msg: ''
  rc: 0
  start: '2023-06-15 11:00:00.083787'
  stderr: ''
  stderr_lines: <omitted>
  stdout: '[]'
  stdout_lines: <omitted>

As this seems to become already very and over complex you might need to overthink what you really trying to achieve. It seems like a X/Y problem and there are already modules which are doing the work, just adding a repository yum_repository module – Add or remove YUM repositories with full check_mode support or for updating ini_file module – Tweak settings in INI files. There is absolut no need for a "CHECK ADD IF ONLY WHEN" or to re-implement already existing functionality.

dorinand avatar
ng flag
Thank you for your reply. This will run it every time - not required. My goal is to show if script will run out of check mode - so change state will be shown. This is quiet unclean if the script will run. Also, skipping message is seen, but read and check every skip reason is quiet not best option I think. I would expect to see it in RECAP, how many changes will be performed.
dorinand avatar
ng flag
From docs: **changed_when** - This lets you determine, based on return codes or output, whether a change should be reported in Ansible statistics and whether a handler should be triggered or not. Also, good example is [Ansible changed_when and failed_when examples](https://www.middlewareinventory.com/blog/ansible-changed_when-and-failed_when-examples/). Maybe I understand it wrongly and my defined output is not possible to implement?
I sit in a Tesla and translated this thread with Ai:

mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.