<p dir="auto"><strong>Issue Type:</strong>  “Bug Report”</p>
<p dir="auto"><strong>Ansible Version</strong>: ansible 1.7.2</p>
<p dir="auto"><strong>Environment:</strong> Fedora/Centos</p>
<p dir="auto"><strong>Summary:</strong></p>
<p dir="auto">In order to configure two servers (backend &amp; frontend) I need to expose the backend variables in order to configure the frontend playbook.  After that ansible-playbook is run and no variables defined in group_vars, it doesn't expand the nested variables.</p>
<p dir="auto"><strong>Steps To Reproduce:</strong></p>
<p dir="auto"><em>Create group_vars/all</em></p>
<div class="highlight highlight-source-yaml notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="backend_version: 2.0.0"><pre class="notranslate"><span class="pl-ent">backend_version</span>: <span class="pl-s">2.0.0</span></pre></div>
<p dir="auto"><em>Create group_vars/backend</em></p>
<div class="highlight highlight-source-yaml notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="backend_version: 2.3.0
backend_foldername: application
backend_name: &quot;{{ backend_foldername }}-{{ backend_version }}&quot;"><pre class="notranslate"><span class="pl-ent">backend_version</span>: <span class="pl-s">2.3.0</span>
<span class="pl-ent">backend_foldername</span>: <span class="pl-s">application</span>
<span class="pl-ent">backend_name</span>: <span class="pl-s"><span class="pl-pds">"</span>{{ backend_foldername }}-{{ backend_version }}<span class="pl-pds">"</span></span></pre></div>
<p dir="auto"><em>Create hosts</em></p>
<div class="highlight highlight-source-yaml notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="[frontend]
test-server1 backend_server=test-server2 
[backend]
test-server2 "><pre class="notranslate"><span class="pl-s">[frontend]</span>
<span class="pl-s">test-server1 backend_server=test-server2 </span>
<span class="pl-s">[backend]</span>
<span class="pl-s">test-server2 </span></pre></div>
<p dir="auto"><em>Create site.yml</em></p>
<div class="highlight highlight-source-yaml notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="
---
- name: Configure and deploy Frontend
  hosts: frontend
  roles:
    - frontend

- name: Configure and deploy Backend
  hosts: backend
  roles:
    - backend"><pre class="notranslate">---
- <span class="pl-ent">name</span>: <span class="pl-s">Configure and deploy Frontend</span>
  <span class="pl-ent">hosts</span>: <span class="pl-s">frontend</span>
  <span class="pl-ent">roles</span>:
    - <span class="pl-s">frontend</span>

- <span class="pl-ent">name</span>: <span class="pl-s">Configure and deploy Backend</span>
  <span class="pl-ent">hosts</span>: <span class="pl-s">backend</span>
  <span class="pl-ent">roles</span>:
    - <span class="pl-s">backend</span></pre></div>
<p dir="auto"><em>Create role/frontend/tasks/main.yml</em></p>
<div class="highlight highlight-source-yaml notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="
---
- name: Does expand properties
  debug: msg={{ hostvars[item]['backend_version'] }}
  with_items: backend_server

- name: Does not expand nested properties
  debug: msg={{ hostvars[item]['backend_name'] }}
  with_items: backend_server"><pre class="notranslate">---
- <span class="pl-ent">name</span>: <span class="pl-s">Does expand properties</span>
  <span class="pl-ent">debug</span>: <span class="pl-s">msg={{ hostvars[item]['backend_version'] }}</span>
  <span class="pl-ent">with_items</span>: <span class="pl-s">backend_server</span>

- <span class="pl-ent">name</span>: <span class="pl-s">Does not expand nested properties</span>
  <span class="pl-ent">debug</span>: <span class="pl-s">msg={{ hostvars[item]['backend_name'] }}</span>
  <span class="pl-ent">with_items</span>: <span class="pl-s">backend_server</span></pre></div>
<p dir="auto"><strong>Results:</strong></p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="PLAY [Configure and deploy Frontend] **************** 

GATHERING FACTS *************************************************************** 
ok: [test-server1]

TASK: [frontend | Does expand properties] ***************************************************** 
ok: [test-server1] =&gt; (item=test-server2) =&gt; {
    &quot;item&quot;: &quot;test-server2&quot;, 
    &quot;msg&quot;: &quot; 2.3.0&quot;
}

TASK: [frontend | Does not expand nested properties] ***************************************************** 
ok: [test-server1] =&gt; (item=test-server2) =&gt; {
    &quot;item&quot;: &quot;test-server2&quot;, 
    &quot;msg&quot;: &quot;{{adaptersuite_foldername}}-{{adaptersuite_version}}&quot;
}"><pre class="notranslate"><code class="notranslate">PLAY [Configure and deploy Frontend] **************** 

GATHERING FACTS *************************************************************** 
ok: [test-server1]

TASK: [frontend | Does expand properties] ***************************************************** 
ok: [test-server1] =&gt; (item=test-server2) =&gt; {
    "item": "test-server2", 
    "msg": " 2.3.0"
}

TASK: [frontend | Does not expand nested properties] ***************************************************** 
ok: [test-server1] =&gt; (item=test-server2) =&gt; {
    "item": "test-server2", 
    "msg": "{{adaptersuite_foldername}}-{{adaptersuite_version}}"
}
</code></pre></div>
<p dir="auto"><strong>Expected Results:</strong></p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="PLAY [Configure and deploy Frontend] **************** 

GATHERING FACTS *************************************************************** 
ok: [test-server1]

TASK: [frontend | Does expand properties] ***************************************************** 
ok: [test-server1] =&gt; (item=test-server2) =&gt; {
    &quot;item&quot;: &quot;test-server2&quot;, 
    &quot;msg&quot;: &quot; 2.3.0&quot;
}

TASK: [frontend | Does not expand nested properties] ***************************************************** 
ok: [test-server1] =&gt; (item=test-server2) =&gt; {
    &quot;item&quot;: &quot;test-server2&quot;, 
    &quot;msg&quot;: &quot; 2.3.0-application&quot;
}"><pre class="notranslate"><code class="notranslate">PLAY [Configure and deploy Frontend] **************** 

GATHERING FACTS *************************************************************** 
ok: [test-server1]

TASK: [frontend | Does expand properties] ***************************************************** 
ok: [test-server1] =&gt; (item=test-server2) =&gt; {
    "item": "test-server2", 
    "msg": " 2.3.0"
}

TASK: [frontend | Does not expand nested properties] ***************************************************** 
ok: [test-server1] =&gt; (item=test-server2) =&gt; {
    "item": "test-server2", 
    "msg": " 2.3.0-application"
}
</code></pre></div>
<p dir="auto">It doesn't expand those nested properties, likely because they aren't part of the visibility of the test-server1. But I guess just using {{ hostvars[item]['backend_name'] }} it will expand those properties...</p>
<p dir="auto">Any ideas?</p>
<h5 dir="auto">Issue Type: Bug Report</h5>
<h5 dir="auto">Ansible Version: ansible 1.7 (devel <a class="commit-link" data-hovercard-type="commit" data-hovercard-url="https://github.com/ansible/ansible/commit/d0dd4ac10c11cafcb143071cff945e1b27838f86/hovercard" href="https://github.com/ansible/ansible/commit/d0dd4ac10c11cafcb143071cff945e1b27838f86"><tt>d0dd4ac</tt></a>)</h5>
<h5 dir="auto">Environment: N/A</h5>
<h5 dir="auto">Summary:</h5>
<p dir="auto">Values comming from hostvars should go through similar variable expansion process as all the others.</p>
<h5 dir="auto">Steps To Reproduce:</h5>
<p dir="auto">$ ansible-playbook -i vars_inv hostvars_example.yml</p>
<p dir="auto">vars_inv:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="localhost ansible_connection=local varA=&quot;{{ some_var }}&quot;
otherhost ansible_connection=local varA=&quot;{{ varB }}&quot; varB=&quot;{{ other_var }}&quot;"><pre class="notranslate"><code class="notranslate">localhost ansible_connection=local varA="{{ some_var }}"
otherhost ansible_connection=local varA="{{ varB }}" varB="{{ other_var }}"
</code></pre></div>
<p dir="auto">hostvars_example.yml:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="- hosts: all
  gather_facts: no
  vars:
    some_var: &quot;some value&quot;
    other_var: &quot;other value&quot;
  tasks:
  - debug: &quot;msg='localhost: {{ hostvars['localhost'].varA }}'&quot;
  - debug: &quot;msg='otherhost: {{ hostvars['otherhost'].varA }}'&quot;"><pre class="notranslate"><code class="notranslate">- hosts: all
  gather_facts: no
  vars:
    some_var: "some value"
    other_var: "other value"
  tasks:
  - debug: "msg='localhost: {{ hostvars['localhost'].varA }}'"
  - debug: "msg='otherhost: {{ hostvars['otherhost'].varA }}'"
</code></pre></div>
<h5 dir="auto">Expected Results:</h5>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="TASK: [debug msg='localhost: {{hostvars['localhost'].varA}}'] ***************** 
ok: [localhost] =&gt; {
    &quot;msg&quot;: &quot;localhost: some value&quot;
}
ok: [otherhost] =&gt; {
    &quot;msg&quot;: &quot;localhost: some value&quot;
}

TASK: [debug msg='otherhost: {{hostvars['otherhost'].varA}}'] ***************** 
ok: [localhost] =&gt; {
    &quot;msg&quot;: &quot;otherhost: other value&quot;
}
ok: [otherhost] =&gt; {
    &quot;msg&quot;: &quot;otherhost: other value&quot;
}"><pre class="notranslate"><code class="notranslate">TASK: [debug msg='localhost: {{hostvars['localhost'].varA}}'] ***************** 
ok: [localhost] =&gt; {
    "msg": "localhost: some value"
}
ok: [otherhost] =&gt; {
    "msg": "localhost: some value"
}

TASK: [debug msg='otherhost: {{hostvars['otherhost'].varA}}'] ***************** 
ok: [localhost] =&gt; {
    "msg": "otherhost: other value"
}
ok: [otherhost] =&gt; {
    "msg": "otherhost: other value"
}
</code></pre></div>
<h5 dir="auto">Actual Results:</h5>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="TASK: [debug msg='localhost: {{hostvars['localhost'].varA}}'] ***************** 
ok: [localhost] =&gt; {
    &quot;msg&quot;: &quot;localhost: {{some_var}}&quot;
}
ok: [otherhost] =&gt; {
    &quot;msg&quot;: &quot;localhost: {{some_var}}&quot;
}

TASK: [debug msg='otherhost: {{hostvars['otherhost'].varA}}'] ***************** 
ok: [localhost] =&gt; {
    &quot;msg&quot;: &quot;otherhost: {{varB}}&quot;
}
ok: [otherhost] =&gt; {
    &quot;msg&quot;: &quot;otherhost: {{varB}}&quot;
}"><pre class="notranslate"><code class="notranslate">TASK: [debug msg='localhost: {{hostvars['localhost'].varA}}'] ***************** 
ok: [localhost] =&gt; {
    "msg": "localhost: {{some_var}}"
}
ok: [otherhost] =&gt; {
    "msg": "localhost: {{some_var}}"
}

TASK: [debug msg='otherhost: {{hostvars['otherhost'].varA}}'] ***************** 
ok: [localhost] =&gt; {
    "msg": "otherhost: {{varB}}"
}
ok: [otherhost] =&gt; {
    "msg": "otherhost: {{varB}}"
}
</code></pre></div>
<h5 dir="auto">Other notes:</h5>
<p dir="auto">The expected result above was created with help of the patch below.<br>
I'm afraid it's not usable as is, that's why I'm submitting this as an issue and not a PR - if you think otherwise I'll happily resubmit it as one</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="diff --git a/lib/ansible/utils/template.py b/lib/ansible/utils/template.py
index 7848dd1..47772c6 100644
--- a/lib/ansible/utils/template.py
+++ b/lib/ansible/utils/template.py
@@ -139,6 +139,42 @@ def template(basedir, varname, vars, lookup_fatal=True, depth=0, expand_lists=Tr
             return varname


+class _jinja2_host_vars(object):
+    '''Wrapper around HostVars to make sure they are templated properly'''
+    def __init__(self, hostvars, vars):
+        self.vars = vars
+        self.hostvars = hostvars
+
+    def __contains__(self, k):
+        return k in self.hostvars
+
+    def __getitem__(self, hostname):
+        return _jinja2_vars(
+            self.vars.basedir,
+            chained_dicts(self.hostvars[hostname], self.vars),
+            self.vars.globals,
+            self.vars.fail_on_undefined)
+
+
+class chained_dicts(object):
+    def __init__(self, *dicts):
+        self.dicts = dicts
+
+    def __contains__(self, k):
+        return any(k in d for d in self.dicts)
+
+    def __getitem__(self, k):
+        for d in self.dicts[:-1]:
+            try:
+                return d[k]
+            except KeyError:
+                pass
+        return self.dicts[-1][k]
+
+    def keys(self):
+        return sorted({k for d in self.dicts for k in d.keys()})
+
+
 class _jinja2_vars(object):
     '''
     Helper class to template all variable content before jinja2 sees it.
@@ -179,7 +215,7 @@ class _jinja2_vars(object):
         var = self.vars[varname]
         # HostVars is special, return it as-is
         if isinstance(var, dict) and type(var) != dict:
-            return var
+            return _jinja2_host_vars(var, self)
         else:
             return template(self.basedir, var, self.vars, fail_on_undefined=self.fail_on_undefined)"><pre class="notranslate"><code class="notranslate">diff --git a/lib/ansible/utils/template.py b/lib/ansible/utils/template.py
index 7848dd1..47772c6 100644
--- a/lib/ansible/utils/template.py
+++ b/lib/ansible/utils/template.py
@@ -139,6 +139,42 @@ def template(basedir, varname, vars, lookup_fatal=True, depth=0, expand_lists=Tr
             return varname


+class _jinja2_host_vars(object):
+    '''Wrapper around HostVars to make sure they are templated properly'''
+    def __init__(self, hostvars, vars):
+        self.vars = vars
+        self.hostvars = hostvars
+
+    def __contains__(self, k):
+        return k in self.hostvars
+
+    def __getitem__(self, hostname):
+        return _jinja2_vars(
+            self.vars.basedir,
+            chained_dicts(self.hostvars[hostname], self.vars),
+            self.vars.globals,
+            self.vars.fail_on_undefined)
+
+
+class chained_dicts(object):
+    def __init__(self, *dicts):
+        self.dicts = dicts
+
+    def __contains__(self, k):
+        return any(k in d for d in self.dicts)
+
+    def __getitem__(self, k):
+        for d in self.dicts[:-1]:
+            try:
+                return d[k]
+            except KeyError:
+                pass
+        return self.dicts[-1][k]
+
+    def keys(self):
+        return sorted({k for d in self.dicts for k in d.keys()})
+
+
 class _jinja2_vars(object):
     '''
     Helper class to template all variable content before jinja2 sees it.
@@ -179,7 +215,7 @@ class _jinja2_vars(object):
         var = self.vars[varname]
         # HostVars is special, return it as-is
         if isinstance(var, dict) and type(var) != dict:
-            return var
+            return _jinja2_host_vars(var, self)
         else:
             return template(self.basedir, var, self.vars, fail_on_undefined=self.fail_on_undefined)
</code></pre></div>