From ansible-skills
Debug, troubleshoot, and validate Ansible playbooks and roles using ansible-lint, Molecule, and debugging best practices. Use this skill when the user asks to: "debug ansible", "troubleshoot playbook", "ansible not working", "fix ansible error", "ansible failing", "why is ansible", "ansible error", "playbook error", "role failing", "test ansible", "ansible lint", "molecule test", "validate playbook", or encounters Ansible errors. Always invoke this skill for Ansible debugging and troubleshooting tasks.
npx claudepluginhub stoleas/ansible-skillsThis skill is limited to using the following tools:
Debug, troubleshoot, and validate Ansible playbooks and roles using Red Hat CoP best practices, ansible-lint, and Molecule.
Creates isolated Git worktrees for feature branches with prioritized directory selection, gitignore safety checks, auto project setup for Node/Python/Rust/Go, and baseline verification.
Executes implementation plans in current session by dispatching fresh subagents per independent task, with two-stage reviews: spec compliance then code quality.
Dispatches parallel agents to independently tackle 2+ tasks like separate test failures or subsystems without shared state or dependencies.
Debug, troubleshoot, and validate Ansible playbooks and roles using Red Hat CoP best practices, ansible-lint, and Molecule.
Use -v flags to get more information about execution:
-v (Basic Verbosity)Shows task results and return values.
ansible-playbook -v playbook.yml
When to use:
Output includes:
-vv (More Verbosity)Shows task input parameters.
ansible-playbook -vv playbook.yml
When to use:
Output includes:
-v output-vvv (Connection Debug)Shows connection debugging information.
ansible-playbook -vvv playbook.yml
When to use:
Output includes:
-vv output-vvvv (Maximum Verbosity)Shows SSH protocol details and internal Ansible workings.
ansible-playbook -vvvv playbook.yml
When to use:
Output includes:
-vvv outputWarning: Extremely verbose, use only when needed.
Symptom:
fatal: [host]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh", "unreachable": true}
Causes and Solutions:
SSH connectivity:
# Test SSH manually
ssh user@host
# Check SSH key
ssh-agent bash
ssh-add ~/.ssh/id_rsa
Incorrect inventory:
# Check inventory file
[webservers]
server1 ansible_host=192.168.1.10 ansible_user=admin
Firewall blocking:
# Check if port 22 is open
telnet host 22
nc -zv host 22
Host key verification:
# In ansible.cfg
[defaults]
host_key_checking = False
Symptom:
fatal: [host]: FAILED! => {"msg": "Incorrect sudo password"}
Solutions:
Become password:
ansible-playbook -K playbook.yml # Prompt for sudo password
Configure passwordless sudo:
# On target host
echo "ansible_user ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers.d/ansible
Specify become method:
- name: Task requiring privileges
become: true
become_method: sudo
become_user: root
Symptom:
fatal: [host]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'apache_port' is undefined"}
Solutions:
Check variable definition:
# See all variables for a host
ansible-inventory -i inventory --host server1 --yaml
Use default filter:
- name: Use variable with default
ansible.builtin.debug:
msg: "Port: {{ apache_port | default(80) }}"
Check variable precedence:
Debug variable value:
- name: Show variable
ansible.builtin.debug:
var: apache_port
Symptom:
fatal: [host]: FAILED! => {"changed": false, "msg": "Failed to find required executable apt-get"}
Solutions:
Check if module/executable exists:
- name: Check if command exists
ansible.builtin.command: which apt-get
register: cmd_check
changed_when: false
failed_when: false
Use platform-agnostic modules:
# Instead of apt/yum
- name: Install package
ansible.builtin.package:
name: httpd
state: present
Conditional execution:
- name: Install on Debian
ansible.builtin.apt:
name: apache2
when: ansible_os_family == "Debian"
Symptom: Running the playbook twice shows "changed" on second run.
Solutions:
Use declarative modules:
# Bad - always reports changed
- ansible.builtin.command: yum install -y httpd
# Good - idempotent
- ansible.builtin.package:
name: httpd
state: present
Add changed_when for commands:
- name: Validate configuration
ansible.builtin.command: httpd -t
changed_when: false
Use creates/removes:
- name: Initialize database
ansible.builtin.command: /usr/local/bin/init-db.sh
args:
creates: /var/lib/db/.initialized
Symptom:
AnsibleUndefinedVariable: 'variable_name' is undefined
Solutions:
Check template syntax:
# Use default filter
Port {{ apache_port | default(80) }}
# Check if variable is defined
{% if apache_ssl_enabled is defined and apache_ssl_enabled %}
SSLEngine on
{% endif %}
Debug template variables:
- name: Show all variables available
ansible.builtin.template:
src: config.j2
dest: /tmp/debug_config
check_mode: yes
diff: yes
- name: Debug variable value
ansible.builtin.debug:
var: my_variable
- name: Debug with message
ansible.builtin.debug:
msg: "The value is {{ my_variable }}"
- name: Debug multiple variables
ansible.builtin.debug:
msg: |
Variable 1: {{ var1 }}
Variable 2: {{ var2 }}
Variable 3: {{ var3 }}
- name: Conditional debugging
ansible.builtin.debug:
msg: "This only shows in verbose mode"
when: ansible_verbosity >= 1
- name: Run command
ansible.builtin.command: /usr/bin/some-command
register: command_result
- name: Show command output
ansible.builtin.debug:
var: command_result
- name: Show just stdout
ansible.builtin.debug:
var: command_result.stdout_lines
# Run in check mode - no changes made
ansible-playbook --check playbook.yml
# See diff of what would change
ansible-playbook --check --diff playbook.yml
# Skip tasks in check mode
- name: This task will be skipped in check mode
ansible.builtin.command: /dangerous/command
check_mode: false
# Always run in check mode
- name: This task always runs in check mode
ansible.builtin.stat:
path: /some/file
check_mode: true
# Test on single host first
ansible-playbook --limit server1 playbook.yml
# Test on subset
ansible-playbook --limit 'webservers:&production' playbook.yml
# Start from specific task
ansible-playbook --start-at-task="Install Apache" playbook.yml
# Use tags to run specific tasks
ansible-playbook --tags "install,configure" playbook.yml
# Skip specific tags
ansible-playbook --skip-tags "deploy" playbook.yml
# Confirm each task before running
ansible-playbook --step playbook.yml
ansible-lint validates playbooks against best practices and Red Hat CoP standards.
# Lint a playbook
ansible-lint playbook.yml
# Lint a role
ansible-lint roles/my_role/
# Lint with specific profile
ansible-lint --profile moderate playbook.yml
# Lint with rules list
ansible-lint --list-rules
# Show all violations (including warnings)
ansible-lint -p playbook.yml
Red Hat CoP recommends the moderate profile:
# .ansible-lint
---
profile: moderate
Profiles hierarchy:
min - Minimal rules (basic syntax)basic - Basic best practicesmoderate - Red Hat CoP recommended (default)safety - Safety-critical rulesshared - For shared/reusable contentproduction - Production-ready standardsBad:
- ansible.builtin.package:
name: httpd
Good:
- name: Install Apache
ansible.builtin.package:
name: httpd
Bad:
- name: Very long task name that goes on and on and on and exceeds the recommended line length of 160 characters which makes it hard to read
Good:
- name: Configure application with recommended settings
Bad:
- name: Install package
package:
name: httpd
Good:
- name: Install package
ansible.builtin.package:
name: httpd
Bad:
- name: Check config
ansible.builtin.command: httpd -t
Good:
- name: Check config
ansible.builtin.command: httpd -t
changed_when: false
Auto-fix where possible:
# Not yet available in ansible-lint, but planned
# ansible-lint --fix playbook.yml
Skip specific rules (use sparingly):
- name: Special case task
ansible.builtin.command: /special/command
tags:
- skip_ansible_lint
Configure in .ansible-lint:
skip_list:
- yaml[line-length] # If you have legitimate long lines
Molecule provides comprehensive role testing.
# Full test (destroy, create, converge, verify)
molecule test
# Individual steps
molecule create # Create test instances
molecule converge # Apply the role
molecule verify # Run verification tests
molecule idempotence # Check idempotence
molecule destroy # Clean up
# Development workflow
molecule converge # Apply changes
molecule verify # Test the result
# Make fixes
molecule converge # Reapply
molecule verify # Verify again
molecule destroy # Clean up when done
Molecule's idempotence test runs the role twice and fails if changes are reported on the second run:
molecule idempotence
Expected output:
Idempotence completed successfully.
If it fails:
CRITICAL Idempotence test failed because of the following tasks:
* [instance] => Task: Install package
This means the task is not idempotent - fix it before proceeding.
Write verification tests in molecule/default/verify.yml:
---
- name: Verify
hosts: all
gather_facts: true
tasks:
- name: Verify package is installed
ansible.builtin.package_facts:
- name: Assert package is present
ansible.builtin.assert:
that:
- "'httpd' in ansible_facts.packages"
fail_msg: "Apache package not installed"
- name: Verify service is running
ansible.builtin.service_facts:
- name: Assert service is active
ansible.builtin.assert:
that:
- ansible_facts.services['httpd.service'].state == 'running'
fail_msg: "Apache service not running"
- name: Verify config file exists
ansible.builtin.stat:
path: /etc/httpd/conf/httpd.conf
register: config_file
- name: Assert config is present
ansible.builtin.assert:
that:
- config_file.stat.exists
fail_msg: "Apache config not found"
Complete validation workflow for playbooks and roles:
ansible-playbook --syntax-check playbook.yml
Catches basic YAML and Ansible syntax errors.
ansible-lint --profile moderate playbook.yml
Validates against best practices and Red Hat CoP standards.
ansible-playbook --check --diff playbook.yml
Shows what would change without making changes.
# Test on one host first
ansible-playbook --limit test_server playbook.yml
Validate on a subset before full deployment.
ansible-playbook -v playbook.yml
Run with appropriate verbosity level.
# Run twice, second run should show no changes
ansible-playbook playbook.yml
ansible-playbook playbook.yml | grep "changed=0"
Or use Molecule for roles:
molecule idempotence
# Check inventory
ansible-inventory -i inventory --list
ansible-inventory -i inventory --host server1
# Test connectivity
ansible all -i inventory -m ping
# Gather facts from a host
ansible server1 -i inventory -m setup
# Check which Python Ansible is using
ansible --version
# See all variables for a host
ansible-playbook playbook.yml --limit server1 -e "ansible_verbosity=4" --tags never
# Validate variable files
python -c "import yaml; yaml.safe_load(open('vars/main.yml'))"
# Check role dependencies
ansible-galaxy role info namespace.role_name
# List all tasks in a playbook
ansible-playbook --list-tasks playbook.yml
# List all tags
ansible-playbook --list-tags playbook.yml
ansible-inventory --list to see available hostshosts: line in playbook-K flag to prompt for sudo passwordbecome settingsssh-add -lansible_user in inventoryssh user@hostansible-doc module_name-v to see task results--syntax-check to validate YAMLansible-lint to check best practices--check mode for dry run--limitdebug tasks to show variable valuesregister to capture task outputansible.log if logging enabledansible-inventory --listansible all -m ping-vvv for connection issuesWhen troubleshooting Ansible issues, systematically identify the problem, gather information with appropriate verbosity, isolate the failing component, apply targeted fixes, and validate the solution using ansible-lint and idempotence testing.