Ansible Molecule is a tool used for testing and validating Ansible roles and playbooks in multiple scenarios. It helps automate the testing of Ansible code and ensures that it will work as intended when applied to target servers. With Molecule, you can test roles and playbooks across different environments and platforms. supports different virtualization and containerization technologies such as Docker, Podman, Vagrant, and cloud providers such as Amazon Web Services, Microsoft Azure, and Google Cloud Platform.

Ansible Molecule is a powerful tool for automating and streamlining the testing and validation of Ansible roles and playbooks. It uses a testing framework such as pytest and provides an environment for the role or playbook to run in.

In this tutorial, you will learn how to set up and test Ansible Roles automatically via Molecule and Docker. You will install Ansible, Docker, and Molecule at the same time, then you will learn how to create an Ansible Roles boilerplate with Molecule and set up automatic testing of Ansible Roles via Docker containers.

Prerequisites

To complete this tutorial, you must have the following requirements:

  • A Linux system – This example uses the latest version of Ubuntu 22.04 server with hostname ‘ansible-test‘.
  • A non-root user with sudo/root administrator privileges – This example uses a user called ‘alice‘.
  • Understanding of Ansible and Ansible Roles.

Installing Dependencies

In the first section, you will install package dependencies that will be used in the following guide. This includes Python3, Pip, Ansible, and Docker CE (Community Edition). Before you start, run the following command to update and refresh your package index.

sudo apt update

Once the package index is updated, enter the following command to install Python3, Pip3, Virtualenv, and Ansible.

sudo apt install python3 python3-pip python3-venv ansible ca-certificates curl gnupg lsb-release

Input y when prompted and press ENTER to proceed.

<img alt="install dependnecies" data-ezsrc="https://kirelos.com/wp-content/uploads/2023/05/echo/1-install-deps.png6470b95b5d192.jpg" ezimgfmt="rs rscb10 src ng ngcb9" height="492" loading="lazy" src="data:image/svg xml,” width=”750″>

After Python3, Pip3, Virtualenv, and Ansible are installed, you will be installing the Docker CE (Community Edition) via the official Docker Repository.

Enter the following command to create a new directory ‘/etc/apt/keyrings‘ and download the GPG key of the Docker repository.

sudo mkdir -m 0755 -p /etc/apt/keyrings

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

Next, add the Docker CE repository to your system using the below command.

echo 

  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu

  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

<img alt="Add docker repo" data-ezsrc="https://kirelos.com/wp-content/uploads/2023/05/echo/2-add-docker-repo.png6470b95c420f2.jpg" ezimgfmt="rs rscb10 src ng ngcb9" height="119" loading="lazy" src="data:image/svg xml,” width=”750″>

Then update and refresh your ubuntu package index to apply the changes.

sudo apt update

Now install Docker CE packages via the following ‘apt‘ command below. When prompted, input y to confirm and press ENTER to proceed.

sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

<img alt="install docker" data-ezsrc="https://kirelos.com/wp-content/uploads/2023/05/echo/3-install-docker.png6470b95cab55e.jpg" ezimgfmt="rs rscb10 src ng ngcb9" height="208" loading="lazy" src="data:image/svg xml,” width=”750″>

With the Docker CE installed, add your current user to the group ‘docker‘ using the following command. This will allow your user to run Docker containers.

sudo usermod -aG docker $USER

You can now verify the Docker configuration using the command below. This will download and run the container ‘hello-world’ on your system.

docker run hello-world

If the user can execute Docker containers, you should see the output of the ‘hello-world‘ container as below.

<img alt="docker run hello world" data-ezsrc="https://kirelos.com/wp-content/uploads/2023/05/echo/4-docker-run-hello-world.png6470b95d08239.jpg" ezimgfmt="rs rscb10 src ng ngcb9" height="584" loading="lazy" src="data:image/svg xml,” width=”744″>

With this, you have now installed package dependencies such as Python3, Pip3, Virtualenv, Ansible, and Docker. In the next section, you will install Molecule within the Python virtual environment.

Installing Molecule

In this section, you will create a new Python virtual environment that will be used for the development environment of Ansible Roles. You will also install Molecule and the Docker driver.

Run the following command to create a new Python virtual environment called ‘ansible-venv‘. Once the virtual environment is created, you should see the new directory ‘ansible-venv‘ on your current working directory.

python3 -m venv ansible-venv

Next, run the below command to activate your Python virtual environment. Once activated, your prompt shell will become like this: ‘(ansible-venv) [email protected]:…‘.

source ansible-venv/bin/activate

Now move your working directory to ‘ansible-venv‘, then install Python packages Molecule and the Molecule Docker driver using the ‘pip3‘ command as below.

cd ansible-venv/

pip3 install wheel molecule 'molecule-plugins[docker]'

Below you can see the installation process of Molecule and the Docker driver.

<img alt="install molecule and docker driver" data-ezsrc="https://kirelos.com/wp-content/uploads/2023/05/echo/5-install-molecule-docker-driver.png6470b95d8ec6f.jpg" ezimgfmt="rs rscb10 src ng ngcb9" height="237" loading="lazy" src="data:image/svg xml,” width=”750″>

After the Molecule and Docker driver are installed, you will next create a new Ansible Role via Molecule.

Initializing Ansible Role with Molecule

After installing Molecule and the Docker driver, you will now create new Ansible Roles via molecule. In this example, you will create roles that will be used to install basic LEMP Stack (Linux, Nginx, MariaDB, and PHP-FPM) packages and ensure that LEMP Stack services are running and enabled.

First, run the following command to generate the Ansible Role boilerplate called ‘test.lemp‘ with the driver ‘docker‘. This will create a new directory called ‘lemp‘ on your current working directory.

molecule init role test.lemp --driver-name docker

<img alt="generate ansible role boilerplate molecule" data-ezsrc="https://kirelos.com/wp-content/uploads/2023/05/echo/6-generate-role-bilerplate-molecule.png6470b95e17ee8.jpg" ezimgfmt="rs rscb10 src ng ngcb9" height="202" loading="lazy" src="data:image/svg xml,” width=”750″>

Move to the ‘lemp’ directory using the below command.

cd lemp

Open the file ‘tasks/main.yml’ using the nano editor and define some tasks for your role.

nano tasks/main.yml

Add the following lines to the file. With this, you will create tasks for installing LEMP Stack packages and verifying LEMP services.

---

- name: "Installing LEMP Stack"

  apt:

    name: "{{ pkg_list }}"

    state: present

- name: "Ensure LEMP Services is running"

  service:

    name: "{{ item }}"

    state: started

    enabled: true

  with_items: "{{ svc_list }}"

Save and close the file when finished.

<img alt="install lemp tasks" data-ezsrc="https://kirelos.com/wp-content/uploads/2023/05/echo/7-install-lemp-tasks.png6470b95eab484.jpg" ezimgfmt="rs rscb10 src ng ngcb9" height="300" loading="lazy" src="data:image/svg xml,” width=”467″>

Now open the file ‘vars/main.yml‘ using the nano editor and add variables for your Ansible roles.

nano vars/main.yml

Add the following lines to the file. In this example, you will define the variable ‘pkg_list‘ that contains package names of the LEMP stack, and the variable ‘svc_list’ that contains the services name of the LEMP Stack.

---

pkg_list:

  - nginx

  - mariadb-server

  - php-fpm

  - php-cli

svc_list:

  - nginx

  - mariadb

  - php8.1-fpm

Save the file and exit the editor when finished.

<img alt="variable lis packages" data-ezsrc="https://kirelos.com/wp-content/uploads/2023/05/echo/8-list-packages-vars.png6470b95f1c03c.jpg" ezimgfmt="rs rscb10 src ng ngcb9" height="272" loading="lazy" src="data:image/svg xml,” width=”335″>

Setting Up Instance for Testing

After creating Ansible roles, you will need to set up the instance that will be used to test Ansible roles. This example will be using the Docker image ‘mipguerrero26/ubuntu-python3‘, which by default contains the Python3 package. You can use different Docker images, but you must ensure that Python is installed.

First, run the following command to download the Docker image ‘mipguerrero26/ubuntu-python3‘.

docker pull mipguerrero26/ubuntu-python3

<img alt="download docker image" data-ezsrc="https://kirelos.com/wp-content/uploads/2023/05/echo/9-download-images-docker.png6470b95fa7cf7.jpg" ezimgfmt="rs rscb10 src ng ngcb9" height="155" loading="lazy" src="data:image/svg xml,” width=”750″>

Once downloaded, verify the list of images using the below command. You should see the Docker image ‘mipguerrero26/ubuntu-python3‘ is downloaded and available on your system.

docker images

<img alt="list image" data-ezsrc="https://kirelos.com/wp-content/uploads/2023/05/echo/10-list-images.png6470b96011d89.jpg" ezimgfmt="rs rscb10 src ng ngcb9" height="158" loading="lazy" src="data:image/svg xml,” width=”750″>

Now run the following command to run a temporary container and execute bash in the attached mode.

docker run -it mipguerrero26/ubuntu-python3 /bin/bash

Once logged in to the container, enter the following command to verify the Python and Ubuntu version.

python3 --version

cat /etc/lsb-release

You should receive an output like this – In this example, the image ‘mipguerrero26/ubuntu-python3‘ is based on Ubuntu 22.04 and comes with Python.

<img alt="verify python image" data-ezsrc="https://kirelos.com/wp-content/uploads/2023/05/echo/11-verify-python-on-image.png6470b9608f453.jpg" ezimgfmt="rs rscb10 src ng ngcb9" height="218" loading="lazy" src="data:image/svg xml,” width=”750″>

Type ‘exit‘ to log out from the container shell.

Next, open the default Molecule configuration ‘molecule/default/molecule.yml‘ using the nano editor.

nano molecule/default/molecule.yml

On the ‘platform’ section, change the name and the default image that will be used for testing. Also, add the setting ‘privileged: true‘. In this example, the instance for testing will be named ‘instance-ubuntu22.04’ with the image ‘mipguerrero26/ubuntu-python3‘.

platforms:

  - name: instance-ubuntu22.04

    image: mipguerrero26/ubuntu-python3

    privileged: true

Save the file and exit the editor.

<img alt="molecule instance setting" data-ezsrc="https://kirelos.com/wp-content/uploads/2023/05/echo/12-molecule-yaml.png6470b96122ee9.jpg" ezimgfmt="rs rscb10 src ng ngcb9" height="327" loading="lazy" src="data:image/svg xml,” width=”383″>

Now run the ‘molecule’ command below to verify the list of instances within your Molecule test project. You should see the instance called ‘instance-ubuntu22.04‘ with the driver ‘Docker‘.

molecule list

<img alt="list molecule instance ready to test" data-ezsrc="https://kirelos.com/wp-content/uploads/2023/05/echo/13-list-molecule-instance-for-test.png6470b961b5b73.jpg" ezimgfmt="rs rscb10 src ng ngcb9" height="182" loading="lazy" src="data:image/svg xml,” width=”750″>

Running Molecule Test Converge

With the Molecule instance created, you can now run the test by simply invoking the command ‘molecule converge‘ as below. This ‘converge‘ parameter allows you to test and verify Ansible roles against the instance that is available on your Molecule project. In this example, the Ansible role ‘lemp‘ will be run against the instance ‘instance-ubuntu22.04‘ via Docker.

molecule converge

Below is an output when the Molecule is running.

<img alt="molecule running test" data-ezsrc="https://kirelos.com/wp-content/uploads/2023/05/echo/14-running-test.png6470b9620d8ab.jpg" ezimgfmt="rs rscb10 src ng ngcb9" height="117" loading="lazy" src="data:image/svg xml,” width=”750″>

The Molecule task is to create a new instance via Docker for testing.

<img alt="create container instance" data-ezsrc="https://kirelos.com/wp-content/uploads/2023/05/echo/15-create-docker-container.png6470b962bd561.jpg" ezimgfmt="rs rscb10 src ng ngcb9" height="391" loading="lazy" src="data:image/svg xml,” width=”750″>

Once the instance is created, the Ansible roles will be applied against the instance.

<img alt="applying role" data-ezsrc="https://kirelos.com/wp-content/uploads/2023/05/echo/16-test-roles.png6470b9631860b.jpg" ezimgfmt="rs rscb10 src ng ngcb9" height="251" loading="lazy" src="data:image/svg xml,” width=”750″>

At this point, the Ansible role ‘lemp‘ is applied to the instance ‘instance-ubuntu22.04‘, which is running via Docker. Enter the following command to verify the running container on your system.

docker ps

You should see the container called ‘instance-ubuntu22.04’, which is matched with the Molecule instance name.

<img alt="list container" data-ezsrc="https://kirelos.com/wp-content/uploads/2023/05/echo/17-list-running-container.png6470b9638bbe8.jpg" ezimgfmt="rs rscb10 src ng ngcb9" height="82" loading="lazy" src="data:image/svg xml,” width=”750″>

Now log in to the container ‘instance-ubuntu22.04‘ using the following command.

docker exec -it instance-ubuntu22.04 /bin/bash

Then verify the list of open ports on the container using the below command. You should see port 80 used by Nginx, port 3306 used by MariaDB, and the PHP-FPM running with the sock file ‘/run/php/php8.1-fpm.sock‘. This confirms that the role ‘lemp’ is running successfully.

ss -tulpn

ss -pl | grep php

<img alt="verify process on container" data-ezsrc="https://kirelos.com/wp-content/uploads/2023/05/echo/18-verify-process-on-cointainer.png6470b9640567c.jpg" ezimgfmt="rs rscb10 src ng ngcb9" height="129" loading="lazy" src="data:image/svg xml,” width=”750″>

Lastly, run the following command to clean up your environment. This will destroy and remove the container ‘instance-ubuntu22’ from your system.

molecule destroy

<img alt="destroy container instance" data-ezsrc="https://kirelos.com/wp-content/uploads/2023/05/echo/19-destroy-container-test.png6470b964c6df8.jpg" ezimgfmt="rs rscb10 src ng ngcb9" height="370" loading="lazy" src="data:image/svg xml,” width=”750″>

If you check the list of running containers on your system, there will be no running container, because the instance ‘instance-ubuntu22‘ is removed.

docker ps

docker ps -a

<img alt="container destroyed" data-ezsrc="https://kirelos.com/wp-content/uploads/2023/05/echo/20-container-destroyed.png6470b96a21912.jpg" ezimgfmt="rs rscb10 src ng ngcb9" height="130" loading="lazy" src="data:image/svg xml,” width=”750″>

Create Test Script with testInfra Python Module

In this section, you will create the test script scenario that will be used to verify the state of the Molecule instance and ensure that roles are applied. This can be done with Python script with the module testInfra.

First, run the following pip3 command to install the testInfra module.

pip3 install pytest-testinfra

<img alt="install testInfra module" data-ezsrc="https://kirelos.com/wp-content/uploads/2023/05/echo/21-install-pytest-testinfra.png6470b96a55505.jpg" ezimgfmt="rs rscb10 src ng ngcb9" height="162" loading="lazy" src="data:image/svg xml,” width=”750″>

Once the testinfra module is installed, create a new directory ‘molecule/default/tests/‘, and create a new test file ‘molecule/default/tests/test_default.py‘ using the following nano editor command.

mkdir -p molecule/default/tests/

nano molecule/default/tests/test_default.py

Add the following Python script to the file. With this, you will test the Ansible Molecule instance to ensure that LEMP Stack packages are installed and running.

import os

import pytest

import testinfra.utils.ansible_runner

testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(

    os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')

@pytest.mark.parametrize('pkg', [

  'nginx',

  'mariadb-server',

  'php-fpm'

])

def test_pkg(host, pkg):

    package = host.package(pkg)

    assert package.is_installed

@pytest.mark.parametrize('svc', [

  'nginx',

  'mariadb',

  'php8.1-fpm'

])

def test_svc(host, svc):

    service = host.service(svc)

    assert service.is_running

    assert service.is_enabled

Save and close the file when finished.

<img alt="create test script" data-ezsrc="https://kirelos.com/wp-content/uploads/2023/05/echo/22-create-test-pytest.png6470b96abd156.jpg" ezimgfmt="rs rscb10 src ng ngcb9" height="597" loading="lazy" src="data:image/svg xml,” width=”623″>

Next, open the Molecule config file ‘molecule/default/molecule.yml’ using the nano editor to define the test.

nano molecule/default/molecule.yml

On the ‘verifier’ section, change the name to ‘testinfra‘ and add the parameter ‘directory: tests’. This means that the test script will be taken from the directory ‘tests‘.

verifier:

  name: testinfra

  directory: tests

Save the file and exit the editor.

<img alt="apply test" data-ezsrc="https://kirelos.com/wp-content/uploads/2023/05/echo/23-apply-tests.png6470b96b41f4c.jpg" ezimgfmt="rs rscb10 src ng ngcb9" height="330" loading="lazy" src="data:image/svg xml,” width=”417″>

Now execute the following command to run the test with all scenarios from start to end. The ‘molecule test‘ command will start testing by creating the instance, applying for the Ansible role, running the test, then destroying everything to clean up.

molecule test

Below is the screenshot when the ‘test‘ command is initialized, the Ansible Molecule will destroy the existing instance if available.

<img alt="run test" data-ezsrc="https://kirelos.com/wp-content/uploads/2023/05/echo/24-run-tests.png6470b96c50235.jpg" ezimgfmt="rs rscb10 src ng ngcb9" height="169" loading="lazy" src="data:image/svg xml,” width=”750″>

<img alt="destroy when available" data-ezsrc="https://kirelos.com/wp-content/uploads/2023/05/echo/25-destroy-when-available.png6470b96cc3781.jpg" ezimgfmt="rs rscb10 src ng ngcb9" height="312" loading="lazy" src="data:image/svg xml,” width=”750″>

Now the process of creating an instance for new testing.

<img alt="create new instance" data-ezsrc="https://kirelos.com/wp-content/uploads/2023/05/echo/26-create-new-environment-test.png6470b96d3b98a.jpg" ezimgfmt="rs rscb10 src ng ngcb9" height="361" loading="lazy" src="data:image/svg xml,” width=”750″>

Then the Ansible role will be applied against the instance.

<img alt="apply role" data-ezsrc="https://kirelos.com/wp-content/uploads/2023/05/echo/27-test-role-apply.png6470b96dbc19c.jpg" ezimgfmt="rs rscb10 src ng ngcb9" height="560" loading="lazy" src="data:image/svg xml,” width=”750″>

When the roles are applied, the test or verifier will begin. When successful, you should get an output such as ‘collected 6 items – 6 passed in …‘.

<img alt="run tests" data-ezsrc="https://kirelos.com/wp-content/uploads/2023/05/echo/28-run-test-script.png6470b96e1fd30.jpg" ezimgfmt="rs rscb10 src ng ngcb9" height="290" loading="lazy" src="data:image/svg xml,” width=”750″>

Lastly, the Molecule will destroy the instance when the test is finished.

<img alt="destroy instance when finished" data-ezsrc="https://kirelos.com/wp-content/uploads/2023/05/echo/29-destroy-when-finished.png6470b96e805a2.jpg" ezimgfmt="rs rscb10 src ng ngcb9" height="286" loading="lazy" src="data:image/svg xml,” width=”750″>

Ansible Molecule Process in Development

When first developing roles, you must ensure that the role is generated via Molecule. Then, you can add tasks and others components to your Ansible role, define the instance for testing, then set up the test script to ensure that the desired state is applied to the target instance.

So, when you have finished the configuration of an Ansible role and defined the instance, run the following command to test the implementation of the Ansible role in your test instance.

molecule converge

Now after creating the test script and defining the test on the Molecule configuration, run the following command to apply the test.

molecule verify

When the test was successful, you can now destroy everything and retest using the command below.

molecule destroy

molecule test

You can check the detailed available paramaters of Ansible Molecule via the command below.

molecule --help

Conclusion

In this tutorial, you have learned the installation of Ansible Molecule that allows you to perform end-to-end testing of Ansible role and playbook via multiple scenarios. You have also learned how to create roles via Molecule and run testing of the Ansible role with Docker. Lastly, you have also learned how to create a Python test script with module testInfra that can be used to verify the state of desired machines/servers.

In the end, you now better understand how to test Ansible roles and work with Molecule and Docker. Learn more about Ansible Molecule by visiting the official documentation of Ansible Molecule.