Using Role in a Play
A role sitting in roles/myapp/ does nothing on its own — it needs to actually be applied to a play. This chapter covers the common, simple way to do that, and gives import_role/include_role — previewed only lightly back in the includes and imports section — their full, working treatment.
Create myapp Role
Before anything else, create a myapp role:
ansible-galaxy role init roles/myappIt will automatically creates roles folder and initialize an empty myapp role. myapp is nothing but a name of your role — name it anything that makes sense to your project.
The reason behind placing any role under roles is because Ansible automatically searches roles directory relative to the playbook file.
The roles: Keyword
- name: Deploy myapp
hosts: servers
roles:
- myappThis runs myapp’s tasks/main.yml against the servers group, automatically picking up its handlers, defaults, and vars along the way — no explicit include or import needed anywhere.
But if you run this playbook, nothing happens because there’s nothing inside tasks/main.yml yet. It will gather facts and complete:
ansible-playbook playbook.yamlTASK [Gathering Facts] ********
ok: [ubuntu]What roles: Actually Does Under The Hood
roles: is essentially shorthand for importing every listed role, at the play level, before anything else. Worth stating explicitly, since it affects execution order the moment a play has both roles: and its own tasks::
- name: Deploy myapp
hosts: servers
roles:
- myapp
tasks:
- name: Extra step after the role
ansible.builtin.debug:
msg: "This runs after every role listed above"Every role in roles: runs completely, in order, before the play’s own tasks: section starts at all — afterall any role is also a list of some tasks.
For practical, a simple task at myapp/tasks/main.yml:
- name: Say Hi
ansible.builtin.debug:
msg: Saying HiNow if you run the above playbook:
PLAY [Deploy myapp] ************************************************************
TASK [Gathering Facts] *********************************************************
ok: [ubuntu]
TASK [myapp : Say Hi] **********************************************************
ok: [ubuntu] => {
"msg": "Saying Hi"
}
TASK [Extra step after the role] ***********************************************
ok: [ubuntu] => {
"msg": "This runs after every role listed above"
}
PLAY RECAP *********************************************************************
ubuntu : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 import_role: The Same Static Resolution As Before
Used inside a tasks: list directly, giving finer control over exactly where a role’s tasks run relative to other tasks:
tasks:
- name: Do something first
ansible.builtin.debug:
msg: "Before the role"
- name: Import myapp role here specifically
ansible.builtin.import_role:
name: myapp
- name: Do something after
ansible.builtin.debug:
msg: "After the role"Resolved at parse time, exactly like import_tasks — the role name has to be fixed and known in advance. You can see it yourself:
PLAY [Deploy myapp] ************************************************************
TASK [Gathering Facts] *********************************************************
ok: [ubuntu]
TASK [Do something first] ******************************************************
ok: [ubuntu] => {
"msg": "Before the role"
}
TASK [myapp : Say Hi] **********************************************************
ok: [ubuntu] => {
"msg": "Saying Hi"
}
TASK [Do something after] ******************************************************
ok: [ubuntu] => {
"msg": "After the role"
} include_role: The Same Dynamic Resolution As Before
tasks:
- name: Include a role determined at runtime
ansible.builtin.include_role:
name: "{{ app_name }}_role"Resolved at runtime, exactly like include_tasks — the role name can depend on a variable only known once the play is actually executing.
When Would You Use import_role/include_role Instead Of roles:?
roles: is the right default for the common case: apply one or more roles to a play, running before anything else in it. Reach for import_role/include_role specifically when a role’s tasks need to be interleaved at a particular point relative to other tasks — not just “always first” — or when the role’s name itself needs to be determined at runtime, which only include_role can do at all.
A Realistic Example: Multiple Roles In One Play
roles: accepts a list, applied in order:
- name: Full Server Setup
hosts: servers
roles:
- common
- myapp
- monitoringcommon runs first, then myapp, then monitoring — each one’s entire task list completing before the next one begins.
Best Practices
- Use
roles:for the common case — applying one or more roles to a play, running before the play’s own tasks. - Use
import_role/include_rolewhen a role’s tasks need to run at a specific point relative to other tasks, not simply first. - Use
include_rolespecifically when a role’s name needs to be determined at runtime —import_rolecan’t do this, for exactly the same static-resolution reasonimport_taskscouldn’t.