Hi,

Encountered somewhat surprising behaviour regarding what happens when you 
enable smart fact gathering, have some plays using 'become: yes' at the top 
level along with using ansible_env['USER'] and ansible_env['HOME'].

I say surprising, because although it's clear what happens once you 
understand what's going on, it's not really "behaviour of least surprise" 
when creating playbooks and roles for maximum reuse.

*The Example*

Taking the following play:
--------------
- hosts: machine1
  tasks:
    - debug: var=ansible_user_id
    - debug: var=ansible_user_dir
    - debug: var=ansible_ssh_user
    - debug: var=ansible_env['USER']

- hosts: machine1
  become: yes
  tasks:
    - debug: var=ansible_user_id
    - debug: var=ansible_user_dir
    - debug: var=ansible_ssh_user
    - debug: var=ansible_env['USER']

- hosts: machine1
  tasks:
  - template:
      src: dump_variables.j2
      dest: /tmp/ansible_variables
  - fetch:
      src: /tmp/ansible_variables
      dest: "ansible_variables"

- hosts: machine1
  become: yes
  tasks:
  - template:
      src: dump_variables.j2
      dest: /tmp/ansible_variables
  - fetch:
      src: /tmp/ansible_variables
      dest: "become_ansible_variables"
--------------

dump_variables.j2 is:
--------------
HOSTVARS (ANSIBLE GATHERED, group_vars, host_vars) :

{{ hostvars[inventory_hostname] | to_yaml }}

PLAYBOOK VARS:

{{ vars | to_yaml }}
--------------

taken from 
https://groups.google.com/forum/#!msg/ansible-project/IVUwp9195Ek/HZ3QXvf6s1sJ.


When run with 'gathering = smart' in the ~/.ansible.cfg you get the same 
results for the each of the two sets of plays, all the debug variables 
print out the same and all of variables dumped are identical. But when you 
run with the default (comment out the gathering config setting) of 
implicit, you see the following output (condensed for convenience):

1: initial debug vars:
"ansible_user_id": "stack"
"ansible_user_dir": "/home/stack"
"ansible_ssh_user": "stack"
"ansible_env['USER']": "stack"

2: same debug vars with 'become: yes':
"ansible_user_id": "root"
"ansible_user_dir": "/root"
"ansible_ssh_user": "stack"
"ansible_env['USER']": "root"


--------------
--- ../../ansible/ansible_variables/deployer/tmp/ansible_variables    
2015-10-05 12:21:28.062786276 +0100
+++ 
../../ansible/become_ansible_variables/deployer/tmp/ansible_variables    
2015-10-05 12:21:29.138790179 +0100
@@ -18,9 +18,9 @@
 ansible_bios_version: Bochs
 ansible_cmdline: {BOOT_IMAGE: /boot/vmlinuz-3.14.51-1-amd64-hlinux, 
console: 'ttyS0,115200',
   nofb: true, nomodeset: true, ro: true, root: /dev/mapper/hlm--vg-root, 
vga: normal}
-ansible_date_time: {date: '2015-10-05', day: '05', epoch: '1444044087', 
hour: '11',
-  iso8601: '2015-10-05T11:21:27Z', iso8601_micro: 
'2015-10-05T11:21:27.257819Z', minute: '21',
-  month: '10', second: '27', time: '11:21:27', tz: UTC, tz_offset: 
'+0000', weekday: Monday,
+ansible_date_time: {date: '2015-10-05', day: '05', epoch: '1444044088', 
hour: '11',
+  iso8601: '2015-10-05T11:21:28Z', iso8601_micro: 
'2015-10-05T11:21:28.178812Z', minute: '21',
+  month: '10', second: '28', time: '11:21:28', tz: UTC, tz_offset: 
'+0000', weekday: Monday,
   year: '2015'}
 ansible_default_ipv4: {address: 192.168.121.40, alias: eth0, gateway: 
192.168.121.1,
   interface: eth0, macaddress: '52:54:00:3b:45:c4', mtu: 1500, netmask: 
255.255.255.0,
@@ -59,13 +59,16 @@
 ansible_distribution_release: cattleprod
 ansible_distribution_version: '8'
 ansible_domain: ''
-ansible_env: {HOME: /home/stack, LANG: C, LC_ADDRESS: en_IE.UTF-8, 
LC_COLLATE: en_US.UTF-8,
+ansible_env: {HOME: /root, LANG: C, LC_ADDRESS: en_IE.UTF-8, LC_COLLATE: 
en_US.UTF-8,
   LC_CTYPE: C, LC_IDENTIFICATION: en_IE.UTF-8, LC_MEASUREMENT: 
en_IE.UTF-8, LC_MESSAGES: en_US.UTF-8,
   LC_MONETARY: en_IE.UTF-8, LC_NAME: en_IE.UTF-8, LC_NUMERIC: en_IE.UTF-8, 
LC_PAPER: en_IE.UTF-8,
-  LC_TELEPHONE: en_IE.UTF-8, LC_TIME: en_IE.UTF-8, LOGNAME: stack, MAIL: 
/var/mail/stack,
-  PATH: '/usr/local/bin:/usr/bin:/bin:/usr/games', PWD: /home/stack, 
SHELL: /bin/bash,
-  SHLVL: '1', SSH_CLIENT: 192.168.121.1 37076 22, SSH_CONNECTION: 
192.168.121.1 37076
-    192.168.121.40 22, SSH_TTY: /dev/pts/0, TERM: xterm, USER: stack, _: 
/bin/sh}
+  LC_TELEPHONE: en_IE.UTF-8, LC_TIME: en_IE.UTF-8, LOGNAME: root, MAIL: 
/var/mail/root,
+  PATH: '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', 
PWD: /home/stack,
+  SHELL: /bin/bash, SUDO_COMMAND: /bin/sh -c echo 
BECOME-SUCCESS-rezdjrduclnbktzmvdjusxutxpbrncak;
+    LANG=en_US.UTF-8 LC_CTYPE=en_US.UTF-8 /usr/bin/python 
/home/stack/.ansible/tmp/ansible-tmp-1444044088.08-205236105695791/setup;
+    rm -rf 
/home/stack/.ansible/tmp/ansible-tmp-1444044088.08-205236105695791/ 
>/dev/null
+    2>&1, SUDO_GID: '1001', SUDO_UID: '1001', SUDO_USER: stack, TERM: 
xterm, USER: root,
+  USERNAME: root}
 ansible_eth0:
   active: true
   device: eth0
@@ -154,12 +157,12 @@
 ansible_swaptotal_mb: 0
 ansible_system: Linux
 ansible_system_vendor: QEMU
-ansible_user_dir: /home/stack
-ansible_user_gecos: ''
-ansible_user_gid: 1001
-ansible_user_id: stack
+ansible_user_dir: /root
+ansible_user_gecos: root
+ansible_user_gid: 0
+ansible_user_id: root
 ansible_user_shell: /bin/bash
-ansible_user_uid: 1001
+ansible_user_uid: 0
 ansible_userspace_architecture: x86_64
 ansible_userspace_bits: '64'
 ansible_virtualization_role: guest
--------------




*The Problem*Where this starts to cause issues, is writing out config files 
that need to contain absolute paths for the target user the task is 
executed for. If we use 'gathering: smart', we end up getting the wrong 
information, or needing to add additional tasks to extract the correct user 
name and home by inspecting the results of echoing variables on the remote.


Working around by using 'gather_facts: True' on the play or adding a 
'setup' task to the start, will reset the variables for all other 
subsequent plays in the site.yml. This means you end up adding a post play 
to call 'gather_facts: True' where become is not set, so that there are no 
unexpected surprises for subsequent plays in site.yml expecting ansible_env 
to reflect that of the remote user instead of the sudo user. This starts to 
negate some of the benefits of smart fact gathering though.


There simply doesn't appear to be any variables that correctly reflect the 
user a task is being executed under, so even we move the "become: yes" to 
be on the role or on each task (on the include more likely), there is no 
variable that is guaranteed to contain the username and home directory of 
the user executing the task.

As mentioned, for many modules, we can use '~' and it'll be correctly 
expanded, but doesn't work so well for configuration files where we need to 
insert the correct value and '~' may not be expanded by the using 
application.

Problem appears a little works for the username, although it does appear as 
though we could use the following hack to get the remote user in cases the 
play may be run by root where most tasks require root but we want to set 
ownership of files/directories to the remote user without hardcoding the 
username:

  {{ ansible_env['SUDO_USER'] | default(ansible_env['USER']) }}

Which should also work for writing such a value to a config file.

Picking up the remote user home directory where we need to perform a task 
as a sudo user but use this value seems more troublesome though we can use 
the following:

  {{ ansible_env['PWD'] }}


Both of these though feel like hacks, the alternative of using a shell 
command and register also feels somewhat clunky.


*It'd be nice..*


It really seems to me that it would be easier to make roles and plays that 
make use of become more reusable if there was a standard pattern that could 
would get consistent results no matter where "become: yes" is placed (task, 
include, role or play) when running with smart fact gathering.


Maybe, 'ansible_task_env' which always reflects the environment of the user 
that a task is executed under. Though that may not work with filters, I'm 
not sure when ansible resolves the final value of variables.


Alternatively a set of user variables

ansible_become_user_dir:
ansible_become_user_gecos:
ansible_become_user_gid:
ansible_become_user_id:
ansible_become_user_shell:
ansible_become_user_uid:

ansible_remote_user_dir:
ansible_remote_user_gecos:
ansible_remote_user_gid:
ansible_remote_user_id:
ansible_remote_user_shell:
ansible_remote_user_uid:


I surmise this could be implemented via a custom module, but it feels like 
something that should be part of the default fact gathering to remove some 
ambiguity around remote and become user for smart fact gathering.


Unless of course this is already changed in v2, or there is a standard 
pattern that I should be following and recommending to other users of 
ansible locally besides just either don't use smart gathering or don't use 
'become: yes' on top level plays.

-- 
Darragh Bailey
"Nothing is foolproof to a sufficiently talented fool"

-- 
You received this message because you are subscribed to the Google Groups 
"Ansible Project" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/ansible-project/680500b4-fc69-4c31-942f-0f7336a3e4b0%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to