Repository: ansible/ansible

Issue:
'ansible_user' is undefined
##### ISSUE TYPE
<!--- Pick one below and delete the rest: -->
 - Bug Report

##### COMPONENT NAME
ansible

##### ANSIBLE VERSION
```
ansible 2.3.0.0
  config file =
  configured module search path = Default w/o overrides
  python version = 2.7.12 (default, Nov 19 2016, 06:48:10) [GCC 5.4.0 20160609]
```

##### CONFIGURATION
vanilla vagrant box, ubuntu/xenial64

##### OS / ENVIRONMENT
Only tested ubuntu/xenial64

##### SUMMARY
`ansible_user` seems to be undefined.

##### STEPS TO REPRODUCE
Just fire up a vagrant box, install ansible via pip and check the ansible_user
```
ubuntu@ubuntu-xenial:~$ pip install ansible
Collecting ansible
  Downloading ansible-2.3.0.0.tar.gz (4.3MB)
    100% |████████████████████████████████| 4.3MB 320kB/s
Collecting jinja2 (from ansible)
  Using cached Jinja2-2.9.6-py2.py3-none-any.whl
Collecting PyYAML (from ansible)
Collecting paramiko (from ansible)
  Using cached paramiko-2.1.2-py2.py3-none-any.whl
Collecting pycrypto>=2.6 (from ansible)
Collecting setuptools (from ansible)
  Using cached setuptools-34.4.1-py2.py3-none-any.whl
Collecting MarkupSafe>=0.23 (from jinja2->ansible)
Collecting pyasn1>=0.1.7 (from paramiko->ansible)
  Using cached pyasn1-0.2.3-py2.py3-none-any.whl
Collecting cryptography>=1.1 (from paramiko->ansible)
Collecting appdirs>=1.4.0 (from setuptools->ansible)
  Using cached appdirs-1.4.3-py2.py3-none-any.whl
Collecting packaging>=16.8 (from setuptools->ansible)
  Using cached packaging-16.8-py2.py3-none-any.whl
Collecting six>=1.6.0 (from setuptools->ansible)
  Using cached six-1.10.0-py2.py3-none-any.whl
Collecting ipaddress (from cryptography>=1.1->paramiko->ansible)
  Using cached ipaddress-1.0.18-py2-none-any.whl
Collecting idna>=2.1 (from cryptography>=1.1->paramiko->ansible)
  Using cached idna-2.5-py2.py3-none-any.whl
Collecting asn1crypto>=0.21.0 (from cryptography>=1.1->paramiko->ansible)
  Using cached asn1crypto-0.22.0-py2.py3-none-any.whl
Collecting enum34 (from cryptography>=1.1->paramiko->ansible)
  Using cached enum34-1.1.6-py2-none-any.whl
Collecting cffi>=1.4.1 (from cryptography>=1.1->paramiko->ansible)
  Using cached cffi-1.10.0-cp27-cp27mu-manylinux1_x86_64.whl
Collecting pyparsing (from packaging>=16.8->setuptools->ansible)
  Using cached pyparsing-2.2.0-py2.py3-none-any.whl
Collecting pycparser (from cffi>=1.4.1->cryptography>=1.1->paramiko->ansible)
Building wheels for collected packages: ansible
  Running setup.py bdist_wheel for ansible ... done
  Stored in directory: /home/ubuntu/.cache/pip/wheels/9e/36/1f/9281addf43ee457e2847b021ba41a5641de9c7f7f1aa68a5d5
Successfully built ansible
Installing collected packages: MarkupSafe, jinja2, PyYAML, pyasn1, appdirs, pyparsing, six, packaging, setuptools, ipaddress, idna, asn1crypto, enum34, pycparser, cffi, cryptography, paramiko, pycrypto, ansible
Successfully installed MarkupSafe-1.0 PyYAML-3.12 ansible-2.2.0.0 appdirs-1.4.3 asn1crypto-0.22.0 cffi-1.10.0 cryptography-1.8.1 enum34-1.1.6 idna-2.5 ipaddress-1.0.18 jinja2-2.9.6 packaging-16.8 paramiko-2.1.2 pyasn1-0.2.3 pycparser-2.17 pycrypto-2.6.1 pyparsing-2.2.0 setuptools-34.4.1 six-1.10.0
You are using pip version 8.1.1, however version 9.0.1 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.
ubuntu@ubuntu-xenial:~$ ansible all -i 'localhost, ' -c local -m debug -a 'var=ansible_user'
localhost | SUCCESS => {
    "ansible_user": "VARIABLE IS NOT DEFINED!",
    "changed": false
}
```

##### EXPECTED RESULTS
```
localhost | SUCCESS => {
    "ansible_user": "ubuntu"
}
```

##### ACTUAL RESULTS
```
localhost | SUCCESS => {
    "ansible_user": "VARIABLE IS NOT DEFINED!",
    "changed": false
}
```

This works on 2.2.0.0

```
ubuntu@ubuntu-xenial:~$ ansible all -i 'localhost, ' -c local -m debug -a 'var=ansible_user'
localhost | SUCCESS => {
    "ansible_user": "VARIABLE IS NOT DEFINED!",
    "changed": false
}
ubuntu@ubuntu-xenial:~$ pip install ansible==2.2.0.0
Collecting ansible==2.2.0.0
Collecting PyYAML (from ansible==2.2.0.0)
Collecting setuptools (from ansible==2.2.0.0)
  Using cached setuptools-34.4.1-py2.py3-none-any.whl
Collecting jinja2 (from ansible==2.2.0.0)
  Using cached Jinja2-2.9.6-py2.py3-none-any.whl
Collecting paramiko (from ansible==2.2.0.0)
  Using cached paramiko-2.1.2-py2.py3-none-any.whl
Collecting pycrypto>=2.6 (from ansible==2.2.0.0)
Collecting appdirs>=1.4.0 (from setuptools->ansible==2.2.0.0)
  Using cached appdirs-1.4.3-py2.py3-none-any.whl
Collecting packaging>=16.8 (from setuptools->ansible==2.2.0.0)
  Using cached packaging-16.8-py2.py3-none-any.whl
Collecting six>=1.6.0 (from setuptools->ansible==2.2.0.0)
  Using cached six-1.10.0-py2.py3-none-any.whl
Collecting MarkupSafe>=0.23 (from jinja2->ansible==2.2.0.0)
Collecting pyasn1>=0.1.7 (from paramiko->ansible==2.2.0.0)
  Using cached pyasn1-0.2.3-py2.py3-none-any.whl
Collecting cryptography>=1.1 (from paramiko->ansible==2.2.0.0)
Collecting pyparsing (from packaging>=16.8->setuptools->ansible==2.2.0.0)
  Using cached pyparsing-2.2.0-py2.py3-none-any.whl
Collecting ipaddress (from cryptography>=1.1->paramiko->ansible==2.2.0.0)
  Using cached ipaddress-1.0.18-py2-none-any.whl
Collecting idna>=2.1 (from cryptography>=1.1->paramiko->ansible==2.2.0.0)
  Using cached idna-2.5-py2.py3-none-any.whl
Collecting asn1crypto>=0.21.0 (from cryptography>=1.1->paramiko->ansible==2.2.0.0)
  Using cached asn1crypto-0.22.0-py2.py3-none-any.whl
Collecting enum34 (from cryptography>=1.1->paramiko->ansible==2.2.0.0)
  Using cached enum34-1.1.6-py2-none-any.whl
Collecting cffi>=1.4.1 (from cryptography>=1.1->paramiko->ansible==2.2.0.0)
  Using cached cffi-1.10.0-cp27-cp27mu-manylinux1_x86_64.whl
Collecting pycparser (from cffi>=1.4.1->cryptography>=1.1->paramiko->ansible==2.2.0.0)
Installing collected packages: PyYAML, appdirs, pyparsing, six, packaging, setuptools, MarkupSafe, jinja2, pyasn1, ipaddress, idna, asn1crypto, enum34, pycparser, cffi, cryptography, paramiko, pycrypto, ansible
Successfully installed MarkupSafe-1.0 PyYAML-3.12 ansible-2.2.0.0 appdirs-1.4.3 asn1crypto-0.22.0 cffi-1.10.0 cryptography-1.8.1 enum34-1.1.6 idna-2.5 ipaddress-1.0.18 jinja2-2.9.6 packaging-16.8 paramiko-2.1.2 pyasn1-0.2.3 pycparser-2.17 pycrypto-2.6.1 pyparsing-2.2.0 setuptools-34.4.1 six-1.10.0
You are using pip version 8.1.1, however version 9.0.1 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.
ubuntu@ubuntu-xenial:~$ ansible all -i 'localhost, ' -c local -m debug -a 'var=ansible_user'
localhost | SUCCESS => {
    "ansible_user": "ubuntu"
}
```


Discussion:
This isn't a bug. In the absence of a specified user, either by `-u`, config, inventory, or play keywords, each connection plugin is free to interpret who the current user is in a way that makes sense. Most choose the user running ansible, but WinRM with kerberos (for example) will use the ambient TGT if one is found. So we can't sanely set a global default value for `ansible_user` without breaking things.
i see your point, but the same command `ansible all -i 'localhost, ' -c local -m debug -a 'var=ansible_user'` seem to have changed behaviour on two different versions of ansible and i didn't see anything mentioned.
The behaviour change for debug was not intended, but also not something we are looking at. `ansible_user` is a variable the user sets for the connection to consume, not the other way around. 

Also note that 'debug' has no connection as it is a special action that does not require one, like add_hosts, group_by, pause, etc. These are actions that affect the data or display of Ansible itself, not the target hosts.
its not only happening in the `debug` module.

```
ubuntu@ubuntu-xenial:~$ cat test.yml
---
  - hosts: all

    tasks:
      - name: touch ansible_user file
        file:
          path: /tmp/{{ ansible_user }}
          state: touch
ubuntu@ubuntu-xenial:~$ !ans
ansible-playbook test.yml  -i 'localhost, '

PLAY [all] *********************************************************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *********************************************************************************************************************************************************************************************************************************************************
ok: [localhost]

TASK [touch ansible_user file] *************************************************************************************************************************************************************************************************************************************************
fatal: [localhost]: FAILED! => {"failed": true, "msg": "the field 'args' has an invalid value, which appears to include a variable that is undefined. The error was: 'ansible_user' is undefined\n\nThe error appears to have been in '/home/ubuntu/test.yml': line 5, column 9, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n    tasks:\n      - name: touch ansible_user file\n        ^ here\n"}
	to retry, use: --limit @/home/ubuntu/test.retry

PLAY RECAP *********************************************************************************************************************************************************************************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=1

ubuntu@ubuntu-xenial:~$ ansible --version
ansible 2.3.0.0
  config file =
  configured module search path = Default w/o overrides
  python version = 2.7.12 (default, Nov 19 2016, 06:48:10) [GCC 5.4.0 20160609]
ubuntu@ubuntu-xenial:~$
```
@bcoca : I Agree with @mhristof here.
The confusing part would be: http://docs.ansible.com/ansible/intro_configuration.html#remote-user

If nothing is set, we expect this to be from the env. Here it just fails. Either we need a doc change, or a change the behavior.
I'm starting to see this creeping in from a lot of different places actually—I've been getting some error reports in certain environments where playbooks were working great and all the sudden are not, and I just experienced it in some of my Travis CI builds, where I'm getting `The error was: 'ansible_user' is undefined`.

See some excellent sleuthing by @oxyc here: https://github.com/geerlingguy/drupal-vm/issues/1302#issuecomment-295090614

Basically, I have to ask... if we can't use `ansible_user` (and `ansible_ssh_user` disappeared post-2.0)—is there any variable that we can use that contains the current `$USER`, or should I start rewriting things to expect that `ansible_user` may or may not be populated?

I haven't had an issue from 2.0 -> 2.2.x, but now that 2.3.0 is here it pops up in some environments but not others.
Additionally, if I pass something like `-u username` it still gives me the error :/
@geerlingguy we started to move to ansible_user_id, but hell who know's what's going to happen on next release.
`ansible_ssh_user` did not go away, its just the 'ssh connection plugin specific' version of `ansible_user`, `ansible_user_id` is a result of fact gathering, which has different implications (also see become interactions).

I suspect the break came here  58ee661437f123fdd8cf516bac053fe593910ced
I would say ansible_user can potentially be none (there is no builtin default but usually remote_user ansible_ssh_user etc provide it) for the reasons @nitzmahone mentioned.

for the case here

 ```
ubuntu@ubuntu-xenial:~$ ansible all -i 'localhost, ' -c local -m debug -a 'var=ansible_user'
localhost | SUCCESS => {
    "ansible_user": "VARIABLE IS NOT DEFINED!",
    "changed": false
}
``` 

It seems like it is a bug that is not being set.
reverting the commit above 'fixes it'

Resolving patch:
diff --git a/lib/ansible/playbook/play_context.py b/lib/ansible/playbook/play_context.py
--- a/lib/ansible/playbook/play_context.py
+++ b/lib/ansible/playbook/play_context.py
@@ -439,11 +439,9 @@ def set_task_and_variable_override(self, task, variables, templar):
                 elif getattr(new_info, 'connection', None) == 'local' and (not remote_addr_local or not inv_hostname_local):
                     setattr(new_info, 'connection', C.DEFAULT_TRANSPORT)
 
-        # if the final connection type is local, reset the remote_user value
-        # to that of the currently logged in user, to ensure any become settings
-        # are obeyed correctly
-        # additionally, we need to do this check after final connection has been
-        # correctly set above ...
+        # if the final connection type is local, reset the remote_user value to that of the currently logged in user
+        # this ensures any become settings are obeyed correctly
+        # we store original in 'connection_user' for use of network/other modules that fallback to it as login user
         if new_info.connection == 'local':
             new_info.connection_user = new_info.remote_user
             new_info.remote_user = pwd.getpwuid(os.getuid()).pw_name
@@ -607,10 +605,6 @@ def update_vars(self, variables):
                 if 'become' in prop:
                     continue
 
-                # perserves the user var for local connections
-                if self.connection == 'local' and 'remote_user' in prop:
-                    continue
-
                 var_val = getattr(self, prop)
                 for var_opt in var_list:
                     if var_opt not in variables and var_val is not None:


Test patch: