Create a role in a playbook's project directory and run it as part of one of the plays in the playbook.
Creating roles in Ansible does not require any special development tools. Creating and using an Ansible Role is a three-step process:
Create the role directory structure.
Define the role content.
Use the role in a playbook.
Ansible looks for roles in a subdirectory called roles in the project directory containing your Ansible Playbook.
Each role has its own directory with a standardized directory structure.
This structure enables you to store roles with the playbook and other supporting files.
For example, the following directory structure contains the files within the layer3_interface role:
[user@host ~]$ tree roles/layer3_interface/
roles/layer3_interface/
├── README.md
├── default
│ └── main.yml
├── meta
│ └── main.yml
├── files
└── tasks
├── ios.yml
├── junos.yml
└── main.yml
4 directories, 6 filesThe README.md file provides a basic human-readable description of the role, documentation, examples of how to use it, and any non-Ansible role requirements.
The defaults subdirectory contains a main.yml file that defines the default values for the variables used in the role.
The meta subdirectory contains a main.yml file that specifies information about the author, license, compatibility, and dependencies for the role.
If a subdirectory exists but is empty, such as files in this example, then it is ignored.
You can omit the subdirectory if the role does not use it.
The main.yml file in the tasks subdirectory contains the role's task definitions.
In this example, the tasks subdirectory also contains specific tasks for Cisco IOS and Juniper Junos managed nodes in the ios.yml and junos.yml files.
You can create all the subdirectories and files needed for a new Ansible Role in VS Code, or by using standard Linux commands.
Alternatively, you can also use the ansible-galaxy command.
Use the following procedure to create a role structure using the ansible-galaxy command:
Change to the roles directory in your project:
[user@host project]$ cd roles
[user@host roles]$Run the ansible-galaxy init command, specifying the role name as an argument to create the role:
[user@host roles]$ ansible-galaxy init vlans_configuration
- Role vlans_configuration was created successfullyThe following screen capture illustrates the directory structure created for the vlans_configuration role as displayed in VS Code:
![]() |
Skeleton for the vlans_configuration Role
After creating the directory structure, you need to write the content of the role.
A good place to start is the task file.
This file contains the main list of tasks that the role runs.ROLENAME/tasks/main.yml
As an example, suppose that you are creating an Ansible Role to configure Layer 3 (L3) interfaces for Cisco IOS and Juniper Junos managed nodes.
You can create the following tasks/main.yml task file within the role:
---
- name: Load the right configuration task
ansible.builtin.include_tasks: "{{ ansible_network_os }}.yml"For Cisco IOS managed nodes, the ansible_network_os variable has the cisco.ios.ios value.
For Juniper Junos managed nodes, the variable has the junipernetworks.junos.junos value.
Depending on the value of that variable, the role configures the L3 interface for Cisco IOS managed nodes or for Juniper Junos managed nodes.
Because the cisco.ios.ios.yml and junipernetworks.junos.junos.yml task files are defined within a role task, the files are retrieved from the role's tasks subdirectory.
You can create the tasks/cisco.ios.ios.yml task file with the following content:
---
- name: Configure L3 interface for Cisco IOS managed node
cisco.ios.ios_l3_interfaces:
config: "{{ l3_interface }}"Similarly, you can create the tasks/junipernetworks.junos.junos.yml task file as follows:
---
- name: Configure L3 interface for Juniper Junos managed node
junipernetworks.junos.junos_l3_interfaces:
config: "{{ l3_interface }}"Notice that you can easily grow your role to any supported platform.
For example, if you want to add Arista EOS managed nodes as another option to configure, then it would be enough to add the tasks/arista.eos.eos.yml task file with the following content:
---
- name: Configure L3 interface for Arista EOS managed node
arista.eos.eos_l3_interfaces:
config: "{{ l3_interface }}"You can find the list of supported platforms at: https://docs.ansible.com/ansible/latest/network/user_guide/platform_index.html.
You can use the defaults/main.yml file to set the default value for the l3_interface variable.
The value defined in the defaults subdirectory can be overwritten by defining the variable in the play that loads the role.
You can define role dependencies in the meta/main.yml file in the role directory hierarchy.
Role dependencies enable a role to include other roles as dependencies.
For example, a role that performs some configuration on your managed nodes might depend on a role that backs up their initial configuration.
A meta/main.yml file might also have a top-level galaxy_info key that has a dictionary of other attributes that specify the author, purpose, license, and metadata such as platforms supported for managed nodes.
The following is a sample meta/main.yml file with dependencies on the node_backups and node_reload roles:
--- galaxy_info: author: Student description: Network managed nodes configuration company: Company, Ltd. license: GPL-2.0 min_ansible_version: 2.1dependencies: -role: node_backup path: backups -role: node_reload
By default, if multiple roles have dependencies on another role, and that role is called by different roles in the play multiple times with the same attributes, then the role only runs the first time it is called.
You can override this behavior by setting the allow_duplicates variable to yes in your role's meta/main.yml file.
You should limit your role's dependencies on other roles. Dependencies make it harder to maintain your role, especially if your role has many complex dependencies.
When the role structure has been created and its content defined, you can use the role in a playbook.
You can use roles in playbooks in several ways.
You can use the roles keyword in a play to run one or more roles before any tasks in the section defined by the tasks keyword.
If you need to run some tasks before running the roles, then you can place those tasks in the section defined by the pre_tasks keyword.
For even more control of the order in which a role runs, use the ansible.builtin.include_role or the ansible.builtin.import_role module.
By using these modules, you can run a role in a specific location in the play.
For example, the following playbook includes the role named layer3_interface, which configures the L3 interface on your managed nodes.
Because the layer3_interface role is defined within a role task, the role is retrieved from the roles subdirectory in the project directory containing your playbook:
---
- name: Configure L3 interface on managed nodes
hosts: all
gather_facts: false
tasks:
- name: Load layer3_interface role
ansible.builtin.include_role:
name: layer3_interfaceA well-written Ansible Role uses default variables to alter the role's behavior to match a related configuration scenario. Roles that use variables are more generic and reusable in various contexts.
Keep in mind that the value of any variable defined in a role's defaults subdirectory is overwritten if the same variable is defined in any of the following places:
In an inventory file, either as a host variable or a group variable
In a YAML file under the group_vars or host_vars directories of a playbook project
As a variable nested in the vars keyword of a play
As a variable when including the role in roles keyword of a play
Suppose you have created a role named banner that configures the login and motd banners on your managed nodes.
The role uses the banner_text and the banner_type variables to specify the text and the type of banner to configure.
You can define these variables in the main.yml file within the defaults or var subdirectories of the role:
--- banner_text: Default message banner_type: login
The following playbook uses the banner role with a different value for the banner_text and banner_type variables:
--- - name: Configure banners on managed nodes hosts: all gather_facts: falsevars: -banner_text: This managed node is managed by Ansible -banner_type: motdroles: -role: banner
If the role variables were defined in the defaults subdirectory, then the banner_text and banner_type variables defined in the playbook replace the variable values.
If the role variables were defined in the var subdirectory, then the banner_text and banner_type variables defined in the playbook do not replace their value.
The following example also shows how to use the banner role with a different value for the banner_text and banner_type role variables:
--- - name: Configure banners on managed nodes hosts: all gather_facts: falseroles: -role: bannerbanner_text: This managed node is managed by Ansiblebanner_type: motd
This time the value specified for the banner_text and banner_type role variables replace the variable reference regardless of being defined in the role's vars or defaults directory.
Variable precedence can be confusing when working with role variables in a play. It is important to understand and take this into account to keep your play from accidentally changing the internal functioning of the role.
Most other variables override a role's default variables: inventory variables, play variables, inline role parameters, and so on.
Fewer variables can override variables defined in a role's vars directory.
Facts, variables loaded with include_vars, registered variables, and role parameters can override these variables.
Inventory variables and play variables cannot.
Variables declared inline as role parameters have high precedence; they can also override variables defined in a role's vars directory.
If a role parameter has the same name as a variable set in play vars section, a role's vars section, or an inventory or playbook variable, the role parameter overrides the other variable.
Ansible Roles enable you to break down playbooks into multiple files, resulting in reusable code. To maximize the effectiveness of newly developed roles, consider implementing the following recommended practices into your role development:
Maintain each role in its own version control repository. Ansible works well with Git-based repositories.
Use variables to configure roles so that you can reuse the role to perform similar tasks in similar circumstances.
Avoid storing sensitive information in a role, such as passwords or SSH keys. Configure role variables that contain sensitive values when called in a play with default values that are not sensitive.
Use the ansible-galaxy init command to create your role structure, and then remove any unnecessary files and directories.
Create and maintain a README.md file and a meta/main.yml file to document the role's purpose, author, and usage.
Keep your role focused on a specific purpose or function. Instead of making one role do many things, write multiple roles.
Reuse roles often.
Resist creating new roles for edge configurations. If an existing role accomplishes most of the required configuration, refactor the existing role to integrate the new configuration scenario.
Use integration and regression testing techniques to ensure that the role provides the required new functionality and does not cause problems for existing playbooks.
A longer unofficial list of good practices to follow when you write a role is available at https://redhat-cop.github.io/automation-good-practices/#_roles_good_practices_for_ansible.