skip to Main Content

I’m using the latest version of Ansible, and I am trying to use a default variable in role-one used on host one, in role-two, used on host two, but I can’t get it to work.

Nothing I have found in the documentation or on StackOverflow has really helped. I’m not sure what I am doing wrong. Ideally I want to set the value of the variable once, and be able to use it in another role for any host in my playbook.

I’ve broken it down below.


In my inventory I have a hosts group called [test] which has two hosts aliased as one and two.

[test]
one ansible_host=10.0.1.10 ansible_connection=ssh ansible_user=centos ansible_ssh_private_key_file=<path_to_key>
two ansible_host=10.0.1.20 ansible_connection=ssh ansible_user=centos ansible_ssh_private_key_file=<path_to_key>

I have a single playbook with a play for each of these hosts and I supply the hosts: value as "{{ host_group }}[0]" for host one and "{{ host_group }}[1]" for host two.

The play for host one uses a role called role-one and the play for host two uses a role called role-two.

- name: Test Sharing Role Variables
  hosts: "{{ host_group }}[0]"
  roles:
    - ../../ansible-roles/role-one

- name: Test Sharing Role Variables
  hosts: "{{ host_group }}[1]"
  roles:
    - ../../ansible-roles/role-two  

In role-one I have set a variable variable-one.

---
# defaults file for role-one

variable_one: Role One Variable

I want to use the value of variable_one in a template in role-two but I haven’t had any luck. I’m using the below as a task in role-two to test and see if the variable is getting "picked-up".

---
# tasks file for role-two
- debug:
    msg: "{{ variable_one }}"

When I run the playbook with ansible-playbook test.yml --extra-vars "host_group=test" I get the below failure.

TASK [../../ansible-roles/role-two : debug] ***********************************************************************************************************************************************************************************************
fatal: [two]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: "hostvars['test']" is undefinednnThe error appears to be in 'ansible-roles/role-two/tasks/main.yml': line 3, column 3, but maynbe elsewhere in the file depending on the exact syntax problem.nnThe offending line appears to be:nn# tasks file for role-twon- debug:n  ^ heren"}

2

Answers


  1. Variables declared in roles are scoped to the play. If you want to access a variable from role-one in role-two, they would both need to be in the same play. For example, you could write:

    - name: Test Sharing Role Variables
      hosts: "{{ host_group }}"
      tasks:
        - import_role:
            name: role-one
          when: inventory_hostname == "one"
    
        - import_role:
            name: role-two
          when: inventory_hostname == "two"
    

    Alternatively, you could restructure your roles so that the variables can be imported separately from your actions. That is, have a role_one_vars role that does nothing but define variables, and then you can import that in both role-one and role-two. That is, you would have a structure something like:

    playbook.yml
    hosts
    roles/
      role-one/
        tasks/
          main.yml
      role-one-vars/
        variables/
          main.yml
      role-two/
        tasks/
          main.yml
    

    And role-one/tasks/main.yml would look like:

    - import_role:
        name: role-one-vars
    
    - debug:
        msg: "in role-one: {{ variable_one }}"
    

    role-two/tasks/main.yml would look like:

    ---
    - import_role:
        name: role-one-vars
    
    - debug:
        msg: "in role-two: {{ variable_one }}"
    

    And role-one-vars/vars/main.yml would look like:

    ---
    variable_one: role one variable
    

    Putting this all together, the output looks like:

    
    PLAY [Test Sharing Role Variables] *****************************************************************************************************************************************
    
    TASK [Gathering Facts] *****************************************************************************************************************************************************
    ok: [one]
    
    TASK [role-one : debug] ****************************************************************************************************************************************************
    ok: [one] => {
        "msg": "in role-one: role one variable"
    }
    
    PLAY [Test Sharing Role Variables] *****************************************************************************************************************************************
    
    TASK [Gathering Facts] *****************************************************************************************************************************************************
    ok: [two]
    
    TASK [role-two : debug] ****************************************************************************************************************************************************
    ok: [two] => {
        "msg": "in role-two: role one variable"
    }
    
    PLAY RECAP *****************************************************************************************************************************************************************
    one                        : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
    two                        : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
    
    Login or Signup to reply.
  2. Q: "Access variable from one role in another role in an Ansible playbook with multiple hosts"

    A: Short answer: Use set_fact and put the variable into the hostvars.

    Details: Given the roles

    shell> cat roles/role-one/defaults/main.yml 
    variable_one: Role One Variable
    
    shell> cat roles/role-one/tasks/main.yml 
    - debug:
        var: variable_one
    
    shell> cat roles/role-two/tasks/main.yml 
    - debug:
        var: variable_one
    

    The playbook

    - hosts: one
      roles:
        - role-one
      tasks:
        - debug:
            var: variable_one
    
    - hosts: two
      roles:
        - role-two
    
    - hosts: one
      tasks:
        - debug:
            var: variable_one
    

    gives (abridged)

    PLAY [one] ************************************************
    
    TASK [role-one : debug] ****
    ok: [one] => 
      variable_one: Role One Variable
    
    TASK [debug] ****
    ok: [one] => 
      variable_one: Role One Variable
    
    PLAY [two] ************************************************
    
    TASK [role-two : debug] ****
    ok: [two] => 
      variable_one: VARIABLE IS NOT DEFINED!
    
    PLAY [one] ************************************************
    
    TASK [debug] ****
    ok: [one] => 
      variable_one: VARIABLE IS NOT DEFINED!
    

    As expected, the variable variable_one is visible to the tasks in the first play. But, there is no reason the variable should be visible to the host two in the second play. The variable is not visible also to the same host in the third play because it hasn’t been stored in the hostvars aka "instantiated". The playbook below

    - hosts: one
      roles:
        - role-one
      tasks:
        - debug:
            var: variable_one
        - set_fact:
            variable_one: "{{ variable_one }}"
    
    - hosts: two
      roles:
        - role-two
    
    - hosts: one
      tasks:
        - debug:
            var: variable_one
    

    gives (abridged)

    PLAY [one] ************************************************
    
    TASK [role-one : debug] ****
    ok: [one] => 
      variable_one: Role One Variable
    
    TASK [debug] ****
    ok: [one] => 
      variable_one: Role One Variable
    
    TASK [set_fact] ****
    ok: [one]
    
    PLAY [two] ************************************************
    
    TASK [role-two : debug] ****
    ok: [two] => 
      variable_one: VARIABLE IS NOT DEFINED!
    
    PLAY [one] ************************************************
    
    TASK [debug] ****
    ok: [one] => 
      variable_one: Role One Variable
    

    Now, the variable is visible to the host one in the whole playbook and can be visible to other hosts using hostvars as well. For example, the playbook below

    - hosts: one
      roles:
        - role-one
      tasks:
        - debug:
            var: variable_one
        - set_fact:
            variable_one: "{{ variable_one }}"
    
    - hosts: two
      tasks:
        - set_fact:
            variable_one: "{{ hostvars.one.variable_one }}"
        - include_role:
            name: role-two
    

    gives (abridged)

    PLAY [one] ************************************************
    
    TASK [role-one : debug] ****
    ok: [one] => 
      variable_one: Role One Variable
    
    TASK [debug] ****
    ok: [one] => 
      variable_one: Role One Variable
    
    TASK [set_fact] ****
    ok: [one]
    
    PLAY [two] ************************************************
    
    TASK [set_fact] ****
    ok: [two]
    
    TASK [include_role : role-two] ****
    
    TASK [role-two : debug] ****
    ok: [two] => 
      variable_one: Role One Variable
    

    The problem with the above setting is that the host referencing hostvars is hardcoded. A better approach is to "instantiate" the variable in the first play for all hosts. For example, add a dummy task to the role

    shell> cat roles/role-one/tasks/noop.yml
    - meta: noop
    

    Then, in the first play, include all hosts, run_once import the role, run the dummy task only, and "instantiate" the variable for all hosts. For example

    - hosts: all
      tasks:
        - import_role:
            name: role-one
            tasks_from: noop.yml
          run_once: true
        - set_fact:
            variable_one: "{{ variable_one }}"
          run_once: true
    
    - hosts: two
      roles:
        - role-two
    
    - hosts: one
      roles:
        - role-two
    

    gives (abridged)

    PLAY [all] ************************************************
    
    TASK [set_fact] ****
    ok: [one]
    
    PLAY [two] ************************************************
    
    TASK [role-two : debug] ****
    ok: [two] => 
      variable_one: Role One Variable
    
    PLAY [one] ************************************************
    
    TASK [role-two : debug] ****
    ok: [one] => 
      variable_one: Role One Variable
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search