Testing and Validating Playbooks
1. Performing Syntax Check
| Option | Values | Description |
|---|---|---|
| --syntax-check | true |
Performs a syntax check without executing the playbook |
| --check | false |
Simulates execution without making changes (optional) |
Note: The
--syntax-check flag ensures that your playbook is syntactically correct before running it in a production environment.2. Running Check Mode
Check mode in Ansible allows you to test a playbook without making any changes to the target systems. It simulates the execution and shows what would happen if the playbook were run in normal mode.
| Option | Values | Description |
|---|---|---|
| --check | -C |
Enable check mode. This flag tells Ansible to run in check mode. |
| --diff | -D |
When used with check mode, shows the differences between the current state and the desired state. |
Note: In check mode, Ansible will not make any changes to the target systems. It only reports what would happen if the playbook were run in normal mode.
3. Using Diff Mode
Example: Enabling diff mode in a playbook
---
- name: Test diff mode
hosts: all
tasks:
- name: Update configuration file
copy:
content: "new config content"
dest: /etc/myapp/config.conf
diff: true
| Option | Values | Description |
|---|---|---|
| diff | true |
Enable diff mode for the task |
| diff | false |
Disable diff mode for the task |
Note: Diff mode shows changes made to files during task execution. It only works with file-based modules like
copy, template, and lineinfile.
Warning: Diff mode can expose sensitive data in logs if file contents are shown. Use with caution in production environments.
4. Testing with Molecule
Molecule is a tool for testing Ansible roles and playbooks in isolated environments. It supports various drivers like Docker, Vagrant, and Podman.
| Driver | Description | Notes |
|---|---|---|
| docker | Uses Docker containers for testing | Fast and lightweight, ideal for CI/CD pipelines |
| vagrant | Uses Vagrant boxes for testing | Good for complex environments, slower startup |
| podman | Uses Podman containers for testing | Secure and rootless, alternative to Docker |
Example: Basic Molecule Configuration
# molecule.yml
---
driver:
name: docker
platforms:
- name: instance
image: "geerlingguy/docker-ubuntu2004-ansible:latest"
pre_build_image: true
provisioner:
name: ansible
config_options:
defaults:
callback_whitelist: timer
verifier:
name: ansible
Example: Test Playbook with Molecule
# molecule/default/tests/test.yml
---
- name: Test role
hosts: all
tasks:
- name: Ensure service is running
command: systemctl is-active myservice
register: result
- name: Verify service status
assert:
that:
- result.rc == 0
Note: Molecule uses a
molecule.yml file to define test scenarios and supports multiple platforms and drivers.
Key Features of Molecule
- Isolation: Runs tests in isolated environments
- Multi-driver support: Supports Docker, Vagrant, Podman
- Test scenarios: Define different test environments
- Verifiers: Use Ansible or Testinfra for validation
5. Creating Test Scenarios
Example: Basic Test Scenario Structure
---
- name: Test playbook execution
hosts: test_hosts
gather_facts: false
tasks:
- name: Ensure service is running
ansible.builtin.systemd:
name: nginx
state: started
| Field | Description | Values |
|---|---|---|
| name | Test scenario description | Test playbook execution |
| hosts | Target inventory group for testing | test_hosts |
| gather_facts | Whether to gather facts before running tasks | false |
Note: Use
hosts: localhost for local testing or define a dedicated test inventory group.
Example: Testing with Mock Data
---
- name: Test with mock data
hosts: test_hosts
vars:
mock_service_status: "running"
tasks:
- name: Check service status
ansible.builtin.command:
cmd: "systemctl is-active {{ mock_service_status }}"
Best Practices for Test Scenarios
- Isolate test environments from production
- Use
varsto mock external dependencies - Validate expected outcomes with assertions
6. Using Docker Driver
Example: Running Ansible tests with Docker driver
---
- name: Test playbook with Docker
hosts: localhost
gather_facts: false
vars:
docker_image: "centos:7"
tasks:
- name: Run test playbook inside Docker container
ansible.builtin.shell: |
ansible-playbook test.yml -i localhost,
args:
executable: /bin/bash
| Driver | Description | Usage |
|---|---|---|
| docker | Uses Docker containers for testing | Run tests inside isolated Docker environments |
Note: Requires Docker daemon to be running on the host machine.
Warning: Ensure proper cleanup of Docker containers after tests to avoid resource leaks.
7. Running Ansible Lint
| Option | Values | Notes |
|---|---|---|
| --version | -v |
Display the version of ansible-lint |
| --config-file | .ansible-lint.yml |
Specify a custom configuration file |
| --exclude | path/to/exclude |
Exclude paths from linting |
| --offline | true |
Disable online checks (e.g., galaxy) |
Note: Ansible Lint checks for best practices and potential issues in your playbooks.
Warning: Some rules may be too strict for development environments.
8. Validating YAML
| Option | Values | Notes |
|---|---|---|
| ansible-playbook --syntax-check | ansible-playbook playbook.yml |
Validates YAML syntax and structure of playbooks |
| ansible-lint | ansible-lint playbook.yml |
Checks for best practices and potential issues in playbooks |
| YAML syntax | yaml.load() |
Python function to parse YAML content |
Note: The
--syntax-check flag ensures that YAML is properly formatted and all required keys are present.
Warning: Even if syntax is correct, logical errors in playbooks can still cause failures at runtime.
9. Testing Idempotence
Idempotence in Ansible ensures that running a playbook multiple times produces the same result as running it once. This is crucial for reliable infrastructure automation.
| Aspect | Description | Best Practice |
|---|---|---|
| Idempotent Actions | Actions that can be safely repeated without side effects | Use state-based modules like file, package, and user |
| State Management | Ensuring resources are in a desired state | Use state: present or state: absent to define desired outcomes |
| Idempotent Modules | Modules that support idempotent operations | Examples: copy, template, yum, apt |
Example: Idempotent File Creation
- name: Create a file if it doesn't exist
file:
path: /etc/myapp/config.ini
state: touch
mode: '0644'
Note: The
file module with state: touch ensures the file is created if it doesn't exist, and does nothing if it already exists.
Example: Idempotent Package Installation
- name: Install a package if not present
yum:
name: httpd
state: present
Warning: Avoid using
command or shell modules for idempotent tasks as they do not inherently support idempotence.
Idempotency Best Practices
- Use modules that support state management (e.g.,
file,package) - Avoid
commandorshellfor idempotent operations - Ensure modules are used with
stateparameters to define desired outcomes - Test playbooks multiple times to verify idempotency
10. Using Assert Module
| Parameter | Description | Values |
|---|---|---|
| assert | Condition to evaluate | condition |
| fail_msg | Message to display if assertion fails | String |
| success_msg | Message to display if assertion passes | String |
| that | List of conditions to check | List of assert objects |
Example: Basic Assert Usage
- name: Check if a file exists
assert:
that:
- "file_exists"
fail_msg: "File does not exist"
success_msg: "File exists as expected"
Example: Assert with Variable
- name: Validate variable value
assert:
that:
- "my_var is defined"
- "my_var == 'expected_value'"
fail_msg: "Variable my_var is not set to expected value"
11. Verifying Results
Example: Check Task Results
---
- name: Verify web server is running
hosts: webservers
tasks:
- name: Check if nginx is running
command: systemctl is-active nginx
register: nginx_status
changed_when: false
- name: Assert nginx is running
assert:
that:
- "nginx_status.rc == 0"
fail_msg: "Nginx is not running"
success_msg: "Nginx is running successfully"
| Property | Description | Values |
|---|---|---|
| register | Stores task output in a variable for later use | variable_name |
| assert | Checks a condition and fails if it's not met | that: [condition] |
| fail_msg | Message to display if assertion fails | string |
| success_msg | Message to display if assertion passes | string |
Note: Use
register to capture command output or task results for verification in subsequent tasks.
Warning:
assert will fail the playbook if any condition in that: evaluates to false.
Example: Validate Configuration Files
---
- name: Validate configuration files
hosts: all
tasks:
- name: Read config file content
command: cat /etc/app.conf
register: config_content
- name: Verify config contains required keys
assert:
that:
- "'port' in config_content.stdout"
- "'host' in config_content.stdout"
fail_msg: "Configuration file missing required keys"
12. Running Verify Phase
| Option | Description | Default |
|---|---|---|
| --verify | Run the playbook in verify mode, which checks syntax and runs a dry-run without making changes | false |
Note: The verify mode is useful for catching syntax errors before running the full playbook in a production environment.