# Setting up Ansible with Molecule

If you're like me and are trying to learn Ansible, you know how cumbersome it is to test. To test your playbook, you setup a cloud instance, run your ansible playbook, verify everything and then delete the instance either to save money or get a clean slate for your next testing scenario.

However, there is a better way. Ansible comes with its own testing framework called Molecule. To test ansible playbooks, Molecule first creates one or more virtual environments on your system, runs the ansible playbook against them, verifies everything using another ansible playbook and finally destroys the virtual environments.

In this blog, I'm going to share how to setup an ansible development environment with Molecule. This blog is heavily inspired by the [official ansible molecule documentation.](https://ansible.readthedocs.io/projects/molecule/)

#### Requirements:

Python installed on your system.

#### Step 1: Create python virtual environment

a.) In your project directory, create a folder. We'll name it `pyvenv`. Then create the python virtual environment using `python -m venv /path/to/your/directory`

```bash
mkdir pyvenv
python3 -m venv pyvenv
```

b.) Activate the python venv

`source pyvenv/bin/activate`

#### Step 2: Install molecule and dependencies

a.) Install required packages in the `pyvenv` environment

```bash
pip3 install molecule
```

Installing `molecule` will automatically install `ansible` as its a dependency.

b. ) Check everything:

```bash
molecule --version
```

Result:

```plaintext
molecule 24.2.1 using python 3.12
    ansible:2.17.0
    default:24.2.1 from molecule
```

#### Step 3: Create ansible collection and role

In older blogs, you might see the command `molecule init role example-role` to create ansible roles. This command basically executed `ansible-galaxy` command in the background to create a proper directory structure for the role, but later ansible removed it because it was redundant.

a.) Create new collection

**IMPORTANT:** According the official documentation, a new collection must be placed under a `collections/ansible_collections` directory. If your collection is not in this directory structure, `molecule` won't be able to find your collection.

We'll name our collection `my.cloud`

```bash
mkdir collections
cd collections
mkdir ansible_collections
cd ansible_collections
ansible-galaxy collection init my.cloud
```

b.) Create role

```bash
cd my/cloud/roles
ansible-galaxy role init test_role
```

c.) Add the following test task in the `test_role/tasks/main.yml` file:

```yaml
---
- name: Task is running from within the role
  ansible.builtin.debug:
    msg: "This is a task from test_role."
```

#### Step 4: Create a playbook

a.) Create `playbook.yml` file in the collection root i.e. the `cloud` folder

```bash
mkdir playbooks
cd playbooks
touch playbook.yml
```

b.) Edit the `playbook.yml` file and add the following:

```yaml
---
- name: Test new role from within this playbook
  hosts: localhost
  gather_facts: false
  tasks:
    - name: Testing role
      ansible.builtin.include_role:
        name: test_role
        tasks_from: main.yml
```

#### Step 5: Adding molecule to the collection

a.) Create new folder `extensions` in the collection root and initiate molecule

```bash
cd ..
mkdir extensions
cd extensions
molecule init scenario test_scenario
```

Inside the molecule directory a folder called `test_scenario` was created. That’s the name of the scenario we defined. If we don’t specify a scenario name, the directory will be called `default` .

```plaintext
.
└── molecule
    └── test_scenario
        ├── converge.yml
        ├── create.yml
        ├── destroy.yml
        └── molecule.yml
```

Make sure your final directory structure is similar to the one shown below, if not, you might get issues running Molecule.

```plaintext
.
└── collections
    └── ansible_collections
        └── my
            └── cloud
                ├── docs
                ├── extensions
                │   └── molecule
                │       └── test_scenario
                ├── meta
                ├── playbooks
                ├── plugins
                └── roles
                    └── test_role
                        ├── defaults
                        ├── files
                        ├── handlers
                        ├── meta
                        ├── tasks
                        ├── templates
                        ├── tests
                        └── vars
```

#### Step 6: Configuring molecule

a.) Edit the `molecule.yml` to configure docker

```yaml
---
driver:
  name: default
platforms:
  - name: instance
    # you might want to add your own variables here based on what provisioning
    # you are doing like:
    image: ubuntu:latest
```

b.) Update `converge.yml` to test our playbook/role

`converge.yml` is where you tell molecule what to test. Add **one** of the following in the `converge.yml`

To test the whole playbook:

```yaml
---
# To test the whole playbook
- name: Include a playbook from a collection
  ansible.builtin.import_playbook: my.cloud.playbook
```

To test just a role:

```yaml
---
# To test a role
- name: Test new role from within this playbook
  hosts: localhost
  gather_facts: false
  tasks:
    - name: Testing role
      ansible.builtin.include_role:
        name: my.cloud.test_role
        tasks_from: main.yml
```

c.) Run molecule test

`molecule test -s test_scenario`
