A role is a playbook in fixed folders
An Ansible role is a fixed folder layout that holds one reusable piece of a set-up: its tasks, its variables, its files and more. Knowing which folder a variable lives in tells you whether a user can override it, which is the part of roles that surprises people most.
A school that sets up three computer rooms can write one role and reuse it three times.
By the end of this lesson
Note
I can lay out a role and say which variable wins.
Key words
| Word | What it means | Example |
|---|---|---|
| Role | a reusable set-up in a fixed folder layout | |
| Playbook | a file that says which tasks run on which hosts | |
| Defaults | a role's weakest variables, meant to be overridden | |
| Role vars | a role's own variables, which beat the play's | |
| Extra vars | values typed with -e, the strongest of all | -e greeting=Hello |
| Precedence | the order that decides which variable wins |
The cells students fill in are left blank in this preview.
A role has a fixed folder layout
Ansible finds each part of a role by its folder name, so the layout never changes. tasks holds what the role does. defaults and vars hold its variables. templates holds files with gaps to fill, handlers holds tasks that wait to be told, and meta says who made it.
The tool ansible-galaxy builds the empty layout for you, with no network needed.
The folders of a role
| Folder | What it holds | Entry file |
|---|---|---|
| tasks | the work the role does | main.yml |
| defaults | variables a user may override | main.yml |
| vars | variables the role keeps for itself | main.yml |
| templates | files with gaps to fill | name.j2 |
| handlers | tasks that run only when told | main.yml |
| meta | the role's author and description | main.yml |
The cells students fill in are left blank in this preview.
Make a folder for the Ansible work
mkdir -p ~/iac-demo/ansibleShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Go into it
cd ~/iac-demo/ansibleShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Build the layout of a role called greeter
ansible-galaxy role init --init-path roles greeterShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
List its folders
find roles/greeter -type d | sortShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
When two variables share a name, one wins
The same variable can be set in several places, and Ansible has a fixed order. From weakest to strongest: a role's defaults, then the play's vars, then the role's own vars, then -e on the command line.
So defaults are for values a user is meant to change. vars are for values the role insists on. -e always has the last word.
Worked example: three variables, four places
| Variable | defaults | Play vars | Role vars | Winner |
|---|---|---|---|---|
| shop_name | Corner Shop | Riverside Bakery | none | Riverside Bakery |
| greeting | Welcome | none | none | Welcome |
| opening_hours | none | all night | 09:00 to 17:00 | 09:00 to 17:00 |
The cells students fill in are left blank in this preview.
Write the role's defaults
printf '%s\n' '---' 'shop_name: Corner Shop' 'greeting: Welcome' > roles/greeter/defaults/main.ymlShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Write the role's own vars
printf '%s\n' '---' 'opening_hours: 09:00 to 17:00' > roles/greeter/vars/main.ymlShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Write one task that shows all three
printf '%s\n' '---' '- name: Show the sign' ' ansible.builtin.debug:' ' msg: "{{ greeting }} to {{ shop_name }}, open {{ opening_hours }}"' > roles/greeter/tasks/main.ymlShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Write a play with two variables
printf '%s\n' '---' '- name: Put up the sign' ' hosts: localhost' ' gather_facts: false' ' vars:' ' shop_name: Riverside Bakery' ' opening_hours: all night' ' roles:' ' - greeter' > sign.ymlShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Run it against this machine
The Runs use -i localhost, to say the only host is this machine, and -c local to say there is nothing to connect to. The play sets shop_name, which beats the default, and opening_hours, which the role's own vars should beat. grep msg keeps the one line that shows the result.
Run the play and show the sign
ansible-playbook -i localhost, -c local sign.yml | grep msgShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Run it again with -e values
ansible-playbook -i localhost, -c local sign.yml -e greeting=Hello -e opening_hours=closed | grep msgShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
A common mistake
Warning
Many people put a value they want users to change in vars/main.yml. Role vars beat the play, so nobody can change it except with -e. Put a value in defaults if it may change, and in vars only if it must not.
Four layers in a dozen lines of Python
# Each layer overwrites the one before it, weakest first.
defaults = {"shop_name": "Corner Shop", "greeting": "Welcome"}
play_vars = {"shop_name": "Riverside Bakery", "opening_hours": "all night"}
role_vars = {"opening_hours": "09:00 to 17:00"}
extra_vars = {}
final = {}
for layer in [defaults, play_vars, role_vars, extra_vars]:
for name in layer:
final[name] = layer[name]
print(final)
# Put "greeting": "Hello" into extra_vars and run it again.