Repository: ansible/ansible

Issue:
Host pattern warnings when using templated target host
##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
Patterns

##### ANSIBLE VERSION
```
ansible 2.4.0 (devel ea7bff4a3f) last updated 2017/03/28 22:53:03 (GMT +200)
```

##### CONFIGURATION
N/A

##### OS / ENVIRONMENT
`Linux milkshake 4.9.0-2-amd64 #1 SMP Debian 4.9.13-1 (2017-02-27) x86_64 GNU/Linux`
`Python 2.7.13`

##### SUMMARY
Using a template variable as argument to `hosts` causes warnings. It seems like the attempt to recognize a pattern expression happens earlier than the variable substitution.

##### STEPS TO REPRODUCE
```yaml
- hosts: "{{ the_host }}"
  tasks:
    - debug:
        msg: "If there's something strange, in the neighborhood. Who you gonna call? bcoca!"
```
Call the playbook and pass the host (in my case my local machine as only entry in the inventory) as extra var
``` bash
ansible-playbook main.yml -e the_host=milkshake -i inventory
```

##### EXPECTED RESULTS
The playbook just runs without any warning and the task gets executed on the targeted host.

##### ACTUAL RESULTS
The playbook runs and the host is properly addressed (ignore the connection failure, that's expected and unrelated), however, ansible prints warnings for no apparent reason.
```
$ ansible-playbook main.yml -e the_host=milkshake -i inventory 
 [WARNING]: Could not match supplied host pattern, ignoring: {{

 [WARNING]: Could not match supplied host pattern, ignoring: the_host

 [WARNING]: Could not match supplied host pattern, ignoring: }}


PLAY [milkshake] **************************************************************************************************************************************************************************************************

TASK [Gathering Facts] ********************************************************************************************************************************************************************************************
fatal: [milkshake]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: Permission denied (publickey,password).\r\n", "unreachable": true}
```



Discussion:
I can confirm that this is also happening to me. However despite the message only the specified host(s) executes the plays!

BTW I am following a pattern that I found on the web to allow the hosts to be passed either as --extra-vars or from a prompt:

```
- name: ETLDeploy
  vars_prompt:
    - name: "hosts"
      prompt: |- 
                You can just press return for the default or enter a comma seperated
                list of machines with name in uppercase and domain in lower case
                (e.g. XXX.src.com or windowshosts)
                
                Which hosts would you like to setup?
      private: no
      default: "XXX.src.com"
  hosts: "{{hosts}}"
```
so issue here seems to be that we run 'getting hosts' x2, once before templating (you get warnings) and again after (when it works!).
currently fixed in #23001, mostly by changing how inventory is called, will look to see if i can get smaller change to do same

Resolving patch:
diff --git a/lib/ansible/template/__init__.py b/lib/ansible/template/__init__.py
--- a/lib/ansible/template/__init__.py
+++ b/lib/ansible/template/__init__.py
@@ -503,8 +503,8 @@ def is_template(self, data):
         ''' lets us know if data has a template'''
         if isinstance(data, string_types):
             try:
-                new = self.do_template(data)
-            except UndefinedError:
+                new = self.do_template(data, fail_on_undefined=True)
+            except (AnsibleUndefinedVariable, UndefinedError):
                 return True
             except:
                 return False
diff --git a/lib/ansible/vars/__init__.py b/lib/ansible/vars/__init__.py
--- a/lib/ansible/vars/__init__.py
+++ b/lib/ansible/vars/__init__.py
@@ -429,8 +429,13 @@ def _get_magic_variables(self, loader, play, host, task, include_hostvars, inclu
             variables['inventory_dir'] = self._inventory.basedir()
             variables['inventory_file'] = self._inventory.src()
             if play:
+                templar = Templar(loader=loader)
+                if templar.is_template(play.hosts):
+                    pattern = 'all'
+                else:
+                    pattern = play.hosts or 'all'
                 # add the list of hosts in the play, as adjusted for limit/filters
-                variables['ansible_play_hosts_all'] = [x.name for x in self._inventory.get_hosts(pattern=play.hosts or 'all', ignore_restrictions=True)]
+                variables['ansible_play_hosts_all'] = [x.name for x in self._inventory.get_hosts(pattern=pattern, ignore_restrictions=True)]
                 variables['ansible_play_hosts'] = [x for x in variables['ansible_play_hosts_all'] if x not in play._removed_hosts]
                 variables['ansible_play_batch'] = [x.name for x in self._inventory.get_hosts() if x.name not in play._removed_hosts]
 


Test patch: