How to display script output in Ansible playbook

AnsibleAnsibleBeginner
Practice Now

Introduction

Ansible is a powerful automation tool that helps system administrators and developers manage infrastructure efficiently. When running scripts or commands through Ansible playbooks, it is often crucial to capture and display their output for monitoring and troubleshooting purposes.

In this hands-on lab, you will learn how to effectively display script output in your Ansible playbooks. We will start with the basics of Ansible playbook structure, then explore various techniques for capturing and displaying command outputs, and finally cover some advanced methods for formatting and filtering output data.

By the end of this lab, you will have practical experience with different approaches to handling script output in Ansible, enabling you to create more informative and easier-to-debug automation workflows.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL ansible(("Ansible")) -.-> ansible/ModuleOperationsGroup(["Module Operations"]) ansible(("Ansible")) -.-> ansible/PlaybookEssentialsGroup(["Playbook Essentials"]) ansible/ModuleOperationsGroup -.-> ansible/apt("Package Manager") ansible/ModuleOperationsGroup -.-> ansible/command("Execute Commands") ansible/ModuleOperationsGroup -.-> ansible/debug("Test Output") ansible/ModuleOperationsGroup -.-> ansible/shell("Execute Shell Commands") ansible/PlaybookEssentialsGroup -.-> ansible/playbook("Execute Playbook") subgraph Lab Skills ansible/apt -.-> lab-415724{{"How to display script output in Ansible playbook"}} ansible/command -.-> lab-415724{{"How to display script output in Ansible playbook"}} ansible/debug -.-> lab-415724{{"How to display script output in Ansible playbook"}} ansible/shell -.-> lab-415724{{"How to display script output in Ansible playbook"}} ansible/playbook -.-> lab-415724{{"How to display script output in Ansible playbook"}} end

Setting Up Your First Ansible Playbook

Before we can explore how to display script output, we need to set up a basic Ansible environment and create our first playbook.

Installing Ansible

Let's start by making sure Ansible is installed on our system:

sudo apt update
sudo apt install -y ansible

This command will install Ansible on your Ubuntu system. Once the installation is complete, verify it by checking the Ansible version:

ansible --version

You should see output similar to the following, which confirms that Ansible is correctly installed:

ansible [core 2.12.x]
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/home/labex/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3/dist-packages/ansible
  ansible collection location = /home/labex/.ansible/collections:/usr/share/ansible/collections
  executable location = /usr/bin/ansible
  python version = 3.10.x (default, Mar 14 2023, 14:50:41) [GCC 11.3.0]
  jinja version = 3.0.3
  libyaml = True

Understanding Ansible Inventory

Ansible needs to know which hosts to connect to. For this lab, we'll use a simple inventory file that includes only the local machine.

Create a directory for our Ansible project:

mkdir -p ~/project/ansible-lab
cd ~/project/ansible-lab

Now, create an inventory file named inventory.ini:

echo "localhost ansible_connection=local" > inventory.ini

This inventory file tells Ansible to run commands on the local machine without using SSH.

Creating Your First Playbook

Now, let's create a basic Ansible playbook. In the WebIDE, create a new file called first_playbook.yml in the ~/project/ansible-lab directory with the following content:

---
- name: My First Ansible Playbook
  hosts: localhost
  gather_facts: yes

  tasks:
    - name: Display a simple message
      debug:
        msg: "Hello from Ansible!"

    - name: Display system information
      debug:
        msg: "You are running {{ ansible_distribution }} {{ ansible_distribution_version }}"

This simple playbook has two tasks:

  1. Display a simple greeting message
  2. Display information about the operating system you're running

Let's run this playbook to see how it works:

cd ~/project/ansible-lab
ansible-playbook -i inventory.ini first_playbook.yml

You should see output similar to this:

PLAY [My First Ansible Playbook] *********************************************************

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

TASK [Display a simple message] **********************************************************
ok: [localhost] => {
    "msg": "Hello from Ansible!"
}

TASK [Display system information] ********************************************************
ok: [localhost] => {
    "msg": "You are running Ubuntu 22.04"
}

PLAY RECAP *******************************************************************************
localhost                  : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

This output shows:

  • The playbook executed successfully
  • All tasks completed with "ok" status
  • The output from each debug task is displayed

Now that we have a basic playbook working, we can explore how to run scripts and display their output in more detail.

Running Scripts and Capturing Output

Now that we have a basic Ansible environment set up, let's explore how to run scripts and capture their output using the command and shell modules.

The Difference Between Command and Shell Modules

Ansible provides two main modules for executing commands:

  • command: Executes a command on a remote node without going through a shell, which means shell operators like |, >, <, and & do not work.
  • shell: Executes a command through a shell, allowing for shell operators and environment variables.

Creating a Simple Script to Run

First, let's create a simple shell script that we can run with Ansible. Create a file named system_info.sh in your ~/project/ansible-lab directory:

cd ~/project/ansible-lab

In the WebIDE, create the file with the following content:

#!/bin/bash

echo "=== System Information ==="
echo "Hostname: $(hostname)"
echo "Kernel Version: $(uname -r)"
echo "CPU Info: $(grep 'model name' /proc/cpuinfo | head -1 | cut -d':' -f2 | xargs)"
echo "Memory Info: $(free -h | grep Mem | awk '{print $2 " total, " $3 " used, " $4 " free"}')"
echo "Disk Usage: $(df -h / | grep / | awk '{print $5 " of " $2 " used"}')"

Make the script executable:

chmod +x ~/project/ansible-lab/system_info.sh

Now let's run this script manually to see what output it produces:

./system_info.sh

You should see output similar to:

=== System Information ===
Hostname: labex-xxxxxxxx
Kernel Version: 5.15.0-xx-generic
CPU Info: Intel(R) Xeon(R) xxxxxx
Memory Info: 8.0G total, 1.2G used, 5.8G free
Disk Usage: 15% of 50G used

Running the Script with Ansible

Now, let's create a new playbook that will run this script and capture its output. Create a file named script_output.yml in your ~/project/ansible-lab directory:

---
- name: Run Script and Capture Output
  hosts: localhost
  gather_facts: no

  tasks:
    - name: Run system info script
      command: ./system_info.sh
      register: script_result

    - name: Display script output
      debug:
        msg: "{{ script_result.stdout }}"

This playbook does two things:

  1. Runs our system_info.sh script using the command module
  2. Stores the output in a variable called script_result using the register keyword
  3. Displays the captured output using the debug module

Let's run this playbook:

cd ~/project/ansible-lab
ansible-playbook -i inventory.ini script_output.yml

You should see output similar to:

PLAY [Run Script and Capture Output] ***************************************************

TASK [Run system info script] ***********************************************************
changed: [localhost]

TASK [Display script output] ************************************************************
ok: [localhost] => {
    "msg": "=== System Information ===\nHostname: labex-xxxxxxxx\nKernel Version: 5.15.0-xx-generic\nCPU Info: Intel(R) Xeon(R) xxxxxx\nMemory Info: 8.0G total, 1.2G used, 5.8G free\nDisk Usage: 15% of 50G used"
}

PLAY RECAP *****************************************************************************
localhost                  : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Notice how the output appears as a single string with newline characters (\n). Let's improve the display in the next section.

Understanding the Register Variable

The register keyword creates a variable that contains several attributes, not just the output of the command. Let's create a new playbook to explore these attributes.

Create a file named register_details.yml in your ~/project/ansible-lab directory:

---
- name: Explore Register Variable
  hosts: localhost
  gather_facts: no

  tasks:
    - name: Run system info script
      command: ./system_info.sh
      register: script_result

    - name: Display all register properties
      debug:
        var: script_result

    - name: Display just stdout
      debug:
        var: script_result.stdout

    - name: Display stdout as a list of lines
      debug:
        var: script_result.stdout_lines

Run this playbook:

ansible-playbook -i inventory.ini register_details.yml

You'll see a more detailed output that shows all properties of the script_result variable, including:

  • stdout: The standard output as a single string
  • stderr: The standard error (if any)
  • rc: The return code (0 means success)
  • stdout_lines: The standard output split into a list of lines

The last task is particularly useful as it formats the output as a list of lines, making it much more readable.

Advanced Output Handling Techniques

Now that we understand the basics of capturing script output, let's explore some more advanced techniques for handling and displaying output in Ansible playbooks.

Handling Script Errors

When running scripts, it's important to properly handle error conditions. Ansible marks a task as failed if the command returns a non-zero exit code. Let's create a playbook that demonstrates error handling.

Create a file named error_handling.yml in your ~/project/ansible-lab directory:

---
- name: Handling Script Errors
  hosts: localhost
  gather_facts: no

  tasks:
    - name: Run a command that might fail
      command: ls /nonexistent_directory
      register: cmd_result
      ignore_errors: yes

    - name: Show error message if command failed
      debug:
        msg: "Command failed with error: {{ cmd_result.stderr }}"
      when: cmd_result.rc != 0

    - name: Show success message if command succeeded
      debug:
        msg: "Command succeeded with output: {{ cmd_result.stdout }}"
      when: cmd_result.rc == 0

In this playbook:

  1. We attempt to list a directory that doesn't exist
  2. We use ignore_errors: yes to prevent the playbook from stopping if the command fails
  3. We check the return code (cmd_result.rc) to determine if the command succeeded or failed
  4. We display appropriate messages based on the result

Let's run this playbook:

cd ~/project/ansible-lab
ansible-playbook -i inventory.ini error_handling.yml

You should see output similar to:

PLAY [Handling Script Errors] ***********************************************************

TASK [Run a command that might fail] ***************************************************
fatal: [localhost]: FAILED! => {"changed": true, "cmd": ["ls", "/nonexistent_directory"], "delta": "0:00:00.003398", "end": "2023-09-15 12:34:56.789012", "msg": "non-zero return code", "rc": 2, "start": "2023-09-15 12:34:56.785614", "stderr": "ls: cannot access '/nonexistent_directory': No such file or directory", "stderr_lines": ["ls: cannot access '/nonexistent_directory': No such file or directory"], "stdout": "", "stdout_lines": []}
...ignoring

TASK [Show error message if command failed] *********************************************
ok: [localhost] => {
    "msg": "Command failed with error: ls: cannot access '/nonexistent_directory': No such file or directory"
}

TASK [Show success message if command succeeded] ****************************************
skipping: [localhost]

PLAY RECAP *******************************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=1

Notice that despite the command failing, the playbook continues to run and displays the error message.

Formatting Multi-line Output

Let's create a more complex script that generates multi-line output, then format it nicely in our playbook.

Create a file named package_info.sh in your ~/project/ansible-lab directory:

#!/bin/bash

echo "TOP 5 LARGEST INSTALLED PACKAGES"
echo "================================"
dpkg-query -W -f='${Installed-Size}\t${Package}\n' | sort -n -r | head -5

Make it executable:

chmod +x ~/project/ansible-lab/package_info.sh

Now create a playbook to run this script and format the output nicely. Create a file named formatted_output.yml:

---
- name: Format Script Output
  hosts: localhost
  gather_facts: no

  tasks:
    - name: Run package info script
      shell: ./package_info.sh
      register: pkg_info

    - name: Display raw output
      debug:
        msg: "{{ pkg_info.stdout }}"

    - name: Display formatted output with a title
      debug:
        msg: |
          Below is the package information:
          --------------------------------
          {% for line in pkg_info.stdout_lines %}
          {{ line }}
          {% endfor %}
          --------------------------------
          Total lines: {{ pkg_info.stdout_lines | length }}

This playbook:

  1. Runs our package_info.sh script
  2. Displays the raw output
  3. Uses a Jinja2 template to format the output with a title and footer

Let's run it:

ansible-playbook -i inventory.ini formatted_output.yml

You should see nicely formatted output:

PLAY [Format Script Output] ***********************************************************

TASK [Run package info script] ********************************************************
changed: [localhost]

TASK [Display raw output] *************************************************************
ok: [localhost] => {
    "msg": "TOP 5 LARGEST INSTALLED PACKAGES\n================================\n112233\tsome-large-package\n99887\tanother-package\n...\n"
}

TASK [Display formatted output with a title] ******************************************
ok: [localhost] => {
    "msg": "Below is the package information:\n--------------------------------\nTOP 5 LARGEST INSTALLED PACKAGES\n================================\n112233\tsome-large-package\n99887\tanother-package\n...\n--------------------------------\nTotal lines: 7"
}

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

Conditionally Displaying Output

Sometimes you only want to display output under certain conditions, such as when running in verbose mode. Let's see how to do this.

Create a file named conditional_output.yml in your ~/project/ansible-lab directory:

---
- name: Conditional Output Display
  hosts: localhost
  gather_facts: yes

  tasks:
    - name: Get disk usage information
      shell: df -h
      register: disk_info

    - name: Display disk usage (always shown)
      debug:
        msg: "{{ disk_info.stdout_lines[0:2] }}"

    - name: Display detailed disk usage (only in verbose mode)
      debug:
        msg: "{{ disk_info.stdout_lines }}"
        verbosity: 1

In this playbook:

  1. We capture disk usage information
  2. We always display a summary (first two lines)
  3. We display the full details only when running in verbose mode (with -v flag)

Let's run it in normal mode first:

ansible-playbook -i inventory.ini conditional_output.yml

Now, let's run it in verbose mode:

ansible-playbook -i inventory.ini conditional_output.yml -v

You'll notice that in verbose mode, you get the complete disk usage information, while in normal mode, you only see the summary.

This technique is useful for hiding potentially sensitive or overwhelming output by default, while still making it available when needed for debugging.

Summary

In this lab, you have learned various techniques for capturing and displaying script output in Ansible playbooks:

  1. You set up a basic Ansible environment and created your first playbook
  2. You learned how to run scripts using the command and shell modules
  3. You captured script output using the register directive
  4. You displayed output using the debug module in different formats
  5. You handled error conditions and implemented conditional output display
  6. You applied formatting techniques to make output more readable

These skills enable you to create more informative Ansible playbooks that provide clear feedback during execution. This visibility is crucial for both monitoring automated processes and troubleshooting when issues arise.

By leveraging these output handling techniques, you can build more robust automation workflows that not only perform tasks but also communicate clearly what is happening at each step. This makes your automation more transparent and easier to maintain, especially when working in team environments or with complex infrastructure.