Difference in import_role and include_role in terms of variable precedence

Viewed 3830

I have defined a variable var_for_all in group_vars/all.yml and another one variables_role_var1 in roles/../vars/main.yml. Overriding them in playbook using import_role and include_role seems to follow different precedence rules and results different values.

I was under the assumption that first role block will output below debug message:

"msg": "var_for_all = From group_vars/all.yml, variables_role_var1 = from role vars/ dir"

But getting output as:

"msg": "var_for_all = From playbook import_role_test2, variables_role_var1 = from role vars/ dir"

Could anyone please explain the behavior or point me if I am missing something? Have gone thorough variable precedence, Dynamic vs Static and Roles.

my_playbook.yml

- hosts: localhost
  gather_facts: no
  tasks:
    - name: import_role_test1
      import_role:
        name: variables
        tasks_from: precedence

    - name: import_role_test2
      import_role:
        name: variables
        tasks_from: precedence
      vars: 
        var_for_all: From playbook import_role_test2
        variables_role_var1: From playbook import_role_test2

    - name: include_role_test3
      include_role:
        name: variables
        tasks_from: precedence
      vars: 
        var_for_all: From playbook import_role_test3
        variables_role_var1: From playbook import_role_test3
2 Answers

I wasn't able to reproduce the results. See the code and results below the line.

  • The result of test 3 can be explained by precedence 17. of "task vars (only for the task)".

  • The result of test 2 can be explained also by precedence 17. of "task vars (only for the task)".

  • The result of "variables_role_var1" in test 1 can be explained by precedence 15. of "role vars (defined in role/vars/main.yml)". I can't explain the result of "var_for_all" in test 1. This might be an Ansible's issue. Both 1st and 2nd test import the role and "Ansible pre-processes all static imports during Playbook parsing time."


shell> cat group_vars/all.yml 
var_for_all: From group_vars/all.yml

shell> cat roles/variables/vars/main.yml
variables_role_var1: From roles/variables/vars/main.yml

shell> cat roles/variables/tasks/precedence.yml 
- debug:
    msg:
      - "{{ var_for_all }}"
      - "{{ variables_role_var1 }}"

shell> cat playbook.yml
- hosts: localhost
  tasks:
    - name: import_role_test1
      import_role:
        name: variables
        tasks_from: precedence.yml

    - name: import_role_test2
      import_role:
        name: variables
        tasks_from: precedence.yml
      vars: 
        var_for_all: From playbook import_role_test2
        variables_role_var1: From playbook import_role_test2

    - name: include_role_test3
      include_role:
        name: variables
        tasks_from: precedence.yml
      vars: 
        var_for_all: From playbook import_role_test3
        variables_role_var1: From playbook import_role_test3

give

    "msg": [
        "From playbook import_role_test2",
        "From roles/variables/vars/main.yml"
    ]

    "msg": [
        "From playbook import_role_test2",
        "From playbook import_role_test2"
    ]

    "msg": [
        "From playbook import_role_test3",
        "From playbook import_role_test3"
    ]

Things may change when the variables are explicitly expanded by a host

shell> cat roles/variables/tasks/precedence.yml 
- set_fact:
    var_for_all: "{{ var_for_all }}"
    variables_role_var1: "{{ variables_role_var1 }}"
- debug:
    msg:
      - "{{ var_for_all }}"
      - "{{ variables_role_var1 }}"

give

    "msg": [
        "From playbook import_role_test2",
        "From roles/variables/vars/main.yml"
    ]

    "msg": [
        "From playbook import_role_test2",
        "From roles/variables/vars/main.yml"
    ]

    "msg": [
        "From playbook import_role_test3",
        "From playbook import_role_test3"
    ]

I think, the problem is quite simpler:

All import_* statement run at initialization time and any kind of vars(*) will be initialized also, whether it has a failing condition or anything that skipping the real execution.

(*) e.g.: vars/main.yml file from the imported task or the vars attribute of the import_role

  • A quite minimal example:
    - debug:
        var: foo
    
    - when: False
      import_role:
        name: any_role
      vars:
        foo: 'Cherry'
    
  • And the result:
    TASK [fun : debug] ******************************************************************
    task path: roles/fun/tasks/main.yml:1
    ok: [test.local] => {
        "foo": "Cherry"
    }
    

(That was the reason why I replaced the my all import_role to include_role)

Related