Passing Variable Into Role
Roles are only genuinely reusable if you can feed them different values for different situations. This chapter covers three distinct ways to do that, and when each one is actually the right choice with examples.
Mechanism One: Just Set A Variable With Higher Precedence
Since defaults/main.yml sits at the very bottom of the precedence hierarchy, literally any other variable source naturally overrides it — no special role-specific syntax required at all:
app_port: 9090defaults/main.yml is overridden by group_vars/servers.yaml:
app_port: 9090- name: Deploy myapp
hosts: servers
roles:
- myapp- name: Display App Port
ansible.builtin.debug:
msg: "App Port = {{ app_port }}"myapp’s tasks see app_port as 9090, sourced entirely from group_vars, with nothing on the roles: line itself needing to change at all.
TASK [myapp : Display App Port] ****************************
ok: [ubuntu] => {
"msg": "App Port = 9090"
}
ok: [fedora] => {
"msg": "App Port = 9090"
}Mechanism Two: Parameters Directly On roles:
A roles: entry can be written as a dictionary instead of a bare string, with extra key/value pairs treated as variables passed specifically to that one role application:
roles:
- role: myapp
app_port: 80Useful when you want a value set right there in the play itself, without needing a separate inventory or group_vars file for it. Anything we have set previously will be overridden:
TASK [myapp : Display App Port] ****************************
ok: [ubuntu] => {
"msg": "App Port = 80"
}
ok: [fedora] => {
"msg": "App Port = 80"
}Mechanism Three: vars: Alongside include_role
When using include_role, a task-level vars: block passes variables specifically to that invocation:
- name: Include myapp with specific variables
ansible.builtin.include_role:
name: myapp
vars:
app_port: 2040ok: [ubuntu] => {
"msg": "App Port = 2040"
}
ok: [fedora] => {
"msg": "App Port = 2040"
}Comparing The Three
- Mechanism one (an external variable source) fits genuinely environment-specific configuration — different values per environment — that belongs in inventory or
group_varsregardless of which role happens to be reading it. - Mechanism two (
roles:parameters) fits a quick, one-off override specific to how this particular play uses the role, kept visible right there in the play file. - Mechanism three (
vars:withinclude_role) is functionally similar to mechanism two, for the dynamic, task-level form specifically — useful when you’re already usinginclude_rolefor its runtime-name flexibility and want to pass variables at the same time.
A Realistic Example: The Same Role, Two Different Apps
This is the whole motivation from the start of this section, made concrete — one role, applied twice, with different data each time:
- name: Deploy Two Different Apps With The Same Role
hosts: servers
roles:
- role: myapp
app_name: "webapp"
app_port: 8080
- role: myapp
app_name: "apiapp"
app_port: 9090- name: Display App Name and App Port
ansible.builtin.debug:
msg: "App Name = {{ app_name }} and App Port = {{ app_port }}"ansible-playbook playbook.yamlTASK [myapp : Display App Name and App Port] *******************************
ok: [ubuntu] => {
"msg": "App Name = webapp and App Port = 8080"
}
ok: [fedora] => {
"msg": "App Name = webapp and App Port = 8080"
}
TASK [myapp : Display App Name and App Port] *******************************
ok: [ubuntu] => {
"msg": "App Name = apiapp and App Port = 9090"
}
ok: [fedora] => {
"msg": "App Name = apiapp and App Port = 9090"
}Since the parameters genuinely differ between these two entries, the role runs fully both times. The full rule governing exactly when Ansible does or doesn’t re-run an already-applied role is covered in the next chapter.
A Gotcha: Parameters On roles: Are Scoped To That Role Only
Setting app_port: 9090 as a parameter on one roles: entry does not leak into the play’s other tasks or other listed roles — it’s scoped specifically to that one role’s invocation, unlike an ordinary play-level vars:, which would be visible everywhere in the play. Worth being explicit about, since it’s a genuinely different scoping behavior than everything else in this course labeled simply vars:.
Best Practices
- Use inventory/group_vars for genuinely environment-specific configuration that shouldn’t be hardcoded into any particular play at all.
- Use
roles:parameters (orvars:withinclude_role) for values specific to how one particular play applies a role — especially when applying the same role more than once with different data. - Remember
roles:parameters are scoped to that specific role invocation, not the whole play.