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:
- We attempt to list a directory that doesn't exist
- We use
ignore_errors: yes
to prevent the playbook from stopping if the command fails
- We check the return code (
cmd_result.rc
) to determine if the command succeeded or failed
- 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.
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:
- Runs our
package_info.sh
script
- Displays the raw output
- 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:
- We capture disk usage information
- We always display a summary (first two lines)
- 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.