0

I have a task in Ansible to install SASS utilities via RubyGems option, which works well without any issues. If I re-run the same playbook again, it will try to re-install this utility again.

In this case, how to run it once that are using command or shell usage in Ansible playbook. I have somehow handled it not to run the installation by using 'when' option, but need a guidance for better logic/implementation

Task Info:

- name: Install SASS packages by using npm utility.
  command: /usr/bin/npm install -g sass

To eliminate re-running the above command task in Ansible playbook, I am using below validation logic and added 'when' option as provided below. Is this fine or do we have a better way of handling this one?

- name: Validation of SASS packages availability.
  shell: /usr/local/bin/sass --version
  register: result
- debug:
    msg: "{{ result.stdout }}"

- name: Install SASS packages by using npm utility.
  command: /usr/bin/npm install -g sass
  when: "'No such file or directory' in result.stdout"

Results:

TASK [mean-stack : Validation of SASS packages availability.] ************************************************************************************
changed: [linuxosdev003.local.lab]

TASK [mean-stack : debug] ************************************************************************************************************************
ok: [linuxosdev003.local.lab] => {
    "msg": "1.47.0 compiled with dart2js 2.15.1"
}

TASK [mean-stack : Install SASS packages by using npm utility.] **********************************************************************************
skipping: [linuxosdev003.local.lab]

PLAY RECAP ***************************************************************************************************************************************
linuxosdev003.local.lab    : ok=6    changed=1    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0

3 Answers3

2

You should avoid command or shell whenever possible, if you want an idempotent playbook. Especially your example - remove sass and try your playbook. It should fail. Either check if the file exists

- name: "Check, if file exists"
  stat:
    path: "/usr/local/bin/sass"
  register: sass_installed

But - as already commented - use an ansible module to install NPM packages, like you would do with system packages (package or yum or apt) or Python modules (with pip). The module is also available in the last RedHat releease 2.9 under https://docs.ansible.com/ansible/2.9/modules/npm_module.html and should be as easy:

- name: "Install NPM package: sass"
  npm:
    name: "sass"

If you need to install more then one NPM package, you could loop over them

- name: "Install NPM packages"
  npm:
    name: "{{ item }}"
  loop:
    - "package1"
    - "package2"
TRW
  • 876
  • 7
  • 23
1

As @β.εηοιτ.βε and @TRW already mentioned, using a module will eliminate the need for such checks.

However, if it is as simple as checking whether a path exists, then command module itself can be used like:

- name: Install SASS packages by using npm utility.
  command:
    cmd: /usr/bin/npm install -g sass
    creates: /usr/local/bin/sass

This is equal to your when condition, i.e. command will run when the path given in creates is not present.

seshadri_c
  • 6,906
  • 2
  • 10
  • 24
0

Whereby I prefer the solution from @β.εηοιτ.βε and @TRW if package managers are available for the tasks, sometimes it might be necessary to stay on the shell module. For such cases I use the similar approach like in the original question.

---
- hosts: test.example.com
  become: no
  gather_facts: no

  tasks:

  - name: Gather installed Java version, if there is any
    shell:
      cmd: java -version 2>&1 | head -1 | cut -d '"' -f 2
    register: result
    check_mode: false
    changed_when: false
    failed_when: result.rc != 0 and result.rc != 127

  - name: Set default version, if there is no
    set_fact:
      result:
        stdout_lines: "0.0.0_000"
    when: "'command not found' in result.stdout"
    check_mode: false

  - name: Report result
    debug:
      msg: "{{ result.stdout_lines }}"
    check_mode: false

Based on the installed version an installer or updater would be called to install or update to the latest version if necessary.

Thanks to

U880D
  • 8,601
  • 6
  • 24
  • 40
  • 1
    Great responses from ```TRW β.εηοιτ.βε seshadri_c``` and also from @U880D on this question. Though I got an answer/logic to use 'when' in the playbook, I will stick with npm module to make more idempotent. Thanks again! – Raghavendra Guttur Jan 10 '22 at 06:39