Ansible Command Modules
Introduction
Ansible command modules are essential components in the Ansible ecosystem that allow you to execute commands on remote hosts. While Ansible's philosophy encourages using specialized modules for most tasks (like apt
for package management or file
for file operations), there are situations where you need to run direct commands on target systems.
In this guide, we'll explore the three primary command modules:
- The
command
module - The
shell
module - The
raw
module
Each has specific use cases, capabilities, and limitations that are important to understand for effective automation.
The Command Module
The command
module is the most basic way to execute commands on remote nodes. It runs the specified command without using the shell, which means shell operators like |
(pipe), >
(redirection), and &
(background tasks) don't work.
Basic Usage
- name: Run a simple command
command: uptime
Output Handling
You can capture the command's output by registering a variable:
- name: Get system uptime
command: uptime
register: system_uptime
- name: Display the captured output
debug:
var: system_uptime.stdout
Parameters and Options
The command module offers several useful parameters:
- name: Run command with working directory and environment variables
command: ls -la
args:
chdir: /var/log
creates: /path/to/sentinel_file
removes: /path/to/file_to_remove
environ:
LANG: C
Key parameters include:
chdir
: Changes to this directory before running the commandcreates
: Skip the command if this file existsremoves
: Skip the command if this file doesn't existenviron
: Set environment variables
The Shell Module
The shell
module is similar to the command module but runs commands through a shell (/bin/sh
on Unix/Linux), allowing you to use shell features like pipes, redirections, and environment variables.
Basic Usage
- name: Find all log files and count lines
shell: find /var/log -name "*.log" | xargs wc -l
register: log_count
Using Shell Features
Shell module allows powerful operations combining multiple commands:
- name: Get top 5 memory-consuming processes
shell: ps aux | sort -rk 4 | head -5
register: top_memory_processes
- name: Display results
debug:
var: top_memory_processes.stdout_lines
Security Considerations
When using the shell module, be cautious about potential security risks, especially with user input:
# DON'T do this in production - risk of command injection
- name: Dangerous command with user input
shell: "find /data -name {{ user_input }}"
# DO this instead - use the 'cmd' parameter with proper quoting
- name: Safe command with user input
shell:
cmd: find /data -name "{{ user_input }}"
vars:
user_input: "safe_value"
The Raw Module
The raw
module is the most direct way to execute commands on remote hosts. It doesn't require Python on the remote system, making it useful for initial bootstrapping or systems without Python.
Basic Usage
- name: Install Python on a fresh system
raw: apt-get update && apt-get install -y python-minimal
When to Use Raw
Use the raw module only when:
- The target system doesn't have Python installed
- You're bootstrapping Python on a system
- You need to interact with devices that don't support Python (like network hardware)
- name: Check if Python exists
raw: test -e /usr/bin/python || test -e /usr/bin/python3
register: python_check
changed_when: false
- name: Install Python if needed
raw: apt-get update && apt-get install -y python3
when: python_check.rc != 0
Practical Examples
Example 1: System Maintenance Script
---
- name: System maintenance tasks
hosts: webservers
become: true
tasks:
- name: Clear old log files
shell: find /var/log -name "*.log.*" -type f -mtime +30 -delete
register: log_cleanup
- name: Show cleanup results
debug:
msg: "Logs cleaned: {{ log_cleanup.stdout if log_cleanup.stdout else 'No logs needed cleaning' }}"
- name: Check disk space
command: df -h
register: disk_space
- name: Display disk space information
debug:
var: disk_space.stdout_lines
Example 2: Application Deployment Checks
---
- name: Application deployment verification
hosts: app_servers
tasks:
- name: Check if application is running
shell: pgrep -f myapp || echo "NOT RUNNING"
register: app_status
changed_when: false # This is just a check, not a change
- name: Verify application port is listening
shell: netstat -tulpn | grep 8080
register: port_status
ignore_errors: true
changed_when: false
- name: Display application status
debug:
msg: >
Application status: {{ "RUNNING" if "NOT RUNNING" not in app_status.stdout else "NOT RUNNING" }}
Port 8080 status: {{ "LISTENING" if port_status.rc == 0 else "NOT LISTENING" }}
Example 3: Database Backup
---
- name: Database backup
hosts: db_servers
become: true
vars:
backup_date: "{{ lookup('pipe', 'date +%Y%m%d') }}"
backup_dir: /var/backups/postgres
tasks:
- name: Ensure backup directory exists
file:
path: "{{ backup_dir }}"
state: directory
mode: '0750'
- name: Perform PostgreSQL backup
shell: |
pg_dump -U postgres main_db > {{ backup_dir }}/backup_{{ backup_date }}.sql
gzip {{ backup_dir }}/backup_{{ backup_date }}.sql
args:
creates: "{{ backup_dir }}/backup_{{ backup_date }}.sql.gz"
become_user: postgres
Command Module Decision Flowchart
Here's a flowchart to help you decide which command module to use:
Best Practices
When working with command modules, follow these best practices:
-
Use specialized modules when possible - Command modules should be your last resort when no specialized module exists for your task.
-
Handle errors properly - Use
failed_when
to define when a command has failed beyond exit codes:yaml- name: Run application check
command: /opt/app/check.sh
register: app_check
failed_when: "'ERROR' in app_check.stdout or app_check.rc != 0" -
Determine when a task has changed - Use
changed_when
to define when a task has actually made a change:yaml- name: Get server status
command: service nginx status
register: result
changed_when: false # This is just a status check, it doesn't change anything -
Avoid command injection - Always use proper quoting and parameter passing:
yaml- name: Safe command execution
shell:
cmd: find /data -name "{{ filename }}"
vars:
filename: "safe_value" -
Use check mode compatibility - Make your commands work with
--check
mode:yaml- name: Restart service if needed
command: service nginx restart
when: config_changed | bool
check_mode: false
Summary
Ansible command modules provide different ways to execute commands on remote hosts:
- Command module: Executes commands without a shell, most secure but limited functionality
- Shell module: Runs commands through a shell, allowing pipes, redirections, but needs careful security consideration
- Raw module: Direct command execution without Python, useful for bootstrapping
While specialized modules are preferred for most tasks, command modules are invaluable for custom scripts, one-off commands, and situations where no appropriate module exists.
Additional Resources
- Practice creating playbooks that use command modules for various system administration tasks
- Experiment with different options for error handling and output capture
- Challenge yourself to convert command module usage to specialized modules when possible
Exercises
- Create a playbook that gathers system information (uptime, disk usage, memory usage) using command modules and displays a summary.
- Write a task that safely executes a shell command with user-provided input.
- Develop a maintenance playbook that uses command modules with proper error handling and change detection.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)