Ansible - Collections 2/2 (developing)

In the last part of "Ansible - Collection", I talked the reason and usage of collections. Now, let's see how you can create your own collection. Believe me or not, this is easier than creating a bunch of roles and playbooks like it was done in the past.

Ansible - Collections 2/2 (developing)
Photo by Todd Quackenbush / Unsplash

In the last part of "Ansible - Collection", I talked the reason and usage of collections. Now, let's see how you can create your own collection. Believe me or not, this is easier than creating a bunch of roles and playbooks like it was done in the past.

Ansible

Ansible is a super simple automation framework. It can be used for infrastructure-as-code, configuration management, deployments on Kubernetes and much more. For me, it is the Swiss Army Knife of automation.

Let's build a collection

Most collections will contain plugins and modules. But you can use them to keep your roles and playbooks, too. Let's take a look at this scenario.

Initialize

First, we need to initialize a directory to hold our desired content. This can be done with the ansible-galaxy command, too.

$ ansible-galaxy collection init mynamespace.mycollection
- Collection mynamespace.mycollection was created successfully

the generated content will look like the below:

$ tree mynamespace/
mynamespace/
└── mycollection
    ├── docs
    ├── galaxy.yml
    ├── meta
    │   └── runtime.yml
    ├── plugins
    │   └── README.md
    ├── README.md
    └── roles

As you can see, this command produces some directories and files. The structure reflects the naming of your collection. In our case, the collection will be named "mynamespace.mycollection" and the content can be called by "mynamespace.mycollection.ROLE/PLAYBOOK/MODULE".

Metafiles

To ensure that you can publish your collection to the Ansible Galaxy and Ansible knows how to handle the collection, you should fill in the meta files, first.

The meta/runtime.yml is super easy. Just add your desired minimum Ansible version.

---
# meta/runtime.yml

requires_ansible: '>=2.11.0'

Next, you should update the galaxy.yml file. The file is excessively well documented, and you just need to fill in the placeholders.

### REQUIRED
# The namespace of the collection. This can be a company/brand/organization or product namespace under which all
# content lives. May only contain alphanumeric lowercase characters and underscores. Namespaces cannot start with
# underscores or numbers and cannot contain consecutive underscores
namespace: mynamespace

# The name of the collection. Has the same character restrictions as 'namespace'
name: mycollection

# The version of the collection. Must be compatible with semantic versioning
version: 1.0.0

# The path to the Markdown (.md) readme file. This path is relative to the root of the collection
readme: README.md

# A list of the collection's content authors. Can be just the name or in the format 'Full Name <email> (url)
# @nicks:irc/im.site#channel'
authors:
- your name <example@domain.com>

...SNIP...

Adding roles

Next, let's add a super simple role to our collection. For now, installing git is fine. I will keep the snippets simple, since we already talked about roles in the "Ansible - Roles" articles about this in more details.

Let's start with a directory layout like below:

$ tree mynamespace/
mynamespace/
└── mycollection
    ├── docs
    ├── galaxy.yml
    ├── meta
    │   └── runtime.yml
    ├── plugins
    │   └── README.md
    ├── README.md
    └── roles
        └── git              # This is new
            └── defaults
                └── main.yml
            └── tasks
                └── main.yml

As you can see, we need to fill in two file. Let's define our defaults first.

---
# roles/git/defaults/main.yml

git_package_names: "git-core"
git_package_state: "present"
...
roles/git/defaults/main.yml

This will ensure that we can adjust the desired package names later on. Next, we want to add the tasks to take care of the installation.

---
# roles/git/tasks/main.yml

- name: "Manage Git Packages"
  ansible.builtin.package:
    name: "{{ git_package_names }}"
    state: "{{ git_package_state }}"
  become: true
  tags:
    - "git"
    - "package"
...
roles/git/tasks/main.yml

That's already it. Our role is present. We can reference it (if the collection is installed) by its name "mynamespace.mycollection.git". We will also do this in the next step.

Adding playbooks

Before continuing with the build process, we might also add a playbook to the collection. This makes it super easy to ship your playbooks. You might even add your roles in this playbook.

First, let's add the playbook to the directory layout.

$ tree mynamespace/
mynamespace/
└── mycollection
    ├── docs
    ├── galaxy.yml
    ├── meta
    │   └── runtime.yml
    ├── playbooks
    │   └── development_tools.yml       # This is new
    ├── plugins
    │   └── README.md
    ├── README.md
    └── roles
        └── git
            └── defaults
                └── main.yml
            └── tasks
                └── main.yml

Now, let's fill in the playbook.

---
# playbooks/development_tools.yml

- name: "Manage development tools"
  hosts: "{{ target | default('all') }}"
  
  tasks:
  
    - name: "Import Git Role"
      ansible.builtin.import_role:
        name: "mynamespace.mycollection.git"

As you can see, we referenced the above created role by its name in the collection. We also made hosts: entry a bit more variable, so the user can adapt it without touching the playbook itself.

Packaging the collection

After adjusting the meta-files and adding our role and playbook, we also want to package our collection. This is super easy, too. Just run:

# Package it
$ ansible-galaxy collection build /path/to/your/collection

# Find the tar ball
$ ls -la

That's already it. The package will be named to the details in your galaxy.yml file. If you change the version there, the package will be named accordingly.

Installing the collection

And if you want to install it? This is as simple as:

# Install it
$ ansible-galaxy collection install /path/to/your/colection-bundle.tar.gz

This will place the content in some predefined paths, so Ansible can easily discover them. One might say, it works similar to the npm or pip commands, but for Ansible collections.

For regular users, you will likely find the collections in the ~/.ansible/collections/ path. Feel free to take a look at them.

Next Steps

This should be enough for now, but how to continue?

Well, there are many options.

  • write integration tests for your roles and modules
  • add modules (there will be an article very soon)
  • publish your collection to the Ansible Galaxy

Lastly, the usual link section. There are also two public collections from me.

overview/README.rst at main · ansible-collections/overview
Collections overview, how to request a namespace. Contribute to ansible-collections/overview development by creating an account on GitHub.
Using Ansible collections — Ansible Documentation
Developing collections — Ansible Documentation
GitHub - whiletruedoio/whiletruedoio.desktop: Ansible collection for desktop workstation and notebook plugins, roles and playbooks.
Ansible collection for desktop workstation and notebook plugins, roles and playbooks. - GitHub - whiletruedoio/whiletruedoio.desktop: Ansible collection for desktop workstation and notebook plugins…
GitHub - whiletruedoio/whiletruedoio.container: Ansible collection to provide container setups and prepared container deployments.
Ansible collection to provide container setups and prepared container deployments. - GitHub - whiletruedoio/whiletruedoio.container: Ansible collection to provide container setups and prepared cont…

Conclusion

As you can see, it is super easy to develop your own collection, bundle it to install this bundle. I would love to know if you already work on a collection or even published one.