Ansible Variable Precedence

By | December 13, 2021

In this article I am going to satisfy my curiosity regarding how variables work in Ansible roles.
An Ansible role is a set of reusable resources, such as tasks, handlers and templates that can be used from Ansible playbooks. As a software developer, I would perhaps compare roles to subroutines – neither can be used in separation but have to be invoked from a main program, or a playbook in the case of Ansible roles.

My intention is that people fairly new to Ansible should be able to follow along this article.

Example

I will take a look at an example consisting of a playbook and two roles. The playbook includes and imports the first role, which in turn has a dependency to the second role.

Structure of the example.

Prerequisites

Obviously you will need to have Ansible installed and it should be at least version 2.11.
To do the exercises and compare the output from the different runs, you will also need a comparison-tool. One such tool which is freely available and available for multiple operating systems is Meld.

Preparations

The directory structure for the example looks like this:

.
├── main.yml
├── role1
│   ├── defaults
│   │   └── main.yml
│   ├── meta
│   │   └── main.yml
│   ├── tasks
│   │   └── main.yml
│   └── vars
│       └── main.yml
└── role2
    ├── defaults
    │   └── main.yml
    ├── meta
    │   └── main.yml
    ├── tasks
    │   └── main.yml
    └── vars
        └── main.yml

Create Project Directories

First create a directory for the example project and then in that directory create the above directory structure using the following commands:

mkdir -p role1/defaults
mkdir -p role1/meta
mkdir -p role1/tasks
mkdir -p role1/vars

mkdir -p role2/defaults
mkdir -p role2/meta
mkdir -p role2/tasks
mkdir -p role2/vars

Playbook

In the example project root directory, create the playbook file named “main.yml” with the following contents:

---
- name: Ansible roles and variables playbook
  hosts: localhost
#  vars:
#    some_parameter: val_playbook_vars
  tasks:
  - name: Log playbook first
    debug:
      msg: "Playbook before include & imports: {{ some_parameter }}"
    when: some_parameter is defined
  - name: Log playbook first, variable undefined
    debug:
      msg: "Playbook before include & imports: some_parameter is not defined"
    when: some_parameter is undefined

  - name: Dynamically include role1
    include_role:
      name: role1

  - name: Log variable some_parameter from playbook
    debug:
      msg: "Main playbook log: {{ some_parameter }}"
    when: some_parameter is defined
  - name: Log from playbook if variable some_parameter is undefined
    debug:
      msg: "Main playbook log: some_parameter is not defined"
    when: some_parameter is undefined

  - name: Dynamically include a role1, setting variable
    include_role:
      name: role1
#    vars:
#      some_parameter: val_playbook_dyn_include

  - name: Log playbook before import
    debug:
      msg: "Playbook before import: {{ some_parameter }}"
    when: some_parameter is defined
  - name: Log playbook before import, variable undefined
    debug:
      msg: "Playbook before import: some_parameter is not defined"
    when: some_parameter is undefined

  - name: Import a role1, setting variable
    import_role:
      name: role1
#    vars:
#      some_parameter: val_playbook_stat_import

Note that:

  • The vars section with an assignment to the some_parameter variable is commented out.
    Commenting out the variable assignments in this playbook is intentional and the assignments will be uncommented later.
  • The two first tasks logs either the value of the variable some_variable or a message saying that the variable is undefined.
  • The next task dynamically include the role1 role.
    Including a role will dynamically include the role at the time when the playbook is executed.
  • The next two tasks logs either the value of the some_variable variable or a message saying that the variable is undefined..
    This in order to examine the value of the variable after the role1 has been executed.
  • The next task again dynamically include the role1 role.
    This time assigning a value to the variable some_variable. In this original version of the playbook this assignment is commented out.
  • Again, the two subsequent tasks logs either the value of the variable some_variable or a message saying that the variable is undefined.
  • The final task statically imports the role1 role.
    Importing a role statically means that the role is imported into the playbook at the time of parsing the playbook.
    Again, there is a commented-out variable assignment.

The commented-out variable assignments will be used to examine how variable assignments at different locations are prioritized.

Role1

The first role, role1, consists of four different files; a defaults file, a meta file, a tasks file and a vars file. The name of all the files is main.ml but they are located in different directories in the role.

Defaults File

The defaults file contains variable assignments that assigns default values to variables used in the role. It is located in the role1/defaults directory and it has the following contents:

---
# defaults variables for role1
#some_parameter: val_role1_defaults

The defaults file of the role1 role only contains a commented-out variable assignment for the some_parameter variable.

Meta File

The meta file contains metadata for the role, such as author, license etc. In addition, the meta file contains the dependencies of the role. The meta file is located in the role1/meta directory.

galaxy_info:
  author: Ivan Krizsan
  license: Apache-2.0
  min_ansible_version: 2.11
  galaxy_tags: []
dependencies:
  - role: role2
#    vars:
#      some_parameter: val_role1_meta_dependency

Of interest in the meta file for the sake of this example is the dependency to the role2 role and the (commented out) variable assignment.

Tasks File

Not surprisingly, the tasks file, located in the role1/tasks directory, contains the tasks of the role:

---
- name: Log some_parameter from role1 tasks file
  debug:
    msg: "Log from role1 tasks file: {{ some_parameter }}"
  when: some_parameter is defined
- name: Log if some_parameter is not defined from role1 tasks file
  debug:
    msg: "Log from role1 tasks file, some_parameter is not defined"
  when: some_parameter is undefined

This task file will log either a message containing the value of the variable some_parameter, provided that the variable has been assigned a value, or a message saying that the variable has not been defined.

Vars File

Finally, the vars file, located in the role1/vars directory, contains additional variable definitions for the role.

---
# vars file for role1
#some_parameter: val_role1_vars

As can be seen, also this assignment to the some_parameter variable has been commented out.

Role2

Role2 is very similar to role1 but has no dependencies to other roles. I will list all the different files with only brief comments. Again, the files all have one and the same name, main.yml, but are located in different directories.

Defaults File

Located in the role2/defaults directory with the following contents:

---
# defaults variables for role2
#some_parameter: val_role2_defaults

Meta File

Located in the role2/meta directory, having the following contents:

galaxy_info:
  author: Ivan Krizsan
  license: Apache-2.0
  min_ansible_version: 2.11
  galaxy_tags: []

dependencies: []

Note that role2 has no dependencies.

Tasks File

Located in the role2/tasks directory:

---
- name: Log some_parameter from role2 tasks file
  debug:
    msg: "Log from role2 tasks file, some_parameter: {{ some_parameter }}"
  when: some_parameter is defined
- name: Log if some_parameter is not defined from role2 tasks file
  debug:
    msg: "Log from role2 tasks file, some_parameter is not defined"
  when: some_parameter is undefined

Note that the tasks logs the value of the variable some_parameter or a message saying that the variable has not been defined.

Vars File

Located in the role2/vars directory:

---
# vars file for role2
#some_parameter: val_role2_vars

First Run – No Variable Assignments

When running the playbook in its original version with all the variable assignments commented out as in the above listings, all the attempts at logging the values of variables will result in messages saying that the variable has not been defined.

The log statements marked in red are executed in the order as shown by the circled digits next to each statement.

First run – all variable assignments are commented-out.

In the root of the example playbook, issue the following command:

ansible-playbook main.yml | grep msg

The console output will look like this:

"msg": "Playbook before include & imports: some_parameter is undefined"
"msg": "***** Log 0004: some_parameter is undefined"
"msg": "***** Log 0003: some_parameter is undefined"
"msg": "***** Log 0001: some_parameter is undefined"
"msg": "***** Log 0003: some_parameter is undefined"
"msg": "***** Log 0002: some_parameter is undefined"
"msg": "***** Log 0003: some_parameter is undefined"

Second Run – Setting a Default in Role2

In this section I will examine how assigning a value to a variable in the defaults section of role2 affects what is being logged when the playbook is executed.

Modify the defaults/main.yml file in role2 to look like this:

---
# defaults variables for role2
some_parameter: val_role2_defaults

Running the playbook after the modification yields logs of the some_parameter value “val_role2_defaults” at the following locations in the order specified by the digits next to the log points.

Second run – variable assigned a value in role2 defaults.

Note that:

  • The location where some_parameter is assigned a value is marked in green.
    This is in the defaults/main.yml file in role2.
  • The locations where a value has been logged for some_parameter are marked in red.
  • The order in which the log statements appear is indicated by the circled digits.
    Log statements with multiple digits are executed multiple times.

Console output from the second run will look like this:

"msg": "Playbook before include & imports: some_parameter=val_role2_defaults"
"msg": "***** Log 0004 some_parameter: some_parameter=val_role2_defaults"
"msg": "***** Log 0003: some_parameter=val_role2_defaults"
"msg": "***** Log 0001: some_parameter=val_role2_defaults"
"msg": "***** Log 0003: some_parameter=val_role2_defaults"
"msg": "***** Log 0002: some_parameter=val_role2_defaults"
"msg": "***** Log 0003: some_parameter=val_role2_defaults"

Observations:

  • The variable some_parameter is logged to have a value first thing in the playbook, before any includes and imports.
    The reason for this is that role1 is imported into the playbook and role1 has a dependency to role2, in which the some_parameter variable is assigned a value. Imports are processed during the pre-processing of the playbook, prior to the playbook actually being executed.

Third Run – Setting a Variable in Role2

The purpose of this section is to determine the priority between variable assignments in the defaults and vars sections of one and the same role.

With the above variable assignment in role2 still in place, modify the vars/main.yml file in role2 to look like this:

---
# vars file for role2
some_parameter: val_role2_vars

Running the playbook again after the modification yields logs of the some_parameter value “val_role2_vars” at the same points as in the second run above and in the order specified by the digits next to the log points.

Third run – variable assigned values in role2 defaults and vars.

Console output from the third run looks like this:

"msg": "Playbook before include & imports: some_parameter=val_role2_vars"
"msg": "***** Log 0004 some_parameter: some_parameter=val_role2_vars"
"msg": "***** Log 0003: some_parameter=val_role2_vars"
"msg": "***** Log 0001: some_parameter=val_role2_vars"
"msg": "***** Log 0003: some_parameter=val_role2_vars"
"msg": "***** Log 0002: some_parameter=val_role2_vars"
"msg": "***** Log 0003: some_parameter=val_role2_vars"

Observations:

  • The assignment to the variable some_parameter in the vars section overrides the assignment in the defaults section.
    Keep in mind that both these assignments are located in one and the same role.
  • The previous observation can be generalized:
    The assignment of a variable in the defaults section of a role is overridden by an assignment to the same variable in the vars section of the same role.

Fourth Run – Setting a Default in Role1

Having two assignments to the some_parameter variable in role2, I will now examine how an assignment to the same variable in the defaults section of role1 affects the logs.

With all the earlier modifications in place, edit the defaults/main.yml file in role1 to look like this:

---
# defaults variables for role1
some_parameter: val_role1_defaults

Running the playbook again after the modification yields logs identical to those produces in the third run above where all logs of some_parameter produced the value “val_role2_vars”.

Fourth run – variable assigned values in defaults and vars in role2 and in defaults in role1.

Console output from the fourth run looks like this:

"msg": "Playbook before include & imports: some_parameter=val_role2_vars"
"msg": "***** Log 0004 some_parameter: some_parameter=val_role2_vars"
"msg": "***** Log 0003: some_parameter=val_role2_vars"
"msg": "***** Log 0001: some_parameter=val_role2_vars"
"msg": "***** Log 0003: some_parameter=val_role2_vars"
"msg": "***** Log 0002: some_parameter=val_role2_vars"
"msg": "***** Log 0003: some_parameter=val_role2_vars"

Note that the console log is identical to that of the third run above.

Observations:

  • An assignment in the vars of role2 will override a default in role1.

Fifth Run – Removing the Variable in Role2

In this section I want to examine the priority between assignments to one and the same variable in the defaults sections of the two roles.

Given the current state of the playbook and the two roles, let us comment out the variable assignment in the role2 vars/main.yml, which will look like this after the modification:

---
# vars file for role2
#some_parameter: val_role2_vars

Running the playbook will still produce logs at the same locations and in the same order as before, but when the some_parameter variable is logged, the value output will now be “val_role1_defaults”.

Fifth run – variable assigned values in defaults in role1 and role2.

Console output from the fifth run looks like this:

"msg": "Playbook before include & imports: some_parameter=val_role1_defaults"
"msg": "***** Log 0004 some_parameter: some_parameter=val_role2_defaults"
"msg": "***** Log 0003: some_parameter=val_role1_defaults"
"msg": "***** Log 0001: some_parameter=val_role1_defaults"
"msg": "***** Log 0003: some_parameter=val_role1_defaults"
"msg": "***** Log 0002: some_parameter=val_role1_defaults"
"msg": "***** Log 0003: some_parameter=val_role1_defaults"

Note that the value of some_parameter logged is the value assigned in the role1 defaults section except for the log written from the tasks section in role2, where the value is the value assigned in the defaults section of role2.

After having verified the above outcome, restore the assignment in the role2 vars/main.yml file so that it looks like this:

---
# vars file for role2
some_parameter: val_role2_vars

Sixth Run – Setting a Variable in Role1

In this section I want to examine the priority between assignments to one and the same variable in the vars sections of the two roles.

Assign a value to the variable some_parameter in the vars section of role1 by modifying the vars/main.yml file to look like this:

---
# vars file for role1
some_parameter: val_role1_vars

Running the playbook produces logs at the same locations as earlier but this time there are different values logged at the different locations as can be seen in the figure below.

Sixth run – variable assigned values in vars and defaults in role1 and role2.

Console output from the sixth run looks like this:

"msg": "Playbook before include & imports: some_parameter=val_role1_vars"
"msg": "***** Log 0004 some_parameter: some_parameter=val_role2_vars"
"msg": "***** Log 0003: some_parameter=val_role1_vars"
"msg": "***** Log 0001: some_parameter=val_role1_vars"
"msg": "***** Log 0003: some_parameter=val_role1_vars"
"msg": "***** Log 0002: some_parameter=val_role1_vars"
"msg": "***** Log 0003: some_parameter=val_role1_vars"

Note that the value of some_parameter logged is the value assigned in the role1 vars section except for the log written from the tasks section in role2, where the value is the value assigned in the vars section of role2.
The behaviour is similar to that of the assignments in the defaults sections seen in the fifth run above.

Seventh Run – Setting a Variable at Dependency in Role1

This run will actually be four runs; one to set a baseline, one in which a variable is set at the dependency in role1 and two runs in which the variable is assigned in vars in role2 and role1 respectively. This in order to determine the priority of variable assignments in connection to dependencies between roles.

Seven-One

To establish a baseline, restore all the files in the example project to the state described in Preparations above. Alternatively, download or pull the example project from GitHub replacing all local modifications.

Modify the role1/defaults/main.yml file to look like this:

---
# defaults variables for role1
some_parameter: val_role1_defaults

Modify the role2/defaults/main.yml file to look like this:

---
# defaults variables for role2
some_parameter: val_role2_defaults

Run the playbook using this command:

ansible-playbook main.yml | grep msg

The output should look like this:

"msg": "Playbook before include & imports: some_parameter=val_role1_defaults"
"msg": "***** Log 0004 some_parameter: some_parameter=val_role2_defaults"
"msg": "***** Log 0003: some_parameter=val_role1_defaults"
"msg": "***** Log 0001: some_parameter=val_role1_defaults"
"msg": "***** Log 0003: some_parameter=val_role1_defaults"
"msg": "***** Log 0002: some_parameter=val_role1_defaults"
"msg": "***** Log 0003: some_parameter=val_role1_defaults"

This is the same result as we have seen earlier when assigning a variable a value in the defaults sections of the two roles.

Seven-Two

To assign a value to the variable in connection to the dependency from role1 to role2, modify the main.yml file in role1/meta to look like this:

galaxy_info:
  author: Ivan Krizsan
  license: Apache-2.0
  min_ansible_version: 2.11
  galaxy_tags: []
dependencies:
  - role: role2
    vars:
      some_parameter: val_role1_meta_dependency

As usual, run the playbook using this command:

ansible-playbook main.yml | grep msg

The console output looks like this:

"msg": "Playbook before include & imports: some_parameter=val_role1_meta_dependency"
"msg": "***** Log 0004 some_parameter: some_parameter=val_role1_meta_dependency"
"msg": "***** Log 0003: some_parameter=val_role1_meta_dependency"
"msg": "***** Log 0001: some_parameter=val_role1_meta_dependency"
"msg": "***** Log 0003: some_parameter=val_role1_meta_dependency"
"msg": "***** Log 0002: some_parameter=val_role1_meta_dependency"
"msg": "***** Log 0003: some_parameter=val_role1_meta_dependency"

Observations:

  • The variable assignment in connection to the declaration of a dependency from one role to another overrides assignments to the same variable in the defaults sections of both roles.

Seven-Three

In the next step the variable will be assigned a value in the vars section of role2 in addition to the previous assignments. Modify the main.yml file in role2/vars to look like this:

---
# vars file for role2
some_parameter: val_role2_vars

Again, run the playbook using this command:

ansible-playbook main.yml | grep msg

The console output looks like this:

"msg": "Playbook before include & imports: some_parameter=val_role2_vars"
"msg": "***** Log 0004 some_parameter: some_parameter=val_role2_vars"
"msg": "***** Log 0003: some_parameter=val_role2_vars"
"msg": "***** Log 0001: some_parameter=val_role2_vars"
"msg": "***** Log 0003: some_parameter=val_role2_vars"
"msg": "***** Log 0002: some_parameter=val_role2_vars"
"msg": "***** Log 0003: some_parameter=val_role2_vars"

Note that the variable assignment in the vars section of role2 overrides both the assignments in the defaults sections but also the assignment in connection to the dependency in role1.

Seven-Four

Finally, as far as the seventh run is concerned, the variable will be assigned a value in the vars section of role1 in addition to the previous assignments. Modify the main.yml file in role1/vars to look like this:

---
# vars file for role1
some_parameter: val_role1_vars

Then run the playbook using this command:

ansible-playbook main.yml | grep msg

The console output looks like this:

"msg": "Playbook before include & imports: some_parameter=val_role1_vars"
"msg": "***** Log 0004 some_parameter: some_parameter=val_role2_vars"
"msg": "***** Log 0003: some_parameter=val_role1_vars"
"msg": "***** Log 0001: some_parameter=val_role1_vars"
"msg": "***** Log 0003: some_parameter=val_role1_vars"
"msg": "***** Log 0002: some_parameter=val_role1_vars"
"msg": "***** Log 0003: some_parameter=val_role1_vars"

Note that the variable assignment in the vars section of role1 overrides assignments in defaults sections of the both roles and the assignment in connection to the dependency in role1. It also overrides the assignment in the vars section of role2 except in the case of the logs from the role2 tasks, as we have seen earlier.

Observations:

  • Assignments to a variable in connection to a dependency from one role to another role overrides assignments to the variable in defaults sections of both roles.
  • Assignments to a variable in connection to a dependency from one role to another role is overridden by assignments in vars sections of either role.

Eighth Run – Setting a Variable at Dynamic Include in Playbook

In the eighth run, the priority of variable assignments in connection to dynamically including a role from a playbook will be examined. With all the modifications from the seventh run retained, modify the main.yml playbook file in the root of the example project to look like this (modified rows are highlighted):

---
- name: Ansible roles and variables playbook
  hosts: localhost
#  vars:
#    some_parameter: val_playbook_vars
  tasks:
  - name: Log playbook first
    debug:
      msg: "Playbook before include & imports: some_parameter={{ some_parameter }}"
    when: some_parameter is defined
  - name: Log playbook first, variable undefined
    debug:
      msg: "Playbook before include & imports: some_parameter is undefined"
    when: some_parameter is undefined

  - name: Dynamically include role1
    include_role:
      name: role1

  - name: Log variable some_parameter from playbook before include
    debug:
      msg: "***** Log 0001: some_parameter={{ some_parameter }}"
    when: some_parameter is defined
  - name: Log variable some_parameter from playbook before include
    debug:
      msg: "***** Log 0001: some_parameter is undefined"
    when: some_parameter is undefined

  - name: Dynamically include a role1, setting variable
    include_role:
      name: role1
    vars:
      some_parameter: val_playbook_dyn_include

  - name: Log variable some_parameter from playbook before import
    debug:
      msg: "***** Log 0002: some_parameter={{ some_parameter }}"
    when: some_parameter is defined
  - name: Log variable some_parameter from playbook before import
    debug:
      msg: "***** Log 0002: some_parameter is undefined"
    when: some_parameter is undefined

  - name: Import a role1, setting variable
    import_role:
      name: role1
#    vars:
#      some_parameter: val_playbook_stat_import

Run the playbook using this command:

ansible-playbook main.yml | grep msg

Logs are the same as before except for the log from the tasks of role1 that are executed as result of the dynamic inclusion of role1 from the playbook:

Eighth run – variable assigned values as indicated by green variable assignments.

Note that when the tasks in role1 are executed in step 3 and step 7, the value logged is “val_role1_vars” but when the tasks in role1 are executed in step 5, which is a result of the dynamic inclusion of the role1 from the playbook, the value logged is “val_playbook_dyn_include”.

The console output looks like this:

"msg": "Playbook before include & imports: some_parameter=val_role1_vars"
"msg": "***** Log 0004 some_parameter: some_parameter=val_role2_vars"
"msg": "***** Log 0003: some_parameter=val_role1_vars"
"msg": "***** Log 0001: some_parameter=val_role1_vars"
"msg": "***** Log 0003: some_parameter=val_playbook_dyn_include"
"msg": "***** Log 0002: some_parameter=val_role1_vars"
"msg": "***** Log 0003: some_parameter=val_role1_vars"

Observations:

  • The assignment of a variable in connection to a dynamic role inclusion overrides any values assigned to the variable in the defaults, vars and meta (dependency) sections during the execution of the tasks in the included role that is a result of the dynamic inclusion.

Ninth Run – Setting a Variable at Static Import in Playbook

Having examined variable assignments in connection to role inclusion, the time has come to take a look at variable assignments in connection to static imports of a role. This procedure will be divided into three steps.

First comment out the variable assignments in the vars/main.yml files in both role1 and role2.
Then modify the playbook in the main.yml file in the root of the example project to look like this (modified rows are highlighted):

---
- name: Ansible roles and variables playbook
  hosts: localhost
#  vars:
#    some_parameter: val_playbook_vars
  tasks:
  - name: Log playbook first
    debug:
      msg: "Playbook before include & imports: some_parameter={{ some_parameter }}"
    when: some_parameter is defined
  - name: Log playbook first, variable undefined
    debug:
      msg: "Playbook before include & imports: some_parameter is undefined"
    when: some_parameter is undefined

  - name: Dynamically include role1
    include_role:
      name: role1

  - name: Log variable some_parameter from playbook before include
    debug:
      msg: "***** Log 0001: some_parameter={{ some_parameter }}"
    when: some_parameter is defined
  - name: Log variable some_parameter from playbook before include
    debug:
      msg: "***** Log 0001: some_parameter is undefined"
    when: some_parameter is undefined

  - name: Dynamically include a role1, setting variable
    include_role:
      name: role1
    vars:
      some_parameter: val_playbook_dyn_include

  - name: Log variable some_parameter from playbook before import
    debug:
      msg: "***** Log 0002: some_parameter={{ some_parameter }}"
    when: some_parameter is defined
  - name: Log variable some_parameter from playbook before import
    debug:
      msg: "***** Log 0002: some_parameter is undefined"
    when: some_parameter is undefined

  - name: Import a role1, setting variable
    import_role:
      name: role1
    vars:
      some_parameter: val_playbook_stat_import

Nine-One

With the above modifications in place, run the playbook using this command:

ansible-playbook main.yml | grep msg

Variable assignments, logged values and order of execution are displayed in the following figure:

Run nine-one – variable assigned values as indicated by variable assignments marked green.

The console output looks like this:

"msg": "Playbook before include & imports: some_parameter=val_playbook_stat_import"
"msg": "***** Log 0004 some_parameter: some_parameter=val_role1_meta_dependency"
"msg": "***** Log 0003: some_parameter=val_role1_meta_dependency"
"msg": "***** Log 0001: some_parameter=val_playbook_stat_import"
"msg": "***** Log 0003: some_parameter=val_playbook_dyn_include"
"msg": "***** Log 0002: some_parameter=val_playbook_stat_import"
"msg": "***** Log 0003: some_parameter=val_playbook_stat_import"

Note that:

  • The variable assignment connected to the static role import has overridden the variable assignment connected to the dependency and the variable assignments in the defaults sections of the both roles in steps 1, 4, 6 and 7.

Nine-Two

Remove the comment in the role2/vars/main.yml file as to enable the variable assignment again.

Run the playbook again using this command:

ansible-playbook main.yml | grep msg

Variable assignments, logged values and order of execution are displayed in the following figure:

Run nine-two – variable assigned values as indicated by green variable assignments.

The console output looks like this:

"msg": "Playbook before include & imports: some_parameter=val_playbook_stat_import"
"msg": "***** Log 0004 some_parameter: some_parameter=val_role2_vars"
"msg": "***** Log 0003: some_parameter=val_role2_vars"
"msg": "***** Log 0001: some_parameter=val_playbook_stat_import"
"msg": "***** Log 0003: some_parameter=val_playbook_dyn_include"
"msg": "***** Log 0002: some_parameter=val_playbook_stat_import"
"msg": "***** Log 0003: some_parameter=val_playbook_stat_import"

Note that:

  • The variable assignment in the vars section of role2 has overridden the assignment in the meta (dependencies) section of role1 in the second and third log statements.
    The affected log statements are located in the tasks sections of role1 and role2 respectively.

Nine-Three

Also remove the comment in the role1/vars/main.yml file as to enable the variable assignment.

Run the playbook again using this command:

ansible-playbook main.yml | grep msg

Variable assignments, logged values and order of execution are displayed in the following figure:

Run nine-three – some_parameter assigned values as indicated by green variable assignments.

The console output looks like this:

"msg": "Playbook before include & imports: some_parameter=val_role1_vars"
"msg": "***** Log 0004 some_parameter: some_parameter=val_role2_vars"
"msg": "***** Log 0003: some_parameter=val_role1_vars"
"msg": "***** Log 0001: some_parameter=val_role1_vars"
"msg": "***** Log 0003: some_parameter=val_playbook_dyn_include"
"msg": "***** Log 0002: some_parameter=val_role1_vars"
"msg": "***** Log 0003: some_parameter=val_playbook_stat_import"

Observations:

  • Variable assignments in connection to static import of roles will override variable assignments in the defaults and vars sections of the imported role.

Tenth Run – Removing Static Import in Playbook

The time has come to see how removing the static import in the playbook affects the output generated when the playbook is run.
In the main.yml file located in the project root directory, comment out the last task. The two last tasks in the file should look like this:

  - name: Log variable some_parameter from playbook before import
    debug:
      msg: "***** Log 0002: some_parameter={{ some_parameter }}"
    when: some_parameter is defined
  - name: Log variable some_parameter from playbook before import
    debug:
      msg: "***** Log 0002: some_parameter is undefined"
    when: some_parameter is undefined

#  - name: Import a role1, setting variable
#    import_role:
#      name: role1
#    vars:
#      some_parameter: val_playbook_stat_import

Run the playbook using this command:

ansible-playbook main.yml | grep msg

Variable assignments, logged values and order of execution are displayed in the following figure:

Run ten – static import has been removed from the playbook.

The console output for the tenth run looks like this:

"msg": "Playbook before include & imports: some_parameter is undefined"
"msg": "***** Log 0004 some_parameter: some_parameter=val_role2_vars"
"msg": "***** Log 0003: some_parameter=val_role1_vars"
"msg": "***** Log 0001: some_parameter is undefined"
"msg": "***** Log 0003: some_parameter=val_playbook_dyn_include"
"msg": "***** Log 0002: some_parameter is undefined"

Note that:

  • At the first log statement some_parameter is undefined.
    This is due to role1 not being statically imported in the playbook. Recall that static imports are processed during pre-processing of the playbook, prior to its execution.
    In addition, there are no assignments to the variable some_parameter being executed before the first log statement.
  • The second log statement logs the value val_role2_vars.
    The log statement is located in the role2 tasks and the value is assigned in the vars section of role2.
  • The third log statement, logged from the role1 tasks, logs the value val_role1_vars.
    The execution of the role1 tasks is the result of the first include in the playbook.
  • The fifth log statement, also logged from the role1 tasks, logs the value val_playbook_dyn_include.
    The execution of the role1 tasks is, in this case, the result of the second include in the playbook, the include with a variable assignment.

Eleventh Run – Setting a Variable in the Playbook

In the eleventh run, which also is the final run of this article, the variable some_parameter will be assigned a value in the vars section of the playbook. In the main.yml file located in the project root directory, remove the commenting of the vars section first in the playbook. The first few rows of the main.yml should look like this:

---
- name: Ansible roles and variables playbook
  hosts: localhost
  vars:
    some_parameter: val_playbook_vars
  tasks:

Run the playbook using this command:

ansible-playbook main.yml | grep msg

Variable assignments, logged values and order of execution are displayed in the following figure:

Run eleven – variable assignment in vars section of playbook has been uncommented.

The console output for the eleventh run should look like this:

"msg": "Playbook before include & imports: some_parameter=val_playbook_vars"
"msg": "***** Log 0004 some_parameter: some_parameter=val_role2_vars"
"msg": "***** Log 0003: some_parameter=val_role1_vars"
"msg": "***** Log 0001: some_parameter=val_playbook_vars"
"msg": "***** Log 0003: some_parameter=val_playbook_dyn_include"
"msg": "***** Log 0002: some_parameter=val_playbook_vars"

Observations:

  • Variable assignments in the vars section of the playbook overrides assignments in defaults sections in dynamically included roles.
  • Variable assignments in the vars section of a dynamical include overrides assignments (to the same variables) in the vars section of the included role.

Final Words

It did take me some time to get here, but all in all I have found the exercise worthwhile. To me there is no better way to learn something than to put it to use. The following list reiterates observations made in this article regarding variable precedence:

  • An assignment in the vars section of a role overrides an assignment (to the same variable) in the defaults section of the role.
  • An assignment in the defaults section of a role is override by an assignment (to the same variable) in the vars section of another role to which the first role has a dependency.
  • An assignment in the defaults section of a role is override by an assignment (to the same variable) in the defaults section of another role to which the first role has a dependency.
  • An assignment in the vars section of a role is override by an assignment (to the same variable) in the vars section of another role to which the first role has a dependency.
  • A variable assignment in connection to the declaration of a dependency from one role to another overrides assignments to the same variable in the defaults sections of both roles.
  • Assignments to a variable in connection to a dependency from one role to another role is overridden by assignments in vars sections of either role.
  • An assignment of a variable in connection to a dynamic role inclusion overrides any values assigned to the variable in the defaults, vars and meta (dependency) sections during the execution of the tasks in the included role that is a result of the dynamic inclusion.
  • Variable assignments in the vars section of the playbook overrides assignments in defaults sections in dynamically included roles.
  • Variable assignments in the vars section of a dynamical include overrides assignments (to the same variables) in the vars section of the included role.

Please refer to the Ansible documentation for in-depth information on Ansible variable precedence.

Happy coding!

Leave a Reply

Your email address will not be published.