Debugging Ansible
Relevant source files
The following files were used as context for generating this wiki page:
- lib/ansible/modules/debug.py
- hacking/README.md
- AGENTS.md
- test/integration/targets/var_precedence/ansible-var-precedence-check.py
- lib/ansible/modules/uri.py
- lib/ansible/modules/blockinfile.py
- lib/ansible/modules/expect.py
- test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py
Ansible provides a range of tools and techniques for debugging playbooks, modules, and the execution environment. These capabilities range from simple print statements within a playbook to sophisticated local module testing and comprehensive static analysis via the ansible-test framework. This document outlines the primary methods for debugging available within the Ansible ecosystem, based on the provided source files.
The debugging mechanisms covered include using the debug module for runtime variable inspection, leveraging the ansible-test tool for CI/CD and local testing, running modules in isolation with test-module.py, and employing specific module parameters like validate for pre-flight checks.
Core Debugging Tools
The debug Module
The ansible.builtin.debug module is the most fundamental tool for debugging playbooks. It prints statements during execution, which is useful for inspecting variable values or expressions without halting the playbook. It is often used with conditional when statements to control its output.
Sources: lib/ansible/modules/debug.py:11-20
Options
| Parameter | Type | Default | Description |
|---|---|---|---|
msg | str | 'Hello world!' | The customized message to be printed. |
var | str | The name of a variable to debug. This is mutually exclusive with msg. The value is implicitly wrapped in {{ }}, so Jinja2 delimiters are not needed unless double interpolation is intended. | |
verbosity | int | 0 | A number that controls when the debug task runs. For example, a value of 3 means the task will only run when the playbook is executed with -vvv or higher. |
Sources: lib/ansible/modules/debug.py:21-41
Usage Examples
Printing a custom message with a variable:
- name: Print the gateway for each host when defined
ansible.builtin.debug:
msg: System {{ inventory_hostname }} has gateway {{ ansible_default_ipv4.gateway }}
when: ansible_default_ipv4.gateway is defined
Sources: lib/ansible/modules/debug.py:73-76
Printing the contents of a registered variable:
- name: Get uptime information
ansible.builtin.shell: /usr/bin/uptime
register: result
- name: Print return information from the previous task
ansible.builtin.debug:
var: result
verbosity: 2
Sources: lib/ansible/modules/debug.py:78-85
Local Module Testing with test-module.py
The hacking/test-module.py script provides a simple way for developers to run an Ansible module locally, outside of a full playbook execution. This is particularly useful for iterative development and debugging, as it allows for direct execution and the use of breakpoints.
Sources: hacking/README.md:21-25
The script takes the module path and its arguments, executing it on the local machine.
Example Usage:
./hacking/test-module.py -m lib/ansible/modules/command.py -a "echo hi"
Sources: hacking/README.md:28-30
For complex arguments, a JSON string can be passed:
./hacking/test-module.py -m module \
-a '{"parent": {"child": [{"item": "first", "val": "foo"}, {"item": "second", "val": "bar"}]}}'
Sources: hacking/README.md:36-39
This diagram shows the workflow for using test-module.py.
Sources: hacking/README.md:21-39
Playbook-Level Debugging Patterns
Registering and Inspecting Task Output
A common debugging pattern is to register the output of a task into a variable and then use the debug module to inspect its contents. This helps in understanding the data structure returned by a module and crafting subsequent tasks.
Sources: lib/ansible/modules/debug.py:78-85
This sequence diagram illustrates the register-and-debug flow.
Sources: lib/ansible/modules/debug.py:78-85
Retrying Tasks with until
For tasks that may fail intermittently, such as waiting for a web service to become available, Ansible provides a loop mechanism with until, retries, and delay. This pattern is a form of active debugging and stabilization.
The following example from the ansible.builtin.uri module demonstrates pausing a play until a URL is reachable.
- name: Pause play until a URL is reachable from this host
ansible.builtin.uri:
url: "http://192.0.2.1/some/test"
follow_redirects: none
method: GET
register: _result
until: _result.status == 200
retries: 720 # 720 * 5 seconds = 1hour (60*60/5)
delay: 5 # Every 5 seconds
Sources: lib/ansible/modules/uri.py:321-329
This flowchart shows the logic of the until loop.
Sources: lib/ansible/modules/uri.py:321-329
Interactive Command Debugging with expect
The ansible.builtin.expect module is designed for simple scenarios where a command requires interactive responses to prompts. It executes a command and responds based on regular expression matches of the output.
This is useful for automating and debugging scripts that would otherwise require manual input.
- name: Case insensitive password string match
ansible.builtin.expect:
command: passwd username
responses:
(?i)password: "MySekretPa$$word"
no_log: true
Sources: lib/ansible/modules/expect.py:96-102
The responses dictionary maps a regular expression to the string (or list of strings) that should be sent when the expression is matched.
Sources: lib/ansible/modules/expect.py:40-45
Advanced Debugging and Testing
Module-Level Validation
Some Ansible modules, such as blockinfile and replace, support a validate parameter. This parameter takes a command string that is executed against a temporary file containing the proposed changes. If the validation command returns a non-zero exit code, the module fails before applying the changes, preventing a broken configuration. The %s placeholder is used to represent the temporary file path.
Sources: lib/ansible/modules/blockinfile.py:202-214, lib/ansible/modules/replace.py:205-217
Example with blockinfile:
- name: Insert/Update configuration using a local file and validate it
ansible.builtin.blockinfile:
block: "{{ lookup('ansible.builtin.file', './local/sshd_config') }}"
path: /etc/ssh/sshd_config
backup: yes
validate: /usr/sbin/sshd -T -f %s
Sources: lib/ansible/modules/blockinfile.py:144-149
Variable Precedence Checker
ansible-var-precedence-check.py is a utility to test and verify the order of variable precedence in Ansible. It dynamically generates and runs playbooks to assert that a variable findme has the expected value based on a list of enabled features, where each feature represents a different method of setting a variable.
The script iterates through a list of features, each corresponding to a variable source (e.g., extra_vars, role_var, play_var), and runs a test to ensure the highest-priority feature in the list correctly sets the final variable value.
Sources: test/integration/targets/var_precedence/ansible-var-precedence-check.py:1-4, test/integration/targets/var_precedence/ansible-var-precedence-check.py:334-362
ansible-test Framework
The ansible-test command is a powerful, unified tool for running various tests, including static analysis (sanity), unit tests, and integration tests. It is the primary tool for debugging CI failures and ensuring code quality.
Sources: AGENTS.md:32-35, AGENTS.md:259-260
Test Types
| Command | Description |
|---|---|
ansible-test sanity | Runs static analysis and linting checks (e.g., pylint, pep8). |
ansible-test units | Runs unit tests, typically using pytest. |
ansible-test integration | Runs integration tests, which execute playbooks against targets. |
Sources: AGENTS.md:38-51
Diagnosing CI Failures
When a CI job fails, ansible-test is the tool to reproduce and debug the issue locally. The AGENTS.md document outlines a clear workflow for this process.
This flowchart shows the recommended workflow for analyzing CI failures.
Sources: AGENTS.md:68-91
validate-modules Sanity Test
A key part of the ansible-test sanity suite is validate-modules. This test performs deep validation on module files, checking for:
- Correct licensing (GPLv3).
- Presence and correctness of
DOCUMENTATION,EXAMPLES, andRETURNsections. - Schema validation of the documentation blocks.
- Consistency between
argument_specin the code and theoptionsin the documentation. - Correct usage of
version_added. - Deprecation consistency.
- Banned imports like
sys.exit.
Sources: test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py:461-468, test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py:590-601, test/lib/ansible_test/_util/controller/sanity/validate-modules/validate_modules/main.py:840-848
Summary
Debugging in Ansible is a multi-faceted process that spans from simple in-playbook print statements to a comprehensive testing and validation framework. The debug module is the first line of defense for inspecting runtime data. For module developers, hacking/test-module.py offers a quick way to run code in isolation. For ensuring correctness and preventing regressions, the ansible-test framework, particularly its sanity checks like validate-modules, provides robust static analysis and a structured way to diagnose CI failures. Finally, patterns like register, until, and the validate parameter in modules offer powerful techniques for building resilient and debuggable automation.
Sources: lib/ansible/modules/debug.py, hacking/README.md, AGENTS.md, lib/ansible/modules/blockinfile.py