Ansible command Module Tutorial + Examples


Percy Grunwald's Profile Picture

Written by Percy Grunwald

— Last Updated February 22, 2019

What does the Ansible command module do?

Ansible’s command module executes simple shell commands on remote hosts.

- name: precompile assets with asdf in $PATH and $RAILS_ENV set to production
  command: "rails assets:precompile"
  args:
    chdir: /opt/my_app
  environment:
    PATH: "/opt/asdf/bin:/opt/asdf/shims:{{ ansible_env.PATH }}"
    RAILS_ENV: production

Ansible command Module Video Tutorial

If you prefer watching to reading, here’s a full video tutorial from the TopTechSkills YouTube channel covering a many of the points and examples from this article. Feel free to comment on this article or the video if you have any questions.

▶️ Watch on YouTube

When to use the command module vs shell module

The command and shell modules are almost identical, except that command cannot use special shell operators such as < > | ; & or access user-specific environment variables like $HOME. According to the docs, the command module is more secure and predictable, so my personal preference is to default to using command unless I need the extended functionality of shell.

# If your command looks like this, use `command`:
/path/to/program arg1 arg2

# If your command looks like this, use `shell`:
java -version 2>&1 | grep OpenJDK

Examples

How to run command only if a file doesn’t exist

Use the creates argument to run the command only if a file doesn’t exist already.

- name: create a new test file only if it doesn't already exist
  command: touch /tmp/test_file
  args:
    creates: /tmp/test_file

How to run command only if a file does exist

Use the removes argument to run the command only if a file already exists.

- name: remove /tmp/test_file only if it already exists
  command: rm /tmp/test_file
  args:
    removes: /tmp/test_file

How to run command in a different directory

Use the chdir argument to run command inside the specified directory.

- name: precompile assets after changing to the app directory
  command: "rails assets:precompile"
  args:
    chdir: /opt/my_app

How to run command with custom environment variables

Use the environment keyword to set environment variables for the command. This is commonly used to alter the $PATH or set framework-specific ENVs such as RAILS_ENV.

- name: precompile assets with asdf in $PATH and $RAILS_ENV set to production
  command: "rails assets:precompile"
  args:
    chdir: /opt/my_app
  environment:
    PATH: "/opt/asdf/bin:/opt/asdf/shims:{{ ansible_env.PATH }}"
    RAILS_ENV: production

How to run command multiple times in a loop

Use the loop keyword to run command multiple times in a loop.

- name: run commands with asdf in $PATH and $RAILS_ENV set to production
  command: "{{ item }}"
  args:
    chdir: /opt/my_app
  environment:
    PATH: "/opt/asdf/bin:/opt/asdf/shims:{{ ansible_env.PATH }}"
    RAILS_ENV: production
  loop:
    - "rake db:migrate"
    - "rails assets:precompile"

How to capture command module output

Use the register keyword to capture the output of any commands you run.

- name: make a test file with command module
  command: touch /tmp/test_file
  register: command_output

- debug: var=command_output

The debug task above will output the following:

ok: [123.123.123.123] => {
    "touch_output": {
        "changed": true,
        "cmd": [
            "touch",
            "/tmp/test_file"
        ],
        "delta": "0:00:00.003577",
        "end": "2018-12-26 05:45:17.411042",
        "failed": false,
        "rc": 0,
        "start": "2018-12-26 05:45:17.407465",
        "stderr": "",
        "stderr_lines": [],
        "stdout": "",
        "stdout_lines": [],
        "warnings": [...]
    }
}

How to capture command module output from a loop

When using the command module in a loop, the output of each item in the loop will go into the results key of the registered variable.

- name: make multiple test files with command module
  command: "touch /tmp/test_file_{{ item }}"
  register: command_output
  loop:
    - 1
    - 2

- debug: var=command_output
ok: [123.123.123.123] => {
    "command_output": {
        "changed": true,
        "msg": "All items completed",
        "results": [
            {
                ...
                "changed": true,
                "cmd": [
                    "touch",
                    "/tmp/test_file_1"
                ],
                "delta": "0:00:00.003201",
                "end": "2018-12-26 05:51:46.044893",
                "failed": false,
                "invocation": {...},
                "item": "1",
                "rc": 0,
                "start": "2018-12-26 05:51:46.041692",
                "stderr": "",
                "stderr_lines": [],
                "stdout": "",
                "stdout_lines": []
            },
            {
                ...
                "changed": true,
                "cmd": [
                    "touch",
                    "/tmp/test_file_2"
                ],
                "delta": "0:00:00.003118",
                "end": "2018-12-26 05:51:46.360661",
                "failed": false,
                "invocation": {...},
                "item": "2",
                "rc": 0,
                "start": "2018-12-26 05:51:46.357543",
                "stderr": "",
                "stderr_lines": [],
                "stdout": "",
                "stdout_lines": []
            }
        ],
        "warnings": [...]
    }
}

Further reading

Ansible command Module on Ansible Docs

Comments