
Let's create a Python virtual environment named poc.
python3 -m venv poc
Let's activate the Python virtual environment.
source poc/bin/activate
Let's upgrade pip in the Python virtual environment and install molecule.
pip install --upgrade pip
pip install molecule
The pip list command should now show that the molecule CLI is installed in the Python virtual environment.
~]$ pip list
Package Version
------------------------- ---------
ansible-compat 25.1.5
ansible-core 2.18.4
attrs 25.3.0
bracex 2.5.post1
cffi 1.17.1
click 8.1.8
click-help-colors 0.9.4
cryptography 44.0.2
enrich 1.2.7
Jinja2 3.1.6
jsonschema 4.23.0
jsonschema-specifications 2024.10.1
markdown-it-py 3.0.0
MarkupSafe 3.0.2
mdurl 0.1.2
molecule 25.4.0
packaging 24.2
pip 25.0.1
pluggy 1.5.0
pycparser 2.22
Pygments 2.19.1
PyYAML 6.0.2
referencing 0.36.2
resolvelib 1.0.1
rich 14.0.0
rpds-py 0.24.0
subprocess-tee 0.4.2
typing_extensions 4.13.1
wcmatch 10.0
Let's ensure the molecule CLI is working.
~]$ molecule --version
molecule 25.4.0 using python 3.12
ansible:2.18.4
default:25.4.0 from molecule
Let's create a new collection called foo.bar.
cd poc/
ansible-galaxy collection init foo.bar
The foo/bar directory should contain some files and directories.
]$ ls -l foo/bar/
total 8
drwxr-xr-x. 2 john.doe users 6 Apr 3 23:20 docs
-rw-r--r--. 1 john.doe users 3089 Apr 3 23:20 galaxy.yml
drwxr-xr-x. 2 john.doe users 25 Apr 3 23:20 meta
drwxr-xr-x. 2 john.doe users 23 Apr 3 23:20 plugins
-rw-r--r--. 1 john.doe users 66 Apr 3 23:20 README.md
drwxr-xr-x. 2 john.doe users 6 Apr 3 23:20 roles
Let's create a role named my_role.
~]$ cd foo/bar/roles/
~]$ ansible-galaxy role init my_role
- Role my_role was created successfully
Let's update main.yml in my_role to contain something simple.
]$ cat my_role/tasks/main.yml
---
- ansible.builtin.debug:
msg: "Hello World"
Let's create a playbook that will include my_role in the foo/bar directory.
]$ cat my_playbook.yml
---
- hosts: localhost
gather_facts: false
tasks:
- name: include_role my_role main.yml
ansible.builtin.include_role:
name: my_role
tasks_from: main.yml
...
The foo/bar directory should now include my_playbook.yml.
]$ ls -l foo/bar/
drwxr-xr-x. 2 john.doe users 6 Apr 3 23:20 docs
-rw-r--r--. 1 john.doe users 3089 Apr 3 23:20 galaxy.yml
drwxr-xr-x. 2 john.doe users 25 Apr 3 23:20 meta
-rw-r--r--. 1 john.doe users 187 Apr 3 23:26 my_playbook.yml
drwxr-xr-x. 2 john.doe users 23 Apr 3 23:20 plugins
-rw-r--r--. 1 john.doe users 66 Apr 3 23:20 README.md
drwxr-xr-x. 3 john.doe users 21 Apr 3 23:22 roles
Running the playbook should return something like this.
]$ ansible-playbook my_playbook.yml
PLAY [localhost]
TASK [include_role my_role main.yml]
included: my_role for localhost
TASK [my_role : ansible.builtin.debug]
ok: [localhost] => {
"msg": "Hello World"
}
PLAY RECAP
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Let's create a directory named extensions in the foo/bar directory.
mkdir extensions
The foo/bar directory should now include the extensions directory.
]$ ll foo/bar/
total 12
drwxr-xr-x. 2 john.doe users 6 Apr 3 23:20 docs
drwxr-xr-x. 3 john.doe users 22 Apr 3 23:28 extensions
-rw-r--r--. 1 john.doe users 3089 Apr 3 23:20 galaxy.yml
drwxr-xr-x. 2 john.doe users 25 Apr 3 23:20 meta
-rw-r--r--. 1 john.doe users 187 Apr 3 23:26 my_playbook.yml
drwxr-xr-x. 2 john.doe users 23 Apr 3 23:20 plugins
-rw-r--r--. 1 john.doe users 66 Apr 3 23:20 README.md
drwxr-xr-x. 3 john.doe users 21 Apr 3 23:22 roles
Let's move into the extensions directory and run the molecule init scenario command. Something like this should be returned.
]$ cd extensions
]$ molecule init scenario
INFO Initializing new scenario default...
PLAY [Create a new molecule scenario] ******************************************
TASK [Check if destination folder exists] **************************************
changed: [localhost]
TASK [Check if destination folder is empty] ************************************
ok: [localhost]
TASK [Fail if destination folder is not empty] *********************************
skipping: [localhost]
TASK [Expand templates] ********************************************************
changed: [localhost] => (item=molecule/default/converge.yml)
changed: [localhost] => (item=molecule/default/create.yml)
changed: [localhost] => (item=molecule/default/destroy.yml)
changed: [localhost] => (item=molecule/default/molecule.yml)
PLAY RECAP *********************************************************************
localhost : ok=3 changed=2 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
The extensions/molecule directory should now contain a few YAML files.
]$ ls -l foo/bar/extensions/molecule/default/
-rw-r--r--. 1 john.doe users 203 Apr 3 23:28 converge.yml
-rw-r--r--. 1 john.doe users 1182 Apr 3 23:28 create.yml
-rw-r--r--. 1 john.doe users 622 Apr 3 23:28 destroy.yml
-rw-r--r--. 1 john.doe users 206 Apr 3 23:28 molecule.yml
Let's update the converge.yml file to test the main.yml file in my_role.
---
- name: converge
hosts: localhost
gather_facts: false
tasks:
- name: converge my_role main.yml
ansible.builtin.include_role:
name: my_role
tasks_from: main.yml
Let's create the ANSIBLE_ROLES_PATH environment variable to point the the roles directory that contains my_role.
export ANSIBLE_ROLES_PATH=/home/john.doe/poc/foo/bar/roles/
From the extensions directory, you can now run molecule commands. Let's run the molecule converge command which will invoke the converge.yml playbook. In this example, since there are no issues in main.yml in my_role, converge did not raise any issues. Cool.
]$ molecule converge
INFO Running default > converge
PLAY [converge] ****************************************************************
TASK [converge my_role main.yml] ***********************************************
included: my_role for localhost
TASK [my_role : ansible.builtin.debug] *****************************************
ok: [localhost] => {
"msg": "Hello World"
}
PLAY RECAP *********************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Did you find this article helpful?
If so, consider buying me a coffee over at