Skip to main content

Testing and Validating Playbooks

1. Performing Syntax Check

Example: Basic syntax check command

ansible-playbook --syntax-check my_playbook.yml
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.

Example: Running a playbook in check mode

ansible-playbook site.yml --check

Example: Running a playbook in check mode with diff

ansible-playbook site.yml --check --diff
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 vars to 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

Example: Running ansible-lint

ansible-lint site.yml
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

Example: Validating playbook syntax

ansible-playbook --syntax-check site.yml
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 command or shell for idempotent operations
  • Ensure modules are used with state parameters 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

Example: Using ansible-playbook with verify

ansible-playbook site.yml --verify
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.

Was this article helpful?