Chapter 8: Simplifying Playbooks with Roles and Collections
Getting roles and modules from collections
Every module name you have typed so far has three parts, such as ansible.posix.firewalld. The first two name a collection: a versioned package of modules, roles and plug-ins. Collections are how almost all Ansible content is distributed, so installing and pinning them is an everyday skill.
- Read a fully qualified collection name and say where its content lives.
- Install collections for a project, from a requirements file, with pinned versions.
- Check which collections Ansible can see, and read a module's documentation.
- Recognise and fix the two common errors: a missing collection and a missing Python library.
What a collection is
ansible-core itself contains only the essentials, in the built-in collection ansible.builtin. Everything else (firewalld, LVM, SELinux tools, cloud providers, network devices) lives in separate collections, each released on its own schedule by whoever maintains it.
A collection can contain modules, roles, plug-ins such as filters, and documentation. You refer to anything inside it by its fully qualified collection name, the FQCN:
Using the full name in every task means a playbook says exactly which module it wants, and keeps working when two collections each have a module called, say, user.
The first part of the name, the namespace, tells you who maintains it: community.* collections (community.general, community.crypto, community.mysql) come from the Ansible community, redhat.* (redhat.rhel_system_roles, redhat.insights, redhat.satellite) from Red Hat, and vendors publish their own (cisco.ios, amazon.aws). Splitting content this way lets each vendor release updates on its own schedule, and lets you install only what a project needs, at the versions it needs.
Where collections come from
| Source | What it offers |
|---|---|
| Automation hub (console.redhat.com) | Red Hat Certified collections, supported by Red Hat and its partners; needs a subscription |
| Private automation hub | An organisation's own server, with its approved collections and its own |
| Ansible Galaxy (galaxy.ansible.com) | Community collections, without support |
| A Git repository or an archive | A collection built and published by anyone |
| The execution environment | Collections already inside the container image, such as ansible.builtin and a supported set |
Which collections can Ansible see?
[student@workstation project]$ ansible-galaxy collection list
# /usr/share/ansible/collections/ansible_collections
Collection Version
------------------------ -------
ansible.posix 1.5.4
community.general 9.5.13
redhat.rhel_system_roles 1.120.5Ansible searches the directories of the collections_path setting in order, and the first copy of a collection it finds wins. A project usually puts its own directory first:
[defaults]
collections_path = ./collections:~/.ansible/collections:/usr/share/ansible/collectionsRemember from chapter 1 that this setting replaces the default list. Leave the system directory off and the collections installed there disappear from view.
In a classroom that uses an execution environment, most collections come inside the container image. ansible-navigator collections lists what the image contains.
The home lab runs without an execution environment, so every collection is a directory on workstation. Section 1.6 put ansible.posix and community.general in /usr/share/ansible/collections; a project's own go in ./collections.
Installing collections
ansible-galaxy collection install downloads a collection and its dependencies. -p collections installs into the project.
[student@workstation project]$ ansible-galaxy collection install community.crypto:2.26.0 -p collectionsThe source can be:
| Source | Example |
|---|---|
| A name on Ansible Galaxy, with a version | community.crypto:2.26.0 |
| An archive on disk | ./community-crypto-2.26.0.tar.gz |
| An archive at a URL | http://utility.lab.example.com/files/ansible-posix-1.5.4.tar.gz |
| A Git repository | git+https://git.example.com/ops/acme.tools.git,v1.0 |
A Git repository must contain a galaxy.yml or a MANIFEST.json at its top level, which is how ansible-galaxy recognises it as a collection.
Pin the version
A collection declares which versions of ansible-core it supports. The newest release of a collection often needs a newer ansible-core than RHEL 9 provides, and Ansible then refuses to load it. Decide on a version that supports your ansible-core, and write it down.
The requirements file
As with roles, record what a project needs. For collections the list sits under a collections: key:
---
collections:
- name: community.crypto
version: 2.26.0
- name: http://utility.lab.example.com/files/ansible-posix-1.5.4.tar.gz[student@workstation project]$ ansible-galaxy collection install -r collections/requirements.yml -p collections
...output omitted...
community.crypto:2.26.0 was installed successfullyOne file may hold both a roles: and a collections: list. ansible-galaxy collection install -r installs the collections from it; ansible-galaxy role install -r installs the roles.
Automation controller installs collections/requirements.yml for you before each run, just as it does for roles.
Other content servers
By default ansible-galaxy downloads from the public Ansible Galaxy site. To use automation hub, a private automation hub, or several servers in order, list them in a [galaxy] section of ansible.cfg, with one section per server:
[galaxy]
server_list = automation_hub, galaxy
[galaxy_server.automation_hub]
url=https://console.redhat.com/api/automation-hub/
auth_url=https://sso.redhat.com/auth/realms/redhat-external/protocol/openid-connect/token
token=eyJh...Jf0o
[galaxy_server.galaxy]
url=https://galaxy.ansible.com/ansible-galaxy tries the servers in the order of server_list. The token for automation hub comes from its web console. A token is a password: rather than committing it in ansible.cfg, set it in the environment, with a variable named after the server:
[student@workstation project]$ export ANSIBLE_GALAXY_SERVER_AUTOMATION_HUB_TOKEN='eyJh...Jf0o'Using what you installed
Nothing else is needed: use the full name in a task.
- name: The private key exists
community.crypto.openssl_privatekey:
path: /etc/pki/tls/private/site.key
size: 2048A role from a collection is named the same way, in roles: or with include_role:
roles:
- redhat.rhel_system_roles.timesyncDocumentation works for any collection Ansible can see:
[student@workstation project]$ ansible-navigator doc community.crypto.openssl_privatekey -m stdout
[student@workstation project]$ ansible-navigator collectionsansible-navigator collections, in its interactive mode, lists every collection it can find, including the project's own collections/ directory. Type a collection's number (:17) to see its modules and roles, and a module's number to read its documentation.
Two errors you will meet
ERROR! couldn't resolve module/action 'community.crypto.openssl_privatekey'. This often indicates a misspelling, missing collection, or incorrect module path.
Check the spelling, then ansible-galaxy collection list. If the collection is installed but not listed, collections_path does not include its directory.
Cannot detect the required Python library cryptography (>= 1.2.3)
The collection is fine. The module runs on the managed host and needs a library there. The module's documentation lists its requirements; install the package in an earlier task.
The first stops the playbook before it starts. The second fails one task on one host, which is a useful clue to which side the problem is on.
- In `community.general.parted`, what is `general`?
- Which collection never needs installing?
- Where does `ansible-galaxy collection install -p collections NAME` put the collection?
- A playbook fails at once with "couldn’t resolve module/action". Where is the problem?
- Why write a version in `collections/requirements.yml`?
Takeaway
Read a fully qualified collection name and say where its content lives. Use the chapter lab to check this on a real host.