How to Use the Ansible Shell Module to Execute Remote Commands

Ansible Shell Module Examples

The Ansible shell module is used to execute shell commands on remote hosts. It provides a way to run any command that you could run in a shell on a remote system. This module can execute complex command sequences and handle pipes and redirections, making it highly versatile.

This guide will provide an in-depth look at the Ansible shell module with practical examples to help you understand its capabilities and usage.

When to Use the Shell Module

Use the shell module when:

  • You need to execute a command that requires shell-specific functionality (e.g., pipes, redirections).
  • No specific Ansible module exists for the task you want to perform.
  • You want to run a series of commands or a command sequence that involves shell features.

Basic Usage

To use the shell module, you need to define it within a playbook. Here’s a basic example:

- name: Basic Shell Command Example
  hosts: all
  tasks:
    - name: Run a simple command
      shell: echo "Hello, World!"

In this example playbook, the echo “Hello, World!” command is executed on all targeted hosts.

Advanced Usage

The shell module can handle more complex scenarios by utilizing various parameters and features:

  • chdir: Change to this directory before running the command.
  • creates: A filename, when it already exists, will skip the command.
  • removes: A filename, when it does not exist, will skip the command.
  • stdin: Set the command’s standard input.

Here’s an advanced example:

- name: Advanced Shell Command Example
  hosts: all
  tasks:
    - name: Run a command in a specific directory
      shell: ls -la
      args:
        chdir: /tmp

This playbook runs the ls -la command in the /tmp directory on all targeted hosts.

Example 1: Running a Simple Command

Here’s an example playbook that uses the Ansible shell module to run the date command on all target hosts:

- name: Run a simple command
  hosts: all
  tasks:
    - name: Get the current date and time
      shell: date
      register: current_date_time

    - name: Display the current date and time
      debug:
        var: current_date_time.stdout

This playbook executes the date command on all specified hosts, which displays the current date and time.

ansible shell module example

Example 2: Show Memory Usage

In this example, we will use free -h command to show memory usage. The command output is registered in the memory_usage variable and displayed in the next task.

- name: Show memory usage
  hosts: all
  tasks:
    - name: Get memory usage
      shell: free -h
      register: memory_usage

    - name: Display memory usage
      debug:
        msg: "Memory usage on {{ inventory_hostname }}: {{ memory_usage.stdout }}"

The above playbook will display the following output.

PLAY [Show memory usage] **********************************************************************

TASK [Gathering Facts] ************************************************************************
ok: [host1]
ok: [host2]

TASK [Get memory usage] ***********************************************************************
changed: [host1]
changed: [host2]

TASK [Display memory usage] *******************************************************************
ok: [host1] => {
    "msg": "Memory usage on host1:               total        used        free      shared  buff/cache   available\nMem:          7.7G        1.2G        5.9G        124M        618M        6.0G\nSwap:         2.0G          0B        2.0G"
}
ok: [host2] => {
    "msg": "Memory usage on host2:               total        used        free      shared  buff/cache   available\nMem:         15.5G        4.5G        9.0G        512M        2.0G       12.5G\nSwap:         4.0G        512M        3.5G"
}

PLAY RECAP ************************************************************************************
host1                      : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
host2                      : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 

In this playbook:

  • The shell module runs the free -h command to get memory usage and stores the output in the memory_usage variable.
  • The debug module is then used to display the output of the memory_usage.stdout variable, which contains the command’s result.

Example 3: Run Multiple Commands

In this example, we will use the shell module to run multiple commands on all target hosts:

- name: Run multiple commands
  hosts: all
  tasks:
    - name: Execute multiple commands
      shell: |
        echo "Listing files in the current directory:"
        ls -l
        echo "Displaying disk usage:"
        df -h
      register: command_output

    - name: Display the output of the commands
      debug:
        var: command_output.stdout

When you run the above playbook, you will see the following result:

PLAY [Run multiple commands] **********************************************************************

TASK [Gathering Facts] ****************************************************************************
ok: [host1]
ok: [host2]

TASK [Execute multiple commands] ******************************************************************
changed: [host1]
changed: [host2]

TASK [Display the output of the commands] *********************************************************
ok: [host1] => {
    "command_output.stdout": "Listing files in the current directory:\ntotal 0\ndrwxr-xr-x  2 user user 64 Oct  9 13:30 folder1\ndrwxr-xr-x  2 user user 64 Oct  9 13:30 folder2\nDisplaying disk usage:\nFilesystem      Size  Used Avail Use% Mounted on\n/dev/sda1        50G  25G  25G  50% /\n"
}
ok: [host2] => {
    "command_output.stdout": "Listing files in the current directory:\ntotal 0\ndrwxr-xr-x  2 user user 64 Oct  9 13:30 folder1\ndrwxr-xr-x  2 user user 64 Oct  9 13:30 folder2\nDisplaying disk usage:\nFilesystem      Size  Used Avail Use% Mounted on\n/dev/sda1        50G  25G  25G  50% /\n"
}

PLAY RECAP *****************************************************************************************
host1                      : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
host2                      : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

In this playbook:

  • The shell module runs a series of commands using a multi-line (|) block.
  • It first prints a message listing the files in the current directory with ls -l.
  • Then, it displays disk usage information with df -h.

Example 4: Check If Apache Service is Running

Here’s an example playbook to check if the Apache service is running:

- name: Check if Apache service is running
  hosts: all
  tasks:
    - name: Check Apache service status
      shell: systemctl is-active apache2
      register: apache_status
      ignore_errors: yes

    - name: Display Apache service status
      debug:
        msg: "Apache service status: {{ apache_status.stdout }}"

In this playbook:

  • shell module runs systemctl is-active apache2 to check if the Apache service is active. The result is stored in the apache_status variable.
  • ignore_errors: yes option ensures that the playbook continues running even if the command fails (e.g., if Apache is not installed).
  • debug module then displays the status of the Apache service.

Example 5: Using creates to Check File Existence

This example demonstrates how to conditionally run a command based on the existence of a file.

- name: Using creates to Check File Existence
  hosts: all
  tasks:
    - name: Create a file if it does not exist
      shell: touch /tmp/unique_file.txt
      args:
        creates: /tmp/unique_file.txt

The command touch /tmp/unique_file.txt will only run if the file /tmp/unique_file.txt does not exist.

Example 6: Combining Shell Commands with Output Processing

This playbook finds the largest file in /var/log, sorts the results by size, and displays the largest file.

- name: Process command output
  hosts: all
  tasks:
    - name: Find the largest file in /var/log
      shell: |
        find /var/log -type f -exec ls -lh {} + | sort -k 5 -rh | head -n 1
      register: largest_file

    - name: Display the largest file
      debug:
        msg: "The largest file in /var/log is: {{ largest_file.stdout }}"

In this example:

  • The first task searches for all files in /var/log, lists them with detailed information, sorts them by size in descending order, and retrieves the largest file.
  • The second task uses the debug module to display the name and details of the largest file found in /var/log.

Conclusion

The Ansible shell module is a handy tool that allows you to execute arbitrary shell commands on remote hosts. While it offers great flexibility, it is important to use it judiciously and follow best practices to ensure your playbooks remain secure and maintainable.

FAQs

1. How is the shell module different from the command module in Ansible?

The shell module allows commands with shell-specific features like pipes, redirects, and variables, whereas the command module only runs commands without a shell.

2. How do I handle errors when using the shell module?

You can handle errors using the ignore_errors parameter or by setting failed_when to customize failure conditions.

3. Can I capture the output of a command executed by the shell module?

Yes, use the register keyword to capture the output of the command into a variable for use in subsequent tasks.

About Hitesh Jethva

I am Hitesh Jethva, Founder and Author at Code2DevOps.com. With over 15 years of experience in DevOps and open source technologies, I am passionate about empowering teams through automation, continuous integration, and scalable solutions.

View all posts by Hitesh Jethva