How to test Ansible playbook/role using Molecules with Docker

Why Molecule?

Have you ever faced issue that your Ansible code gets executed successfully but something went wrong like, service is not started, the configuration is not getting changed, etc?

There is another issue which you might have faced, that your code is running successfully on Redhat 6 but not running successfully on Redhat 7, to make your code smart enough to run on every Linux flavour, in order to achieve this, molecule came into the picture. Let’s start some brainstorm in Molecule.

Molecule has capability to execute YML linter and custom test cases which you have written for your Ansible code. We will explain the linter and test cases below

Why code testing is required?

Sometimes during the playbook execution, although it executes playbook fine but it does not give us the desired result so in order to check this we should use code testing in Ansible.

In general, code testing helps developer to find bugs in code/application and make sure the same bugs don’t cause the application to break. it also helps us to deliver application/software as per standard of code. code testing helps us to increase code stability.

Introduction :

This whole idea is all about to use Molecule (A testing tool) you can test your Ansible code whether it’s functioning correctly on all Linux flavour including all its functionalities or not.

Molecule is a code linter program that analyses your source code for potential errors. It can detect errors such as syntax errors; structural problems like the use of undefined variables, etc.

The molecule has capabilities to create a VM/Container environment automatically and on top, it will execute your ansible code to verify all its functionalities.

Molecule Can also check syntax, idempotency, code quality, etc

Molecule only support Ansible 2.2 or latest version

NOTE: To run ansible role with the molecule in different OS flavour we can use the cloud, vagrant, containerization (Docker)
Here we will use Docker……………………

Let’s Start……………

How Molecule works:

“When we setup molecule a directory with name “molecule” creates inside ansible role directory then it reads it’s main configuration file “molecule.yml” inside molecule directory. Molecule then creates platform (containes/Instances/Servers) in your local machine once completed it executes Ansible playbook/role inside newly created platform after successful execution, it executes test cases. Finally Molecule destroy all newly created platform”

Installation of Molecule:

Installation of the molecule is quite simple.

$ sudo apt-get update
$ sudo apt-get install -y python-pip libssl-dev
$ pip install molecule [ Install Molecule ]
$ pip install --upgrade --user setuptools [ do not run in case of VM ]

That’s it…………

Now it’s time to setup Ansible role with the molecule. We have two option to integrate Ansible with molecule:

  1. With New Ansible role
  2. with existing Ansible role

1. Setup new ansible role with molecule:

$ molecule init role --role-name ansible-role-nginx --driver-name docker

When we run above command, a molecule directory will be created inside the ansible role directory

2. Setup the existing ansible role with molecule:

Goto inside ansible role and run below command.

$ molecule init scenario --driver-name docker

When we run above command, a molecule directory will be created inside the ansible role directory

NOTE: Molecule internally uses ansible-galaxy init command to create a role

Below is the main configuration file of the molecule:

  • molecule.yml – Contains the definition of OS platform, dependencies, container platform driver, testing tool, etc.
  • playbook.yml – playbook for executing the role in the vagrant/Docker
  • tests/ | we can write test cases here.

Content of molecule.yml

cat molecule/default/molecule.yml

    - venv

  name: galaxy
  name: docker
  name: yamllint	
  - name: centos7
    image: centos/systemd:latest
    privileged: True
  - name: ubuntu16
    image: ubuntu:16.04
  name: ansible
    name: ansible-lint
#    enabled: False
  name: testinfra
    name: flake8
  name: default  # optional
    - create
    - prepare
    - destroy
    - dependency
    - create

Explanation of above contents:


Testing roles may rely upon additional dependencies. Molecule handles managing these dependencies by invoking configurable dependency managers.

“Ansible Galaxy” is the default dependency manager.


A linter is a problem which analyses our code for potential errors.

What code linters can do for you?

Code linter can do:

  1. Syntax errors;
  2. Check for undefined variables;
  3. Best practice or code style guideline.
  4. Extra lines.
  5. Extra spaces. etc

**We have linters for almost every programming languages like we have yamllint for YAML languages, etc.

yamllint: It checks for syntax validity, key repetition, lines length, trailing spaces, indentation, etc.

provisioner: Ansible is the default provisioner. No other provisioner will be supported.

Flake8:– is the default verifier linter. Usage python file


What platform (Containers) will be created and Ansible code will be executed.


Driver defines your platform where your Ansible code will be executed

Molecule supports below drivers:

  • Azure
  • Docker
  • EC2
  • GCE
  • Openstack
  • Vagrant


Scenario – scenario defines what will be performed when we run molecule

Below is the default scenario:

–> Test matrix

└── default
├── lint
├── destroy
├── dependency
├── syntax
├── create
├── prepare
├── converge
├── idempotence
├── side_effect
├── verify
└── destroy

However, we can change this scenario and sequence by changing molecule.yml file :

  name: default  # optional
  create_sequence:      # molecule create 
    - create
    - prepare
  check_sequence:       # molecule check 
    - destroy
    - dependency
    - create
    - prepare
    - converge
    - check
    - destroy
  converge_sequence:    # molecule converge 
    - dependency
    - create
    - prepare
    - converge
  destroy_sequence:     # molecule destroy 
    - cleanup
    - destroy
  test_sequence:        # molecule test 
#    - lint
    - cleanup
    - dependency
    - syntax
    - create
    - prepare
    - converge

NOTE: If anyone scenario (action) fails, others will not be executed. this is the default molecule behaviour

Here I am defining all the scenarios:

lint: Checks all the YAML files with yamllint

destroy: If there is already a container running with the same name, destroy that container

Dependency: This action allows you to pull dependencies from ansible-galaxy if your role requires them

Syntax: Checks the role with ansible-lint

create: Creates the Docker image and use that image to start our test container.

prepare: This action executes the prepare playbook, which brings the host to a specific state before running converge. This is useful if your role requires a pre-configuration of the system before the role is executed.

Example: prepare.yml

- name: Prepare
  hosts: all
  gather_facts: false
    - name: Install net-tools curl
      	name: ['curl', 'net-tools']
      	state: installed 
      when: ansible_os_family == "Debian"

NOTE: when we run “molecule converge” below task will be performed :

====> Create –> create.yml will be called
====> Prepare –> prepare.yml will be called
====> Provisioning –> playbook.yml will be called

converge: Run the role inside the test container.

idempotence: molecule runs the playbook a second time to check for idempotence to make sure no unexpected changes are made in multiple runs:

side_effect: Intended to test HA failover scenarios or the like. See Ansible provisioner

verify: Run tests inside the container which we have written

destroy: Destroys the created container

NOTE: When we run molecule commands, a directory with name molecule created inside /tmp which is molecule managed, which contains ansible configuration, Dockerfile for all linux flavour and ansible inventory

cd /tmp/molecule

└── osm_nginx
└── default
├── ansible.cfg
├── Dockerfile_centos_systemd_latest
├── Dockerfile_ubuntu_16_04
├── inventory
│ └── ansible_inventory.yml
└── state.yml

state.yml :- maintain scenario which has been performed .

Molecule managed

converged: true
created: true
driver: docker
prepared: true


This is is most important part of Molecule where we will write some test cases.

Testinfra is the default test runner.

Below module should be installed:

  • $ pip install testinfra
  • $ molecule verify

Molecule calls below file for unit test using “testinfra” verifier



Verifier is used for running your test cases.

Below are the three verifiers which we can use in Molecule

  • testinfra – It usage python language for writing test cases.
  • goss – It usage yml language for writing test cases.
  • serverspac – usage ruby language for writing test cases.

Here I am using testinfra as verifier for writing test case.

Molecule commands:

  • # molecule check [ Run playbook.yml in check mode ]
  • # molecule create [ Create instance/ Platform]
  • # molecule destroy [ destroy instance / Platform]
  • # molecule verify [ perform unit test ]
  • # molecule test [ It performs below default scenario in sequence ]
  • # molecule prepare
  • #molecule converge

NOTE: To enable logs to run a command with –debug flag

$ molecule –debug test

Sample Test cases :

cat molecule/default/tests/

import os

import testinfra.utils.ansible_runner

testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(

def test_user(host):
    user = host.user("www-data")
    assert user.exists

def test_nginx_is_installed(host):
    nginx = host.package("nginx")
    assert nginx.is_installed

def test_nginx_running_and_enabled(host):
  os = host.system_info.distribution
  if os == 'debian':
    nginx1 = host.service("nginx")
    assert nginx1.is_running
    assert nginx1.is_enabled

def test_nginx_is_listening(host):
    assert host.socket('tcp://').is_listening

 That’s all ! we have covered all required topics which will help you to create your own environment of Molecule and test cases.

Thanks all !!! see you soon with new and effective blog 🙂

Links you may refer:



Managing errors is one of the major challenges while working with any code, the same goes with ansible. It has its own ways of managing errors, whenever ansible encounters an error it stops the execution by default like most of the programming languages and throws an error, and in most cases, these errors leave the hosts in the undesirable state.

To avoid servers from undergoing into an undesirable state while execution, Ansible came up with the various ways by providing options like ignore_errors, any_errors_fatal, and many more such options. But these parameters are constrained to particular cases and can’t be used everywhere. Also, this way we are not managing the errors, we are just playing safe !!!


Ansible came up with the solution to this problem using “BLOCKS”. Blocks are the tasks that are logically grouped together to serve the purpose. Consider BLOCKS as the “try” parameter used for exception handling in most of the programming languages. We define those task in blocks which are more prone to cause errors.

EXAMPLE-Let’s take the example of Apache2 installation on the Ubuntu to understand better, here we will be using the apt and service module to install and start the Apache2 service respectively. These two tasks will be defined under a single block and the playbook for the same will look something like

- name: exception handling in ansible
  hosts: web
  become: true
    - name: install and start the apache2 service
        - name: install apache2
            name: apache2
            update_cache: true
            state: present
        - name: enable and restart the apache2 service
            name: apache2
            enabled: true
            state: restarted

Here you can see that the multiple tasks are defined under a single block, this way it will be easier to manage the code. As the single block can be managed more easily than individual tasks.


Now comes the error handling part, like I have already mentioned that the code which is more prone to throw errors is defined under blocks and in case if the block fails we have the option of “rescue” and “always” which is more or less similar to “catch” and “finally” when compared to other programming languages.

Now, let’s consider due to some reasons the above block fails, in those cases, we can introduce “rescue & always” to manage errors. For the sake of this example, we are printing the message to understand better, although we can use any module in such cases. Now the updated playbook with rescue and always with the debug module will look something like this,

- name: exception handling in ansible
  hosts: web
  become: true
    - name: install and start the apache2 service
        - name: install apache2
            name: apache2
            update_cache: true
            state: present
        - name: enable and restart the apache2 service
            name: apache2
            enabled: true
            state: restarted

        - debug: 
            msg: "Hi!!! I will be executed when the block fails"
        - debug: 
             msg: "Hi!!! I will always execute."

Here, Also I have created a situation so that the task defined in the block will fail and automatically the tasks defined in “rescue” will be executed( print message is our case). Also, the tasks defined in “always” will be executed every time. Now, after running the playbook the output will look something like-

Here, we can see that one of the tasks in the block fails which leads to failure of the whole block resulting in the calling of rescue and always. And the task defined in rescue and always is executed( message is printed on the console output ).

I hope this post clears out how the playbook got executed successfully, in-spite of the errors. This way it will be more easy for the users to write efficient and error-free playbooks.

Ansible has once again proven its worth!!!

Cheers !!! 


Speeding up Ansible Execution Part 1

The knowledge of one of the SCM tools is a must for any DevOps engineer, ANSIBLE is one of the popular tools in this category, we all are aware of the ease that Ansible provides whether it is infra provisioning, orchestration or application deployment.
The reason for the vast popularity of Ansible is the long list of modules it provides to support any level of automation, moreover it also gives users the flexibility to create their own modules as per their requirement.
But The purpose of this blog is not to mention the features that ansible provides, but to show how we can speed up our playbook execution in Ansible, as a beginner executing ansible, is very easy and it also feels like saving a lot of time with it, but as you dive deep into it, you will come to know that running ansible playbooks will engage you for a considerable amount of time.
There are a lot of articles available on the internet on how we can speed up our ansible execution, so I have decided to sum up those articles into my blog, with the following methods, we can reduce our execution time without compromising with the overall performance of Ansible.
Before starting, I request  you guys to make a small change in your ansible configuration file (ansible.cfg), this small change will help you in tracking the time it will take for the playbook execution, and it also lists out the time is taken by each task.
Just add these lines to your ansible.cfg file under default section,


callback_whitelist = profile_tasks


When you are running your playbooks on various hosts, then you may have noticed that the number of servers where the playbook executes simultaneously is 5. You can increase this number inside the ansible.cfg file:
# ansible.cfg

forks = 10

or with a command line argument to ansible-playbook with the -f or –forks options. We can increase or decrease this value as per our requirement.
while using forks we should use “local_action” or “delegated” steps limited in number, as with higher fork value it will affect the ansible-server’s performance.


In ansible, each task blocks the playbook, meaning the connections stay open until the task is done on each node, which is some cases takes a lot of time, here we can use “async” for those particular tasks, with the help of this ansible will automatically move to another task without waiting for the task execution on each node.
To launch a task asynchronously, we need to specify its maximum runtime and how frequently we would like to poll for status, it’s default value in 10 sec.

– name: “name of the task”  

command: “command we want to execute”     

async: 40    

poll: 15
The only condition is that the subsequent tasks must not have a dependency on this task.

Free Strategy 

When running Ansible playbooks, you might have noticed that the Ansible runs every task on each node one by one, it will not move to another task until a particular task is completed on each node, which will take a lot of time, in some cases.
By default, the strategy is set to “linear”, we can set it to free.

– hosts: “hosts/groups”  

name: “name of the playbook”  

strategy: free

It will run the playbook on each host independently, without waiting for each node to complete.
Facts gathering is the default feature while executing playbook, sometimes we don’t need it.
In those cases, we can disable facts gathering, This has advantages in scaling Ansible in push mode with very large numbers of systems.

– hosts: “hosts/groups”  

name: “name of the playbook”  

gather_facts: no


For each task in Ansible, there are lots of ssh connection created, which results in increasing the total execution time. Pipelining reduces the number of ssh operations required to execute a module by executing many Ansible modules without an actual file transfer. We just have to make these changes in the ansible.cfg file,
# ansible.cfg Pipelining = True
Although this can result in a very significant performance improvement when enabled, Pipelining is disabled by default because requiretty is enabled by default for many distros.

Poll Interval

When we run any the task in Ansible, it starts polling to check if the task is completed on the host or not, we can decrease this polling interval time in ansible.cfg to increase its performance, but it will increase the CPU usage, so we need to adjust its value accordingly We just have to adjust this the parameter in the ansible.cfg file,

so, these are the various ways to decrease our playbook execution time in Ansible, generally we don’t use all these methods in a single setup, we use these features as per the requirement, 
The main motive of writing this blog is to determine the factors which will help in fine-tuning the Ansible performance, and there are many more factors which serves the same purpose but here I am mentioning the most important parameters among them.
I hope I have covered all the important aspects of the blog, feel free to provide your valuable feedback.
Thanks !!!



Thinking what the above diagram is all about. Once you are done with this blog, you will know exactly what it is. Till one month ago, I was of the opinion that Dynamic Inventory is a cool way of managing your AWS infrastructure as you don’t have to track your servers you just have to apply proper tags and Ansible Dynamic Inventory magically manages the inventory for you. Having said that I was not really comfortable using dynamic inventory as it was a black box I tried going through the Python script which was very cryptic & difficult to understand. If you are of the same opinion, then this blog is worth reading as I will try to demystify how things work in Dynamic Inventory and how you can implement your own Dynamic inventory using a very simple python script.

You can refer below article if you want to implement Dynamic inventory for your AWS infrastructure.

Now coming to what is dynamic inventory and how you can create one. You have to understand what Ansible accepts as an inventory file. Ansible expects a JSON in the below format. Below is the screenshot showing the bare minimum content which is required by Ansible. Ansible expects a dictionary of groups (each group having a list of group>hosts, and group variables in the group>vars dictionary), and a _meta dictionary that stores host variables for all hosts individually (inside a hostvars dictionary).

So as long as you can write a script which generates output in the above JSON format. Ansible won’t give you any trouble. So let’s start creating our own custom inventory.

I have created a python script which reads the data from input.csv and generates the JSON as mentioned above. For simplicity, I have kept my input.csv as simple as possible. You can find the code here:-

If you want to test it just clone the code and replace the IP, user and key details as per your environment in the input.csv file. To make sure that our python script is generating the output in standard JSON format as expected by Ansible. You can run ./ –list
And it will generate the output in standard JSON format as shown in below screenshot.

If you want to check how the static inventory file would have looked for the above scenario. You can refer to the below screenshot. It would have served the same purpose as the above dynamic inventory

Now to make sure your custom inventory is working fine. You can run

ansible all -i -m ping

It will try to ping all the hosts mentioned in the CSV. Let’s check it

See it is working, that’s how easy it is.

Instead of a static CSV file, we can have a database where all the hosts and related details are getting updated dynamically. Then Ansible dynamic inventory script can use this database as an inventory source as long as it returns a JSON structure, mentioned in the first screenshot.

Best practices of Ansible Role

Ansible Role
(Best practices)


I have written many Ansible Roles in my career. But when I talk about the “Best Practice of writing an Ansible Role” half of them were non-considerable. When I started writing Ansible Roles, I wrote them with a thought as to just complete my task. This thought made me struggle as a “DevOps Guy” because of this practice I just have to write each and every Ansible Role again and again when needed. Without the proper understanding about the Architecture of Ansible Role, I was incapable of enjoying all the functionality which I could have used to write an Ansible Role where I was just using “command” and “shell”  modules.

Advantages of Best Practices
  • Completing the task using Full Functionality.
  • Vandalized Architecture helps to create Ansible roles as Utilities which can be used further using different values.
  • Applying best practices helps you to learn new things every day.
  • Following “Convention Over Configuration” makes your troubleshooting much easier.
  • Helps you to grow your Automation skills.
  • You don’t have to worry about the latest version or change in values ever.

I can talk about the Advantages of best practices continuously but you should understand it after using them. So now, Let’s talk about “How to apply them”.


First, we will understand the complete directory structure on Ansible Role:
  • Defaults: The default variables for the role are been stored here inside this directory. These variables have the lowest priority.
  • Files: All the static files are being stored here which are used inside the role.
  • Handlers: All the handlers are being used here not inside the Task directory. And automatically called upon from here.
  • Meta: This directory contains the metadata about your role regarding the dependencies which are being required to run this role in any system, so it will not be run until the dependencies inside it are not been resolved.
  • Tasks: This directory contains the main list of the tasks which needs to be executed by the role.
  • Vars: This directory has high precedence than defaults directory and can only be overwritten by passing them On the command line, In the specific task or In a block.
  • Templates:This directory contains the Jinja to template inside this. Basically, all the dynamic files are being stored here which can be variablized.


Whitespace and Comments

Generous use of whitespace and breaking things up is really appreciated. One very important thing is the use of comments inside your roles so that someone using your role in future could be able to easily understand it properly.


YAML format

Learn YAML format properly and use of indentation properly inside the document. Sometimes, when running the role gives the error for Invalid Syntax due to bad indentation format. And writing in proper Indentation makes your role look beautiful.



Always Name Tasks

It is possible to leave off the ‘name’ for a given task, though it is recommended to provide a description about something is being done instead. This name is shown when that particular task is being run.



Version Control
Use version control. Keep your roles and inventory files in git and commit when you make changes to them. This way you have an audit trail describing when and why you changed the rules that are automating your infrastructure.



Variable and Vaults
Since the variable contains sensitive data, so It is often easier to find variables using grep or similar tools inside the Ansible system. Since vaults obscure these variables, It is best to work with a layer of Indirection. This allows Ansible to find the variables inside the unencrypted file and all sensitive variables come from an encrypted file.
The best approach to perform is to start with a group_vars subdirectory containing two more subdirectories inside it naming “Vars” and “Vaults”. Inside “Vars”  directory define all the variable including sensitive variables also. Now, copy those sensitive variables inside “Vault” directory while using the prefix “vault_*” for the variables. Now you should adjust the variables in the “Vars” to point the matching “vault_*” variables using jinja2 syntax and ensure that vault file is vault encrypted.


Roles for multiple OS
Roles should be written in a way that they could be run on multiple Operating systems. Try to make your roles as generic as you can. But if you have created a role for some specific kind of operating system or some specific application, then try to explicitly define that inside the role name.


Single role Single goal
Avoid tasks within a role which are not related to each other. Don’t build a common role. It’s ugly and bad for readability of your role.


Other Tips:
  • Use a module if available
  • Try not to use command or shell module
  • Use the state parameter
  • Prefer scalar variables
  • Set default for every variable
  • If you have multiple roles related to each other than try to create a common variable file for all of them which will be called inside your playbook


  • Use “copy” or “template” module instead of “lineinfile” module
  • Make role fully variablized


  • Be explicit when writing tasks. Suppose, If you are creating a file or directory then rather defining src and destination, try to define owner, group, mode etc.
  • Create a Role which could be used further.
  • Create it using proper modules for better understanding.
  • Do proper comments inside it so that it would be understood by someone else also.
  • Use proper Indentation for the YAML format.
  • Create your Role variables and also secure them using vault.
  • Create Single role for Single goal.