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.