Ansible roles are a powerful organizational mechanism that allows you to structure your Ansible content in a modular and reusable way. They provide a structured approach to managing tasks, variables, files, and other artifacts within your playbooks. You can push your Ansible roles in SCM and follow the changes and different versions. Ansible roles are the industry standard way to distribute and share Ansible code with the community.
Creating and using roles will have the following advantages:
- Modularity: Roles break down complex playbooks into smaller, manageable components. Each role encapsulates a specific functionality or purpose, making it easier to maintain and understand your automation code base.
- Reusability: Once you create a role, you can reuse it across multiple playbooks. This promotes consistency and reduces duplication. Imagine having a well-defined “web server” role that you can apply to various projects without rewriting the same tasks repeatedly.
- Collaboration: Roles can be shared with other users via Ansible Galaxy, a community repository for Ansible content. By contributing roles or leveraging existing ones, you tap into a wealth of community knowledge and best practices.
The role directory structure
An Ansible role must have a defined directory structure, which includes the following key directories:
tasks/: Contains the main list of tasks executed by the role. You can split tasks into smaller files if needed.handlers/: Defines handlers that respond to specific events triggered during playbook execution.templates/: Stores the template files (usually ending in.j2) used with thetemplateresource.files/: Houses files deployed by the role.vars/: Holds variables associated with the role.defaults/: Contains default variables (with lower priority) that can be overridden.meta/: Includes metadata about the role, such as dependencies and maintainer.
Creating roles by hand
To start with Ansible roles initially, create the following files and directories:
common/
├── defaults
│ └── main.yml
├── files
├── handlers
│ └── main.yml
├── meta
│ └── main.yml
├── tasks
│ └── main.yml
├── templates
└── vars
└── main.yml
Later in the course you will learn how to create roles with Ansible’s ansible-galaxy command. For this time, use mkdir and touch (or your preferred equivalents) to create files and directories.
Using roles in playbooks
- Defining roles in playbooks:
- Specify roles in your playbook using the
roleskeyword. - Roles can be either statically imported (using
import_role) or dynamically included (usinginclude_role). You will read about it later here.
- Specify roles in your playbook using the
- Role dependencies:
- Roles can depend on other roles. Define dependencies in the
meta/main.ymlfile.
- Roles can depend on other roles. Define dependencies in the
- Running roles multiple times:
- You can execute a role multiple times within a single playbook.
- Use different parameters or set
allow_duplicates: true.
Ansible’s import_role and include_role are both used to reuse roles in Ansible playbooks, but they differ in how they are processed and executed.
import_role is a static import, which means it is pre-processed at the time playbooks are parsed. This means that all the tasks in the imported role are added to the playbook at the point of import, and the role is executed in its entirety before moving on to the next task. This can be useful for roles that are always needed in a playbook, as it ensures that they are executed before any other tasks.
On the other hand, include_role is a dynamic import, which means it is processed as it is encountered during the execution of the playbook. This allows for more flexibility, as it allows for tasks to be executed before or after the role, and it also allows for looping and other dynamic behavior.
In summary, import_role is best used for roles that are always needed in a playbook and should be executed before other tasks, while include_role is best used for roles that need more flexibility and dynamic behavior.
import_role and include_role also differ in terms of variable precedence. When using import_role, variables defined in the role’s vars/ directory have higher precedence than variables defined in the playbook or group variables. However, when using include_role, variables defined in the playbook or group variables have higher precedence than variables defined in the role’s vars/ directory.
In addition, import_role and include_role also differ in terms of how they handle variables passed to them. When using import_role, variables are passed to the role at the time the playbook is parsed, while when using include_role, variables are passed to the role at the time the role is encountered during execution.
In conclusion, while both import_role and include_role are used to reuse roles in Ansible playbooks, they differ in how they are processed, executed, and how they handle variables. It’s important to understand these differences in order to choose the right one for a given scenario.
The following playbook snippet will import the common role with the roles keyword.
---
- name: A playbook that loads a role
hosts: all
roles:
- common
Running the playbook will load the the role from the same directory as the playbook file.
It is possible to import multiple roles as a list with the role keyword.
---
- name: A playbook that loads more roles
hosts: all
roles:
- common
- other
The Ansible recommended project directory structure
Ansible has a recommended directory layout for the maximum efficiency. It is well documented in the official documentation, but let’s mention it here too.
I use the text of the original docs here:
production # inventory file for production servers
staging # inventory file for staging environment
group_vars/
group1.yml # here we assign variables to particular groups
group2.yml
host_vars/
hostname1.yml # here we assign variables to particular systems
hostname2.yml
library/ # if any custom modules, put them here (optional)
module_utils/ # if any custom module_utils to support modules, put them here (optional)
filter_plugins/ # if any custom filter plugins, put them here (optional)
site.yml # main playbook
webservers.yml # playbook for webserver tier
dbservers.yml # playbook for dbserver tier
tasks/ # task files included from playbooks
webservers-extra.yml # <-- avoids confusing playbook with task files
In this lesson you learned the importance of separating the code of different operations into individual roles, then import them into your playbooks. It is just the tip of the iceberg with using roles, but as a beginner you can achieve serious results with this knowledge.
If you want to discuss the topic with other technology-minded people, join my Discord: https://discord.gg/YbSYGsQYES
Now we have an IRC channel as well: irc.libera.chat / #tomsitcafe