Ansible Galaxy: Collections and Roles¶
In this chapter you will learn how to use, install, and manage Ansible roles and collections.
Objectives: In this chapter you will learn how to:
install and manage collections.
install and manage roles.
ansible, ansible-galaxy, roles, collections
Knowledge:
Complexity:
Reading time: 40 minutes
Ansible Galaxy provides Ansible Roles and Collections from the Ansible Community.
The elements provided can be referenced in the playbooks and used out of the box
ansible-galaxy
command¶
The ansible-galaxy
command manages roles and collections using galaxy.ansible.com.
- To manage roles:
ansible-galaxy role [import|init|install|login|remove|...]
Sub-commands | Functionality |
---|---|
install |
installs a role. |
remove |
remove one or more roles. |
list |
display the name and the version of installed roles. |
info |
display information about a role. |
init |
generate a skeleton of a new role. |
import |
import a role from the galaxy web site. Requires a login. |
- To manage collections:
ansible-galaxy collection [import|init|install|login|remove|...]
Sub-commands | Functionality |
---|---|
init |
generate a skeleton of a new collection. |
install |
installs a collection. |
list |
display the name and the version of installed collections. |
Ansible Roles¶
An Ansible role is a unit that promotes the reusability of playbooks.
Note
More information can be found here
Installing useful Roles¶
In order to highlight the interest of using roles, I suggest you to use the alemorvan/patchmanagement
role, which will allow you to perform a lot of tasks (pre-update or post-update for example) during your update process, in only a few lines of code.
You can check the code in the github repo of the role here.
- Install the role. This needs only one command:
ansible-galaxy role install alemorvan.patchmanagement
- Create a playbook to include the role:
- name: Start a Patch Management
hosts: ansible_clients
vars:
pm_before_update_tasks_file: custom_tasks/pm_before_update_tasks_file.yml
pm_after_update_tasks_file: custom_tasks/pm_after_update_tasks_file.yml
tasks:
- name: "Include patchmanagement"
include_role:
name: "alemorvan.patchmanagement"
With this role, you can add your own tasks for all your inventory or for only your targeted node.
Let's create tasks that will be run before and after the update process:
- Create the
custom_tasks
folder:
mkdir custom_tasks
- Create the
custom_tasks/pm_before_update_tasks_file.yml
(feel free to change the name and the content of this file)
---
- name: sample task before the update process
debug:
msg: "This is a sample tasks, feel free to add your own test task"
- Create the
custom_tasks/pm_after_update_tasks_file.yml
(feel free to change the name and the content of this file)
---
- name: sample task after the update process
debug:
msg: "This is a sample tasks, feel free to add your own test task"
And launch your first Patch Management:
ansible-playbook patchmanagement.yml
PLAY [Start a Patch Management] *************************************************************************
TASK [Gathering Facts] **********************************************************************************
ok: [192.168.1.11]
TASK [Include patchmanagement] **************************************************************************
TASK [alemorvan.patchmanagement : MAIN | Linux Patch Management Job] ************************************
ok: [192.168.1.11] => {
"msg": "Start 192 patch management"
}
...
TASK [alemorvan.patchmanagement : sample task before the update process] ********************************
ok: [192.168.1.11] => {
"msg": "This is a sample tasks, feel free to add your own test task"
}
...
TASK [alemorvan.patchmanagement : MAIN | We can now patch] **********************************************
included: /home/ansible/.ansible/roles/alemorvan.patchmanagement/tasks/patch.yml for 192.168.1.11
TASK [alemorvan.patchmanagement : PATCH | Tasks depends on distribution] ********************************
ok: [192.168.1.11] => {
"ansible_distribution": "Rocky"
}
TASK [alemorvan.patchmanagement : PATCH | Include tasks for CentOS & RedHat tasks] **********************
included: /home/ansible/.ansible/roles/alemorvan.patchmanagement/tasks/linux_tasks/redhat_centos.yml for 192.168.1.11
TASK [alemorvan.patchmanagement : RHEL CENTOS | yum clean all] ******************************************
changed: [192.168.1.11]
TASK [alemorvan.patchmanagement : RHEL CENTOS | Ensure yum-utils is installed] **************************
ok: [192.168.1.11]
TASK [alemorvan.patchmanagement : RHEL CENTOS | Remove old kernels] *************************************
skipping: [192.168.1.11]
TASK [alemorvan.patchmanagement : RHEL CENTOS | Update rpm package with yum] ****************************
ok: [192.168.1.11]
TASK [alemorvan.patchmanagement : PATCH | Inlude tasks for Debian & Ubuntu tasks] ***********************
skipping: [192.168.1.11]
TASK [alemorvan.patchmanagement : MAIN | We can now reboot] *********************************************
included: /home/ansible/.ansible/roles/alemorvan.patchmanagement/tasks/reboot.yml for 192.168.1.11
TASK [alemorvan.patchmanagement : REBOOT | Reboot triggered] ********************************************
ok: [192.168.1.11]
TASK [alemorvan.patchmanagement : REBOOT | Ensure we are not in rescue mode] ****************************
ok: [192.168.1.11]
...
TASK [alemorvan.patchmanagement : FACTS | Insert fact file] *********************************************
ok: [192.168.1.11]
TASK [alemorvan.patchmanagement : FACTS | Save date of last PM] *****************************************
ok: [192.168.1.11]
...
TASK [alemorvan.patchmanagement : sample task after the update process] *********************************
ok: [192.168.1.11] => {
"msg": "This is a sample tasks, feel free to add your own test task"
}
PLAY RECAP **********************************************************************************************
192.168.1.11 : ok=31 changed=1 unreachable=0 failed=0 skipped=4 rescued=0 ignored=0
Pretty easy for such a complex process, isn't it?
This is just one example of what can be done using roles made available by the community. Have a look at galaxy.ansible.com to discover the roles that could be useful for you!
You can also create your own roles for your own needs and publish them on the Internet if you feel like it. This is what we will briefly cover in the next chapter.
Introduction to Role development¶
A role skeleton, serving as a starting point for custom role development, can be generated by the ansible-galaxy
command:
$ ansible-galaxy role init rocky8
- Role rocky8 was created successfully
The command will generate the following tree structure to contain the rocky8
role:
tree rocky8/
rocky8/
├── defaults
│ └── main.yml
├── files
├── handlers
│ └── main.yml
├── meta
│ └── main.yml
├── README.md
├── tasks
│ └── main.yml
├── templates
├── tests
│ ├── inventory
│ └── test.yml
└── vars
└── main.yml
8 directories, 8 files
Roles allow you to do away with the need to include files. There is no need to specify file paths or include
directives in playbooks. You just have to specify a task, and Ansible takes care of the inclusions.
The structure of a role is fairly obvious to understand.
Variables are simply stored either in vars/main.yml
if the variables are not to be overridden, or in default/main.yml
if you want to leave the possibility of overriding the variable content from outside your role.
The handlers, files, and templates needed for your code are stored in handlers/main.yml
, files
and templates
respectively.
All that remains is to define the code for your role's tasks in tasks/main.yml
.
Once all this is working well, you can use this role in your playbooks. You will be able to use your role without worrying about the technical aspect of its tasks, while customizing its operation with variables.
Practical work: create a first simple role¶
Let's implement this with a "go anywhere" role that will create a default user and install software packages. This role can be systematically applied to all your servers.
Variables¶
We will create a rockstar
user on all of our servers. As we don't want this user to be overridden, let's define it in the vars/main.yml
:
---
rocky8_default_group:
name: rockstar
gid: 1100
rocky8_default_user:
name: rockstar
uid: 1100
group: rockstar
We can now use those variables inside our tasks/main.yml
without any inclusion.
---
- name: Create default group
group:
name: "{{ rocky8_default_group.name }}"
gid: "{{ rocky8_default_group.gid }}"
- name: Create default user
user:
name: "{{ rocky8_default_user.name }}"
uid: "{{ rocky8_default_user.uid }}"
group: "{{ rocky8_default_user.group }}"
To test your new role, let's create a test-role.yml
playbook in the same directory as your role:
---
- name: Test my role
hosts: localhost
roles:
- role: rocky8
become: true
become_user: root
and launch it:
ansible-playbook test-role.yml
PLAY [Test my role] ************************************************************************************
TASK [Gathering Facts] *********************************************************************************
ok: [localhost]
TASK [rocky8 : Create default group] *******************************************************************
changed: [localhost]
TASK [rocky8 : Create default user] ********************************************************************
changed: [localhost]
PLAY RECAP *********************************************************************************************
localhost : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Congratulations! You are now able to create great things with a playbook of only a few lines.
Let's see the use of default variables.
Create a list of packages to install by default on your servers and an empty list of packages to uninstall. Edit the defaults/main.yml
files and add those two lists:
rocky8_default_packages:
- tree
- vim
rocky8_remove_packages: []
and use them in your tasks/main.yml
:
- name: Install default packages (can be overridden)
package:
name: "{{ rocky8_default_packages }}"
state: present
- name: "Uninstall default packages (can be overridden) {{ rocky8_remove_packages }}"
package:
name: "{{ rocky8_remove_packages }}"
state: absent
Test your role with the help of the playbook previously created:
ansible-playbook test-role.yml
PLAY [Test my role] ************************************************************************************
TASK [Gathering Facts] *********************************************************************************
ok: [localhost]
TASK [rocky8 : Create default group] *******************************************************************
ok: [localhost]
TASK [rocky8 : Create default user] ********************************************************************
ok: [localhost]
TASK [rocky8 : Install default packages (can be overridden)] ********************************************
ok: [localhost]
TASK [rocky8 : Uninstall default packages (can be overridden) []] ***************************************
ok: [localhost]
PLAY RECAP *********************************************************************************************
localhost : ok=5 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
You can now override the rocky8_remove_packages
in your playbook and uninstall for example cockpit
:
---
- name: Test my role
hosts: localhost
vars:
rocky8_remove_packages:
- cockpit
roles:
- role: rocky8
become: true
become_user: root
ansible-playbook test-role.yml
PLAY [Test my role] ************************************************************************************
TASK [Gathering Facts] *********************************************************************************
ok: [localhost]
TASK [rocky8 : Create default group] *******************************************************************
ok: [localhost]
TASK [rocky8 : Create default user] ********************************************************************
ok: [localhost]
TASK [rocky8 : Install default packages (can be overridden)] ********************************************
ok: [localhost]
TASK [rocky8 : Uninstall default packages (can be overridden) ['cockpit']] ******************************
changed: [localhost]
PLAY RECAP *********************************************************************************************
localhost : ok=5 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Obviously, there is no limit to how much you can improve your role. Imagine that for one of your servers, you need a package that is in the list of those to be uninstalled. You could then, for example, create a new list that can be overridden and then remove from the list of packages to be uninstalled those in the list of specific packages to be installed by using the jinja difference()
filter.
- name: "Uninstall default packages (can be overridden) {{ rocky8_remove_packages }}"
package:
name: "{{ rocky8_remove_packages | difference(rocky8_specifics_packages) }}"
state: absent
Ansible Collections¶
Collections are a distribution format for Ansible content that can include playbooks, roles, modules, and plugins.
Note
More information can be found here
To install or upgrade a collection:
ansible-galaxy collection install namespace.collection [--upgrade]
You can then use the newly installed collection using its namespace and name before the module's name or role's name:
- import_role:
name: namespace.collection.rolename
- namespace.collection.modulename:
option1: value
You can find a collection index here.
Let's install the community.general
collection:
ansible-galaxy collection install community.general
Starting galaxy collection install process
Process install dependency map
Starting collection install process
Downloading https://galaxy.ansible.com/download/community-general-3.3.2.tar.gz to /home/ansible/.ansible/tmp/ansible-local-51384hsuhf3t5/tmpr_c9qrt1/community-general-3.3.2-f4q9u4dg
Installing 'community.general:3.3.2' to '/home/ansible/.ansible/collections/ansible_collections/community/general'
community.general:3.3.2 was installed successfully
We can now use the newly available module yum_versionlock
:
- name: Start a Patch Management
hosts: ansible_clients
become: true
become_user: root
tasks:
- name: Ensure yum-versionlock is installed
package:
name: python3-dnf-plugin-versionlock
state: present
- name: Prevent kernel from being updated
community.general.yum_versionlock:
state: present
name: kernel
register: locks
- name: Display locks
debug:
var: locks.meta.packages
ansible-playbook versionlock.yml
PLAY [Start a Patch Management] *************************************************************************
TASK [Gathering Facts] **********************************************************************************
ok: [192.168.1.11]
TASK [Ensure yum-versionlock is installed] **************************************************************
changed: [192.168.1.11]
TASK [Prevent kernel from being updated] ****************************************************************
changed: [192.168.1.11]
TASK [Display locks] ************************************************************************************
ok: [192.168.1.11] => {
"locks.meta.packages": [
"kernel"
]
}
PLAY RECAP **********************************************************************************************
192.168.1.11 : ok=4 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Creating your own collection¶
As with roles, you are able to create your own collection with the help of the ansible-galaxy
command:
ansible-galaxy collection init rocky8.rockstarcollection
- Collection rocky8.rockstarcollection was created successfully
tree rocky8/rockstarcollection/
rocky8/rockstarcollection/
├── docs
├── galaxy.yml
├── plugins
│ └── README.md
├── README.md
└── roles
You can then store your own plugins or roles inside this new collection.