Collections
In this section, you’ll learn the following:
-
Create a Collection
-
Build and Install a Collection
-
Sharing Collections
-
Downloading Collections from Ansible Galaxy
So far, we’ve seen Ansible Roles as a way to implement everyday Ansible tasks and reuse them across playbooks/projects. But in some cases, we might need something more, not just to share tasks between tasks but also modules, plugins, other roles, or even playbooks.
Ansible Collections are a distribution format for Ansible content that can include playbooks, roles, modules, and plugins.
Ansible Collections have two default lookup paths:
- User scoped path
-
/home/<username>/.ansible/collections
- System scoped path
-
/usr/share/ansible/collections
First Collection
In your working directory, create a new directory named my-collection
:
mkdir my-collection
cd my-collection
Then use Ansible Galaxy CLI to scaffold the collection layout.
The collection is named mynamespace.collection
:
ansible-galaxy collection init mynamespace.collection
- Collection mynamespace.collection was created successfully
The structure of a plugin is the following one:
.
└── mynamespace (1)
└── collection (2)
├── README.md
├── docs (3)
├── galaxy.yml (4)
├── meta (5)
│ └── runtime.yml
├── plugins (6)
│ └── README.md
└── roles (7)
1 | Sets a namespace to the collection |
2 | Collection name |
3 | Collections documentation |
4 | Contains all the metadata used in the Ansible Galaxy hub to index the collection |
5 | Additional metadata |
6 | Holds plugins, modules, and module_utils |
7 | Hosts custom roles |
Create an Ansible Module
Let’s create a custom Ansible module that returns a Hello World message with the name
of the person provided as a parameter.
Move into the plugins
directory and create a new directory named modules
:
cd mynamespace/collection/plugins/
mkdir modules
cd modules
Let’s implement the hello module with the Hello World logic. In this case, it’s a Python module, but any other language could be used.
Create a new file, demo_hello.py
in the modules
directory with the following content:
#!/usr/bin/python
ANSIBLE_METADATA = {
'metadata_version': '1.0',
'status': ['preview'],
'supported_by': 'community'
}
DOCUMENTATION = '''
---
module: demo_hello
short_description: A module that says hello
version_added: "2.8"
description:
- "A module that says hello."
options:
name:
description:
- Name of the person to salute. If no value is provided the default
value will be used.
required: false
type: str
default: John Doe
author:
- Gianni Salinetti (@giannisalinetti)
'''
EXAMPLES = '''
# Pass in a custom name
- name: Say hello to Linus Torvalds
demo_hello:
name: "Linus Torvalds"
'''
RETURN = '''
fact:
description: Hello string
type: str
sample: Hello John Doe!
'''
import random
from ansible.module_utils.basic import AnsibleModule
FACTS = "Hello {name}!"
def run_module():
module_args = dict(
name=dict(type='str', default='Ada'),
)
module = AnsibleModule(
argument_spec=module_args,
supports_check_mode=True
)
result = dict(
changed=False,
fact=''
)
result['fact'] = FACTS.format(
name=module.params['name']
)
if module.check_mode:
return result
module.exit_json(**result)
def main():
run_module()
if __name__ == '__main__':
main()
Create a Role
It’s a good practice to provide an Ansible Role when developing a module, so the user of the module is abstracted from the module, and Ansible Collections are about bundling content that belongs together.
So let’s create a role inside the custom collection that utilizes the new module to get the message and print it in the console. We’ve already seen this in Ansible Roles section on how to develop Roles.
Go to my-collection/mynamespace/collection
directory and run the following command to scaffold a Role:
cd ../..
ansible-galaxy init --init-path roles hello_msg
- Role hello_msg was created successfully
At this point, the Role is created in the roles/hello_msg
directory.
Let’s create the task role running the module and printing the output.
Open roles/hello_msg/tasks/main.yml
file and copy the following content:
---
# tasks file for hello_motd
- name: Generate greeting and store result
demo_hello: (1)
name: "{{ friend_name }}" (2)
register: demo_greeting (3)
- name: dump output
debug: (4)
msg: "{{ demo_greeting }}\n"
1 | Sets the name of the module (Python file) |
2 | Sets the name parameter |
3 | Registers the output of the command |
4 | Prints the outout to console |
Finally, set a default value for the name
parameter in roles/hello_msg/defaults/main.yml
:
---
# defaults file for hello_motd
friend_name: "Alexandra"
Build and Install the Collection
From the my-collection/mynamespace/collection
directory run the following command to build the collection and generate a .tar.gz
file that can be installed locally or uploaded to Galaxy:
ansible-galaxy collection build
Created collection for mynamespace.collection at /Users/asotobu/git/ansible-tutorial/apps/my-collection/mynamespace/collection/mynamespace-collection-1.0.0.tar.gz (1)
1 | The output folder will depend on your layout |
Then install the collection into the Ansible collections folder (~/.ansible/collections/ansible_collections
) to use it in the playbooks:
ansible-galaxy collection install mynamespace-collection-1.0.0.tar.gz
Starting galaxy collection install process
Process install dependency map
Starting collection install process
Installing 'mynamespace.collection:1.0.0' to '/Users/asotobu/.ansible/collections/ansible_collections/mynamespace/collection'
mynamespace.collection:1.0.0 was installed successfully (1)
1 | Version is taken from galaxy.yml file |
Test the Collection
Now, it’s time to validate the Collection; to do that, we’ll create a new playbook that uses our new module and role.
Return to the my-collection
directory and create a new directory named collections_test
to generate the test playbook.
mkdir collections_test
cd collections_test
To test the Collection, create a simple playbook.yaml
file with the following content:
---
- hosts: localhost (1)
connection: local
tasks:
- import_role: (2)
name: mynamespace.collection.hello_msg
vars:
friend_name: "Alexandra" (3)
1 | Sets localhost host so no inventory required |
2 | Imports the hello_msg Role defined in the collection |
3 | Override the variable value to Alexandra |
The playbook runs the hello_msg Role, which invokes the module we’ve developed in Python.
Go to the terminal and run the following command:
ansible-playbook playbook.yaml
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
PLAY [localhost] *****************************************************************************************************************************************************************
TASK [Gathering Facts] ***********************************************************************************************************************************************************
ok: [localhost]
TASK [mynamespace.collection.hello_msg : Generate greeting and store result] *****************************************************************************************************
ok: [localhost]
TASK [mynamespace.collection.hello_msg : dump output] ****************************************************************************************************************************
ok: [localhost] => { (1)
"msg": {
"changed": false,
"fact": "Hello Alexandra!",
"failed": false
}
}
PLAY RECAP ***********************************************************************************************************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
1 | The output from the module |
Share Collections
So far, we’ve seen Ansible Collections enable the creation of modules, roles, and playbooks and package them in a single artifact. But how to share Collections between different projects/teams?
There are several ways to do this, like Ansible Galaxy or Ansible Tower, but the easiest way is using a well-known old friend named Git.
The way is pretty similar as we’ve seen in Sharing Roles, but the commands are slightly different:
To install a remote Collection from the Git repository using the ansible-galaxy
tool:
ansible-galaxy collection install git+https://github.com/ansible-collections/amazon.aws.git
Or you can also set them in a requirements.yaml
file:
collections:
- name: https://github.com/ansible-collections/amazon.aws.git
type: git
version: main
And you can install it by running the ansible-galaxy install
command:
ansible-galaxy install -r requirements.yaml
Download Collections from Ansible Galaxy
Ansible Galaxy provides pre-packaged units of work known to Ansible as roles and collections. It’s like the NPM or Maven central but for Ansible.
One of these packages available in Galaxy is the Ansible Posix Collection.
It is an Ansible Collection of modules and plugins that target POSIX UNIX/Linux and derivative Operating Systems.
The collection contains several modules, for example, one for managing firewalld
, another for selinux
, sysctl
, …
In this example, we’ll install Posix Collection and the at
module to schedule a job using the at tool.
Go to the root directory, and create a new directory named atlinux-example
.
This directory should be a sibling of my-collection
:
mkdir atlinux-example
cd atlinux-example
Install the Collection from Galaxy
Let’s install the Collection from the Galaxy server to our local machine.
ansible-galaxy
downloads automatically (if no URL is defined) a collection from Ansible Galaxy.
Run the following command from the terminal:
ansible-galaxy collection install -f ansible.posix
Starting galaxy collection install process
Process install dependency map
Starting collection install process
Downloading https://galaxy.ansible.com/download/ansible-posix-1.5.1.tar.gz to /Users/asotobu/.ansible/tmp/ansible-local-213760u56e6h9/tmp7w0rcl7k/ansible-posix-1.5.1-tysuskbu
Installing 'ansible.posix:1.5.1' to '/Users/asotobu/.ansible/collections/ansible_collections/ansible/posix'
ansible.posix:1.5.1 was installed successfully
Create Ansible Content
To test this Galaxy Collection, create an inventory file and a playbook.
First, create a new inventory
file configuring the container’s location.
It’s the same document we created at Inventory:
[staging]
staging ansible_user=root ansible_host=127.0.0.1 ansible_port=2223 ansible_ssh_private_key_file=~/.ssh/id_rsa_ansible
[production]
production ansible_user=root ansible_host=127.0.0.1 ansible_port=2224 ansible_ssh_private_key_file=~/.ssh/id_rsa_ansible
Then let’s create a atlinux-playbook.yaml
enforcing SELinux in the production host:
---
- name: set at command
hosts: production (1)
become: yes
tasks:
- name: Install at Package (2)
dnf:
name: httpd >= 2.4
state: present
- name: Schedule a command to execute in 1 minute making sure it is unique in the queue
ansible.posix.at: (3)
command: ls -d / > /tmp/ls.txt
count: 10
units: minutes
unique: yes
1 | Applies only to production hosts |
2 | Installs at package |
3 | Uses the fully qualified name <author>.<collection>.<module> |
Finally, run the playbook to schedule a job:
ansible-playbook -i inventory atlinux-playbook.yaml
[WARNING]: Found both group and host with same name: production
[WARNING]: Found both group and host with same name: staging
PLAY [set at command] ************************************************************************************************************************************************************
TASK [Gathering Facts] ***********************************************************************************************************************************************************
ok: [production]
TASK [Install at Package] ********************************************************************************************************************************************************
ok: [production]
TASK [Schedule a command to execute in 1 minute making sure it is unique in the queue] *******************************************************************************************
ok: [production]
PLAY RECAP ***********************************************************************************************************************************************************************
production : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Done. You’ve used Ansible to schedule a Job on a remote machine.