Why not use the script module in ansible?

In most cases, ansible inbuilt modules are enough to do the job. However, in some cases, it’s either highly complex or impossible to achieve the goal using ansible inbuilt modules. In such cases, many users write their scripts(Eg: bash or python,Etc.), which is fine for most cases. However, there are some downsides to using scripts.

In this post, I will cover the following:
1. An example showing why using scripts may lead to uncertain behavior by the playbook.
2. Discuss a better alternative for scripts.

A sample script is called my_script.sh

#!/bin/bash
rm -rf /tmp/foo
echo "This is start of my script!"
ls -lrt /tmp/foo
echo "This is  end  of my script!"


A sample playbook calling the above sample script


Note that, in the below playbook, we call the above bash script using the script module. The playbook consists of two plays, 1st play is executing against the local host, and the 2nd play runs against remote hosts over SSH.

---
- name: Sample play to target localhost
  hosts: localhost
  tasks:
    - name: "Check for file presence"
      ansible.builtin.script: my_script.sh
      register: script_out

    - name: "print script output"
      debug:
        msg: "{{ script_out }}"

- name: Other play to target remote hosts
  hosts: webservers
  tasks:
    - name: "Check for file presence"
      ansible.builtin.script: my_script.sh
      register: script_out

    - name: "print script output"
      debug:
        msg: "{{ script_out }}"


The result:

PLAY [Sample play to target localhost] *********************************************************************************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************************************************************************************************
ok: [127.0.0.1]

TASK [Check for file presence] *****************************************************************************************************************************************************************************
changed: [127.0.0.1]

TASK [print script output] *********************************************************************************************************************************************************************************
ok: [127.0.0.1] => {
    "msg": {
        "changed": true,
        "failed": false,
        "rc": 0,
        "stderr": "ls: cannot access '/tmp/foo': No such file or directory\n",
        "stderr_lines": [
            "ls: cannot access '/tmp/foo': No such file or directory"
        ],
        "stdout": "This is start of my script!\nThis is  end  of my script!\n",
        "stdout_lines": [
            "This is start of my script!",
            "This is  end  of my script!"
        ]
    }
}

PLAY [Other play to target remote hosts] *******************************************************************************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************************************************************************************************
ok: [192.168.122.83]

TASK [Check for file presence] *****************************************************************************************************************************************************************************
changed: [192.168.122.83]

TASK [print script output] *********************************************************************************************************************************************************************************
ok: [192.168.122.83] => {
    "msg": {
        "changed": true,
        "failed": false,
        "rc": 0,
        "stderr": "Shared connection to 192.168.122.83 closed.\r\n",
        "stderr_lines": [
            "Shared connection to 192.168.122.83 closed."
        ],
        "stdout": "This is start of my script!\r\nls: cannot access '/tmp/foo': No such file or directory\r\nThis is  end  of my script!\r\n",
        "stdout_lines": [
            "This is start of my script!",
            "ls: cannot access '/tmp/foo': No such file or directory",
            "This is  end  of my script!"
        ]
    }
}

PLAY RECAP *************************************************************************************************************************************************************************************************
127.0.0.1                  : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
192.168.122.83             : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   


The Problem:


If you notice, the stderr generated by the ls command is merged into the stdout. Worse, this only happens for the tasks executed over SSH.

cannot access '/tmp/foo': No such file or directory
"stdout": "This is start of my script!\r\nls: cannot access '/tmp/foo': No such file or directory\r\nThis is  end  of my script!\r\n",
        "stdout_lines": [
            "This is start of my script!",
            "ls: cannot access '/tmp/foo': No such file or directory",
            "This is  end  of my script!"
        ]


Reason: The following snippet from ansible documentation

NOTES:
      * It is usually preferable to write Ansible modules rather than pushing scripts. Convert your script to an Ansible module for bonus points!
      * The `ssh' connection plugin will force pseudo-tty allocation via `-tt' when scripts are executed. Pseudo-ttys do not have a stderr channel and all
        stderr is sent to stdout. If you depend on separated stdout and stderr result keys, please switch to a copy+command set of tasks instead of using
        script.
      * If the path to the local script contains spaces, it needs to be quoted.
      * This module is also supported for Windows targets.


Better approach?


Consider converting your scripts into custom modules. They are more ansible native and plugged into the ansible ecosystem. You can find a simple module example on this page.

Leave a Comment

Your email address will not be published.

Scroll to Top