Ansible Shell Module: Run Remote Commands

By
Marko Aleksic
Published:
November 20, 2025
Topics:

Ansible handles configuration management using specialized modules (e.g., for managing services or packages). When tasks require remote execution of arbitrary shell syntax, users can turn to one of the two primary generic modules: the ansible.builtin.command module for simple command execution, and the ansible.builtin.shell module for complex commands and scripts.

This article will explain how to use the Ansible shell module to execute shell operations on remote machines.

Ansible shell module: Run remote commands.

Prerequisites

What Is Ansible Shell Module?

The Ansible shell module runs a command string on a remote host by passing it to the host's shell interpreter. By default, Ansible attempts to use /bin/sh or the remote user's configured default shell.

Because the command is interpreted by the shell, it can contain complex syntax that the simpler command module cannot handle. This capability makes the shell module one of the most widely used modules in the Ansible ecosystem.

Note: The shell module is part of the standard Ansible collection, so it is available out of the box.

Ansible Shell Module vs. Other Modules

Understanding the distinction between the shell module and other command-execution modules is essential for writing secure and efficient Ansible playbooks.

The following table compares the shell module and four other popular Ansible modules:

ModuleExecution MethodShell FeaturesUse Case
shellPasses the command to the default remote shell interpreter (e.g., /bin/sh).Available.Executing complex scripts or commands with shell syntax.
commandExecutes the command directly, bypassing the shell.Not available.Simple command execution (e.g., ls, mkdir) where shell features are not needed. Preferred for simple tasks.
rawExecutes the command directly via SSH/WinRM.Not available.Helps with remote nodes that lack a Python interpreter.
scriptTransfers a local script file to the remote host and then executes it using the remote host's default shell.Available (for the script's content)Running large, complex logic defined in a local .sh or similar file.
expectExecutes an interactive command and allows the playbook to wait for specific output (prompts) and send pre-defined responses.Available (through the executed command's shell)Interacting with older or legacy CLI programs that require interactive input and don't support non-interactive automation.

When to Run Ansible Commands Using Shell Module?

Use the shell module only when the command structure requires a shell interpreter. The following scenarios explicitly require the shell module:

  • Piping output. Using the pipe symbol (|) to send the output of one command as input to another, such as dmesg | grep -i error.
  • Input/output redirection. Using symbols like overwrite (>), append (>>), or input from file (<) to manage command I/O, e.g., echo "Hello" >> /tmp/log.txt.
  • Variable expansion. Accessing shell variables like $HOME, $PATH, or user-defined variables within the command string.
  • Chained/conditional execution. Combining multiple commands using shell control operators like semicolon (;), double ampersand (&&) for logical AND, or double pipe (||) for logical OR, e.g., mkdir -p /app/data && chown user:group /app/data.
  • Background processes. Running commands in the background using the ampersand (&).

If the command is a simple executable name followed by arguments (e.g., ls -l /tmp), the command module is appropriate and safer.

Ansible Shell Module Syntax

The shell module syntax accepts the command string as its primary input. It also supports several arguments that modify the execution context.

For quick, one-off tasks, the shell module is called directly from the command line using the ad-hoc syntax:

ansible [pattern] -m [module_name] -a '[module_arguments]'

Shell features like pipes (|) and variable expansion often require quoting or escaping to ensure they are passed correctly to the remote shell.

The following example shows the backslash used before the awk command variable:

ansible webservers -m shell -a 'df -h /var | awk "{print \$5}"'
Output of a command using ad-hoc Ansible syntax, featuring the Shell module.

Within an Ansible Playbook, the shell module is defined as a task. Using the args section allows you to pass additional parameters that modify the command's execution context.

The code below shows the syntax for each parameter:

- name: [command_description]
  ansible.builtin.shell: "[command_string]"
  args:
    chdir: [path]
    creates: [path]
    removes: [path]
    warn: [boolean]
    executable: [path_to_interpreter]
  register: [command_output]

Key arguments for the shell module include:

  • chdir (string). Specifies the directory on the remote node where the shell command executes.
  • creates (string). The shell command runs only if the specified file does not exist on the remote node, making the task idempotent.
  • removes (string). The shell command runs only if the specified file exists on the remote node, which is helpful for cleanup tasks.
  • warn (boolean). Controls the warning message that suggests using the command module instead of the shell. Default is true. Setting it to false suppresses the warning, though proper module selection is still recommended.
  • executable (string). Specifies the exact shell interpreter to use, overriding the system's default, e.g., /bin/bash instead of /bin/sh. It allows the use of shell-specific features (such as bash arrays) if required.

Ansible Shell Module Examples

The following examples demonstrate how to use key arguments like chdir, creates, and shell redirection operators to perform complex tasks.

Change Directory and Execute Command

Use the chdir argument to navigate to a specific directory before running a sequence of commands.

- name: Run git pull inside the application directory
  shell: "git pull origin main && npm install"
  args:
    chdir: /var/www/my_app

Use Conditional Chaining with creates

In the example below, the creates parameter ensures the setup runs only once by instructing Ansible to run the complex command only if the /tmp/setup_complete file does not exist. The ('&&') symbol guarantees that the touch command runs only if the wget command succeeds.

- name: Download and unpack a tarball if it hasn't been done
  shell: "wget -q -O- http://example.com/file.tar.gz | tar xzf - -C /opt/data && touch /tmp/setup_complete"
  args:
    creates: /tmp/setup_complete

The following example uses the shell module to clone the test repository if and only if it does not already exist, then prints the result of the cloning task.

- name: Clone the repository if it does not exist
  ansible.builtin.shell: "git clone https://github.com/example/test.git ."
  args:
    chdir: /opt/myproject/
    creates: /opt/myproject/repo
  register: git_clone_result

- name: Display the result of the cloning task
  ansible.builtin.debug:
    msg: "Cloning Status: {{ git_clone_result.changed | ternary('Cloned successfully', 'Already exists/Skipped') }}"

Redirect Output for System Logging

The code below executes the date command and appends its output, along with a custom message, to a log file on the remote server.

- name: Server Configuration and Logging
  hosts: webservers
  gather_facts: yes
  vars:
    deployment_status_msg: "Deployment of service A finished successfully"
    log_file_path: "/var/log/application.log"
  tasks:
    - name: Log a status update to a system file
      ansible.builtin.shell: "echo '{{ deployment_status_msg }} at $(date)' >> {{ log_file_path }}"

Alternative Ways to Run Remote Commands with Ansible

Although the shell module is versatile, exclusive reliance on it introduces risks and often leads to less readable, less idempotent playbooks. Ansible provides purpose-built modules for executing remote code, each with specific advantages.

Ansible Command Module

The command module is the standard choice for running simple executables. Because it bypasses the shell, it prevents shell injection vulnerabilities and avoids unexpected behavior caused by shell environment settings.

Ansible Expect Module

The expect module addresses interactive commands. It operates by sending input (using the send argument) after matching a specific pattern (using the regexp argument) in the command's output. The module is crucial for automation, where a command prompts for confirmation or credentials that cannot be passed as direct arguments.

Ansible Script Module

The script module allows execution of entire script files (such as Perl, Python, or shell scripts) stored locally on the Ansible control node. Ansible transfers the script to the remote node and executes it using the remote shell. This approach is better for long, complex logic than using a single shell module string.

Ansible Raw Module

The raw module provides the lowest-level remote execution. It executes a command directly via SSH or WinRM, without loading the Python interpreter on the remote host. This property is necessary for bootstrapping environments that lack a Python installation, such as embedded systems or a fresh, minimal install where the Python requirement for standard Ansible modules has not yet been met.

Conclusion

This article should help you to confidently use the Ansible Shell module to execute complex commands in your playbooks. While the Ansible Shell module is essential for complex operations that require native shell features like piping, best practices prioritize dedicated Ansible modules for security and idempotency.

Next, learn how to create a file in Ansible.

Was this article helpful?
YesNo