Comprehensive toolkit for validating, linting, testing, and automating Ansible playbooks, roles, and collections. Use this skill when working with Ansible files (.yml, .yaml playbooks, roles, inventories), validating automation code, debugging playbook execution, performing dry-run testing with check mode, or working with custom modules and collections.
Overall
score
100%
Does it follow best practices?
Validation for skill structure
This document provides solutions to common Ansible errors, including syntax errors, module errors, connection issues, and runtime problems.
ERROR! Syntax Error while loading YAML.
mapping values are not allowed hereCause: YAML indentation error or missing quote
Example Problem:
- name: Configure app
template:
src: config.j2
dest: /etc/app/config.yml
vars:
db_host: localhost:5432 # WRONG: colon not quotedSolution:
- name: Configure app
template:
src: config.j2
dest: /etc/app/config.yml
vars:
db_host: "localhost:5432" # QuotedERROR! Syntax Error while loading YAML.
found undefined alias 'anchor'Cause: Using YAML anchor/alias incorrectly
Solution: Ensure anchors are defined before use
# Define anchor
common_packages: &common_packages
- git
- curl
- vim
# Use alias
- name: Install common packages
apt:
name: *common_packagesERROR! could not find expected ':'Cause: Missing colon or improper YAML structure
Example Problem:
- name Install package # Missing colon after name
apt:
name nginx # Missing colon after nameSolution:
- name: Install package
apt:
name: nginxERROR! Unsupported parameters for (module) module: parameter_nameCause: Using wrong parameter name or typo
Example Problem:
- name: Create file
file:
path: /tmp/test
state: present
mod: '0644' # WRONG: should be 'mode'Solution:
- name: Create file
file:
path: /tmp/test
state: present
mode: '0644' # Correct parameter nameHow to check: Use ansible-doc module_name to see correct parameters
fatal: [host]: FAILED! => {"changed": false, "module_stderr": "..."}Common Causes:
Solutions:
# Specify Python interpreter in inventory
[webservers]
server1 ansible_python_interpreter=/usr/bin/python3
# Or in playbook
- hosts: all
vars:
ansible_python_interpreter: /usr/bin/python3fatal: [host]: FAILED! => {"changed": false, "msg": "missing required arguments: name"}Cause: Required module parameter not provided
Solution: Add the required parameter
# Wrong
- name: Install package
apt:
state: present
# Correct
- name: Install package
apt:
name: nginx
state: presentfatal: [host]: FAILED! => {"msg": "An unhandled exception occurred while templating..."}Common Causes:
Example Problem:
- name: Configure app
template:
src: config.j2
dest: /etc/app/config.yml
vars:
port: "{{ app_port }}" # app_port undefinedSolutions:
# Use default filter
vars:
port: "{{ app_port | default(8080) }}"
# Or use required filter
vars:
port: "{{ app_port | required('app_port must be defined') }}"
# Or check if defined
- name: Configure app
template:
src: config.j2
dest: /etc/app/config.yml
when: app_port is definedfatal: [host]: FAILED! => {"msg": "Unexpected templating type error occurred on (...)"}Cause: Wrong variable type (e.g., trying to use int as string)
Solution: Use type conversion filters
# Convert to string
port: "{{ app_port | string }}"
# Convert to int
replicas: "{{ replica_count | int }}"
# Convert to bool
enabled: "{{ feature_enabled | bool }}"fatal: [host]: UNREACHABLE! => {"msg": "Failed to connect to the host via ssh"}Common Causes:
Solutions:
# Test SSH connectivity
ssh user@host
# Check Ansible can ping
ansible host -m ping
# Use correct SSH key
ansible-playbook -i inventory playbook.yml --private-key=~/.ssh/id_rsa
# Specify user in inventory
[webservers]
server1 ansible_user=ubuntu ansible_ssh_private_key_file=~/.ssh/id_rsafatal: [host]: UNREACHABLE! => {"msg": "Failed to connect to the host via ssh: Permission denied (publickey)."}Solutions:
# Ensure SSH key is added to target
ssh-copy-id user@host
# Or specify key in inventory
[webservers]
server1 ansible_ssh_private_key_file=~/.ssh/custom_key
# Check SSH agent
ssh-add -l
ssh-add ~/.ssh/id_rsafatal: [host]: UNREACHABLE! => {"msg": "Authentication or permission failure."}Solutions:
# Use password authentication (less secure)
- hosts: all
vars:
ansible_ssh_pass: password # Better to use vault
ansible_become_pass: password
# Or use ask-pass
ansible-playbook -i inventory playbook.yml --ask-pass --ask-become-passfatal: [host]: FAILED! => {"msg": "Missing sudo password"}Solutions:
# Provide sudo password at runtime
ansible-playbook -i inventory playbook.yml --ask-become-pass
# Or configure passwordless sudo on target
# /etc/sudoers.d/ansible
ansible_user ALL=(ALL) NOPASSWD: ALLfatal: [host]: FAILED! => {"msg": "Could not create file: Permission denied"}Solution: Add become: yes to task or play
- name: Install package
apt:
name: nginx
state: present
become: yes
# Or for entire play
- hosts: all
become: yes
tasks:
- name: Install package
apt:
name: nginxfatal: [host]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'variable' is undefined"}Solutions:
# Use default filter
- name: Use variable with default
debug:
msg: "{{ my_var | default('default_value') }}"
# Check if defined before use
- name: Use variable conditionally
debug:
msg: "{{ my_var }}"
when: my_var is defined
# Use required filter to make it explicit
- name: Require variable
debug:
msg: "{{ my_var | required('my_var must be defined') }}"[WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see detailsCause: Variable or group name contains invalid characters (hyphens, spaces)
Solution: Use underscores instead
# Wrong
[web-servers]
# Correct
[web_servers][WARNING]: Could not match supplied host pattern, ignoring: webserversCause: Host group not defined in inventory
Solution: Check inventory file
# inventory/hosts
[webservers]
web1.example.com
web2.example.com
[databases]
db1.example.com[WARNING]: Unable to parse /path/to/inventory as an inventory sourceCause: Invalid inventory format
Solution: Fix inventory syntax
# Wrong - mixing styles
[webservers]
web1 ansible_host=192.168.1.10
web2
ansible_host: 192.168.1.11 # YAML syntax in INI file
# Correct - consistent INI format
[webservers]
web1 ansible_host=192.168.1.10
web2 ansible_host=192.168.1.11fatal: [host]: FAILED! => {"msg": "Invalid data passed to 'loop', it requires a list"}Cause: Loop variable is not a list
Solution: Ensure loop variable is a list
# Wrong
- name: Install packages
apt:
name: "{{ item }}"
loop: nginx # String, not list
# Correct
- name: Install packages
apt:
name: "{{ item }}"
loop:
- nginx
- python3[DEPRECATION WARNING]: with_items is deprecated, use loop insteadSolution: Replace with_items with loop
# Old style (deprecated)
- name: Install packages
apt:
name: "{{ item }}"
with_items:
- nginx
- python3
# New style
- name: Install packages
apt:
name: "{{ item }}"
loop:
- nginx
- python3ERROR! The requested handler 'restart nginx' was not foundCause: Handler name mismatch or handler not defined
Solution: Ensure handler name matches exactly
# tasks/main.yml
- name: Configure nginx
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: restart nginx # Must match handler name exactly
# handlers/main.yml
- name: restart nginx # Must match notification exactly
systemd:
name: nginx
state: restartedfatal: [host]: FAILED! => {"msg": "Unable to retrieve file contents. Could not find or access 'file.yml'"}Cause: File path incorrect or file doesn't exist
Solution: Check file path (relative to playbook location)
# Wrong
- include_tasks: tasks/install.yml # If tasks/ doesn't exist
# Correct
- include_tasks: install.yml # File in same directory
# Or
- include_tasks: roles/common/tasks/install.yml # Full pathERROR! Recursively included/imported file is causing infinite loopCause: Circular dependency (file A includes file B, file B includes file A)
Solution: Restructure includes to avoid circular dependencies
ERROR! couldn't resolve module/action 'community.general.docker_container'Cause: Collection not installed
Solution: Install required collection
# Install single collection
ansible-galaxy collection install community.general
# Install from requirements.yml
# requirements.yml
collections:
- name: community.general
version: ">=5.0.0"
ansible-galaxy collection install -r requirements.ymlERROR! Requirement already satisfied by a different versionSolution: Update or downgrade collection
# Force reinstall
ansible-galaxy collection install community.general --force
# Install specific version
ansible-galaxy collection install community.general:5.0.0fatal: [host]: FAILED! => {"msg": "This module does not support check mode"}Cause: Module doesn't support check mode
Solution: Skip check mode for this task
- name: Command that doesn't support check mode
command: /usr/local/bin/custom-script.sh
check_mode: no # Always run, even in check mode# Basic verbosity
ansible-playbook playbook.yml -v
# More details
ansible-playbook playbook.yml -vv
# Very verbose (shows module arguments)
ansible-playbook playbook.yml -vvv
# Connection debugging
ansible-playbook playbook.yml -vvvv# Print variable
- name: Debug variable
debug:
var: my_variable
# Print message
- name: Debug message
debug:
msg: "Value is {{ my_variable }}"
# Print all facts
- name: Print all facts
debug:
var: ansible_facts
# Conditional debug
- name: Debug when condition met
debug:
msg: "Debug message"
when: ansible_distribution == "Ubuntu"# Validate conditions
- name: Assert variables are defined
assert:
that:
- app_version is defined
- app_version | length > 0
- app_port | int > 0
- app_port | int < 65536
fail_msg: "Invalid configuration"
success_msg: "Configuration validated"Solutions:
# ansible.cfg
[ssh_connection]
pipelining = True# ansible.cfg
[defaults]
gathering = smart
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible_facts
fact_caching_timeout = 86400- hosts: all
gather_facts: no- name: Long running task
command: /usr/bin/long-task
async: 3600
poll: 0Solutions:
- hosts: all
serial: 10 # Process 10 hosts at a time- hosts: all
strategy: free # Don't wait for all hosts to complete task# Syntax check
ansible-playbook playbook.yml --syntax-check
# Dry run
ansible-playbook playbook.yml --check --diff
# Run with tags
ansible-playbook playbook.yml --tags webserver
# Limit to specific hosts
ansible-playbook playbook.yml --limit webserver1
# Verbose output
ansible-playbook playbook.yml -vvv
# List tasks
ansible-playbook playbook.yml --list-tasks
# List hosts
ansible-playbook playbook.yml --list-hosts
# Step through tasks
ansible-playbook playbook.yml --step
# Start at specific task
ansible-playbook playbook.yml --start-at-task="Install nginx"