Defining rolesLast updated: 15 October 2020
Roles are ways of automatically loading certain vars_files, tasks, and handlers based on a known file structure. Grouping content by roles also allows easy sharing of roles with other users.
Roles are ways of automatically loading certain vars_files, tasks, and handlers based on a known file structure. Grouping content by roles also allows easy sharing of roles with other users.
Example project structure:
|site.yml
|webservers.yml
|fooservers.yml
|roles
| |common
| | |tasks
| | |handlers
| | |files
| | |templates
| | |vars
| | |defaults
| | |meta
| |webservers
| | |tasks
| | |defaults
| | |meta
Roles expect files to be in certain directory names. Roles must include at least one of these directories, however it is possible to exclude any directory which is not in use.
When a directory is used, each directory must contain a main.yml file, which contains the following:
Other YAML files may be included in certain directories. For example, it is common practice to have platform-specific tasks included from the tasks/main.yml file:
---
# roles/example/tasks/main.yml
- name: added in 2.4, previously 'include' was used
import_tasks: redhat.yml
when: ansible_facts['os_family']|lower == 'redhat'
- import_tasks: debian.yml
when: ansible_facts['os_family']|lower == 'debian'
# roles/example/tasks/redhat.yml
- yum:
name: "httpd"
state: present
# roles/example/tasks/debian.yml
- apt:
name: "apache2"
state: present
Roles may also include modules and other plugin types.
The classic (original) way to use roles is via the roles: option for a given play:
---
- hosts: webservers
roles:
- common
- webservers
This designates the following behaviors, for each role ‘x’:
When using tags with tasks, unsure that you also tag your pre_tasks, post_tasks, and role dependencies. Pass this along as well, especially if the pre/post tasks and role dependencies are used for monitoring outage window control or load balancing.
As of v2.4, you can now use roles in line with any other tasks using import_role or include_role:
---
- hosts: webservers
tasks:
- debug:
msg: "before we run our role"
- import_role:
name: example
- include_role:
name: example
- debug:
msg: "after we ran our role"
When roles are defined in the classic manner, they are treated as static imports and processed during playbook parsing. The name used for the role can be a simple name, or it can be a fully qualified path:
---
- hosts: webservers
roles:
- role: '/path/to/my/roles/common'
Roles can also accept other keywords:
---
- hosts: webservers
roles:
- common
- role: foo_app_instance
vars:
dir: '/opt/a'
app_port: 5000
- role: foo_app_instance
vars:
dir: '/opt/b'
app_port: 5001
Or, using the newer syntax:
---
- hosts: webservers
tasks:
- include_role:
name: foo_app_instance
vars:
dir: '/opt/a'
app_port: 5000
You can conditionally import a role and execute its tasks:
---
- hosts: webservers
tasks:
- include_role:
name: some_role
when: "ansible_facts['os_family'] == 'RedHat'"
Finally, you may wish to assign tags to the tasks inside the roles you specify. You can do that as follows:
---
- hosts: webservers
roles:
- role: foo
tags:
- bar
- baz
# using YAML shorthand, this is equivalent to the above:
- { role: foo, tags: ["bar", "baz"] }
Or, again, using the newer syntax:
---
- hosts: webservers
tasks:
- import_role:
name: foo
tags:
- bar
- baz
This tags all of the tasks in that role with the tags specified, appending to any tags that are specified inside the role.
If you want to tag just the import of the role itself:
---
- hosts: webservers
tasks:
- include_role:
name: bar
tags:
- foo
The tags in this example will not be added to tasks inside an include_role, you can use a surrounding block directive to do both.
It is not possible to import a role while specifying a subset of tags to execute. If you want to construct a role with many tags and you want to call subsets of the role at different times, you should consider just splitting that role into multiple roles.
Role default variables allow you to set default variables for included or dependent roles (see below).
To create defaults, simply add a code [defaults/main.yml] file in your role directory.
These variables will have the lowest priority of any variables available, and can be easily overridden by any other variable, including inventory variables.
Role dependencies allow you to automatically pull in other roles when using a role. Role dependencies are stored in the meta/main.yml file within the role directory, as noted above. This file should contain a list of roles and parameters to insert before the specified role, such as the following example roles/myapp/meta/main.yml:
---
dependencies:
- role: common
vars:
some_parameter: 3
- role: apache
vars:
apache_port: 80
- role: postgres
vars:
dbname: blarg
other_parameter: 12
Role dependencies must use the classic role definition style. Role dependencies are always executed before the role that includes them, and may be recursive. Dependencies also follow the duplication rules specified above. If another role also lists it as a dependency, it will not be run again based on the same rules given above.
Always remember that when using allow_duplicates: true, it needs to be in the dependent role’s meta/main.yml, not the parent. For example, a role named car depends on a role named wheel as follows:
---
dependencies:
- role: wheel
vars:
n: 1
- role: wheel
vars:
n: 2
- role: wheel
vars:
n: 3
- role: wheel
vars:
n: 4
And the wheel role depends on two roles: tire and brake. The meta/main.yml for wheel would then contain the following:
---
dependencies:
- role: tire
- role: brake
And the meta/main.yml for tire and brake would have to contain the following:
---
allow_duplicates: true
The resulting execution order would be as follows:
tire(n=1)
brake(n=1)
wheel(n=1)
tire(n=2)
brake(n=2)
wheel(n=2)
...
car
Note that we did not have to use allow_duplicates: true for wheel, because each instance defined by car uses different parameter values.
Ansible will only allow one role to execute once, even if defined multiple times, if the parameters defined on the role are not different for each definition. For example:
---
- hosts: webservers
roles:
- foo
- foo
Given the above, the role foo will only be run once.
To make roles run more than once, there are two options:
---
- hosts: webservers
roles:
- role: foo
vars:
message: "first"
- { role: foo, vars: { message: "second" } }
In this example, because each role definition has different parameters, foo will run twice.# playbook.yml
---
- hosts: webservers
roles:
- foo
- foo
# roles/foo/meta/main.yml
---
allow_duplicates: true
In this example, foo will run twice because we have explicitly enabled it to do so.
Ansible will search for roles in the following way: