After completing this section, you should be able to 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 requires no special development tools. Creating and using a role is a three step process:
Create the role directory structure.
Define the role content.
Use the role in a playbook.
By default, Ansible looks for roles in a subdirectory called roles in the directory containing your Ansible Playbook. This allows you to store roles with the playbook and other supporting files.
If Ansible cannot find the role there, it looks at the directories specified by the Ansible configuration setting roles_path, in order. This variable contains a colon-separated list of directories to search. The default value of this variable is:
~/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles
This allows you to install roles on your system that are shared by multiple projects. For example, you could have your own roles installed your home directory in the ~/.ansible/roles subdirectory, and the system can have roles installed for all users in the /usr/share/ansible/roles directory.
Each role has its own directory with a standardized directory structure. For example, the following directory structure contains the files that define the motd role.
[user@host ~]$tree roles/roles/ └── motd ├── defaults │ └── main.yml ├── files ├── handlers ├── meta │ └── main.yml ├── README.md ├── tasks │ └── main.yml └── templates └── motd.j2
You can create all the subdirectories and files needed for a new role using standard Linux commands. Alternatively, command line utilities exist to automate the process of new role creation.
The ansible-galaxy command line tool (covered in more detail later in this course) is used to manage Ansible roles, including the creation of new roles. You can run ansible-galaxy init to create the directory structure for a new role. Specify the name of the role as an argument to the command, which creates a subdirectory for the new role in the current working directory.
[user@host playbook-project]$cd roles[user@host roles]$ansible-galaxy init my_new_role- my_new_role was created successfully[user@host roles]$ls my_new_role/defaults files handlers meta README.md tasks templates tests vars
Once you have created the directory structure, you must write the content of the role. A good place to start is the task file, the main list of tasks run by the role.ROLENAME/tasks/main.yml
The following tasks/main.yml file manages the /etc/motd file on managed hosts. It uses the template module to deploy the template named motd.j2 to the managed host. Because the template module is configured within a role task, instead of a playbook task, the motd.j2 template is retrieved from the role's templates subdirectory.
[user@host ~]$cat roles/motd/tasks/main.yml--- # tasks file for motd - name: deliver motd file template: src: motd.j2 dest: /etc/motd owner: root group: root mode: 0444
The following command displays the contents of the motd.j2 template of the motd role. It references Ansible facts and a system_owner variable.
[user@host ~]$cat roles/motd/templates/motd.j2This is the system {{ ansible_facts['hostname'] }}. Today's date is: {{ ansible_facts['date_time']['date'] }}. Only use this system with permission. You can ask {{ system_owner }} for access.
The role defines a default value for the system_owner variable. The defaults/main.yml file in the role's directory structure is where this value is set.
The following defaults/main.yml file sets the system_owner variable to user@host.example.com. This will be the email address that is written in the /etc/motd file of managed hosts that this role is applied to.
[user@host ~]$cat roles/motd/defaults/main.yml--- system_owner: user@host.example.com
Roles allow playbooks to be written modularly. 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.
Sensitive information, such as passwords or SSH keys, should not be stored in the role repository. Sensitive values should be parameterized as variables with default values that are not sensitive. Playbooks that use the role are responsible for defining sensitive variables through Ansible Vault variable files, environment variables, or other ansible-playbook options.
Use ansible-galaxy init to start your role, and then remove any directories and files that you do not need.
Create and maintain README.md and meta/main.yml files to document what your role is for, who wrote it, and how to use it.
Keep your role focused on a specific purpose or function. Instead of making one role do many things, you might write more than one role.
Reuse and refactor roles often. Resist creating new roles for edge configurations. If an existing role accomplishes a majority 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 also does not cause problems for existing playbooks.
Role dependencies allow a role to include other roles as dependencies. For example, a role that defines a documentation server may depend upon another role that installs and configures a web server. Dependencies are defined in the meta/main.yml file in the role directory hierarchy.
The following is a sample meta/main.yml file.
---
dependencies:
- role: apache
port: 8080
- role: postgres
dbname: serverlist
admin_user: felixLimit your role's dependencies on other roles. Dependencies make it harder to maintain your role, especially if it has many complex dependencies.
To access a role, reference it in the roles: section of a play. The following playbook refers to the motd role. Because no variables are specified, the role is applied with its default variable values.
[user@host ~]$cat use-motd-role.yml--- - name: use motd role playbook hosts: remote.example.com remote_user: devops become: true roles: - motd
When the playbook is executed, tasks performed because of a role can be identified by the role name prefix. The following sample output illustrates this with the motd : prefix in the task name:
[user@host ~]$ansible-playbook -i inventory use-motd-role.ymlPLAY [use motd role playbook] ************************************************** TASK [setup] ******************************************************************* ok: [remote.example.com] TASK [motd: deliver motd file] ************************************************ changed: [remote.example.com] PLAY RECAP ********************************************************************* remote.example.com : ok=2 changed=1 unreachable=0 failed=0
The above scenario assumes that the motd role is located in the roles directory. Later in the course you will see how to use a role that is remotely located in a version control repository.
A well-written role uses default variables to alter the role's behavior to match a related configuration scenario. This helps make the role more generic and reusable in a variety of contexts.
The value of any variable defined in a role's defaults directory will be overwritten if that same variable is defined:
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
The following example shows how to use the motd role with a different value for the system_owner role variable. The value specified, someone@host.example.com, will replace the variable reference when the role is applied to a managed host.
[user@host ~]$cat use-motd-role.yml--- - name: use motd role playbook hosts: remote.example.com remote_user: devops become: true vars: system_owner: someone@host.example.com roles: - role: motd
When defined in this way, the system_owner variable replaces the value of the default variable of the same name. Any variable definitions nested within the vars keyword will not replace the value of the same variable if defined in a role's vars directory.
The following example also shows how to use the motd role with a different value for the system_owner role variable. The value specified, someone@host.example.com, will replace the variable reference regardless of being defined in the role's vars or defaults directory.
[user@host ~]$cat use-motd-role.yml--- - name: use motd role playbook hosts: remote.example.com remote_user: devops become: true roles: - role: motd system_owner: someone@host.example.com
Variable precedence can be confusing when working with role variables in a play.
Almost any other variable will override a role's default variables: inventory variables, play vars, 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 are some variables that can do that. Inventory variables and play vars cannot. This is important because it helps keep your play from accidentally changing the internal functioning of the role.
However, variables declared inline as role parameters, like the last of the preceding examples, have very high precedence. They can override variables defined in a role's vars directory. If a role parameter has the same name as a variable set in play vars, a role's vars, or an inventory or playbook variable, the role parameter overrides the other variable.