ansible.posix
ansible.posix copied to clipboard
synchronize doesn't parse variables for ansible_user
SUMMARY
When using variable in ansible_user
, and the default value set_remote_user=true
, then we got error:
group_vars/all/default.yml
deploy_user: ubuntu
ansible_user: '{{ deploy_user }}'
task.yml
- name: ANSISTRANO | RSYNC DIRECT | Rsync application files directly to remote release path
synchronize:
src: "{{ ansistrano_deploy_from }}"
dest: "{{ ansistrano_release_path.stdout }}"
set_remote_user: yes
recursive: yes
delete: yes
archive: yes
compress: yes
use_ssh_args: "{{ ansistrano_rsync_use_ssh_args | default(omit) }}"
rsync_opts: "{{ ansistrano_rsync_extra_params | default(omit) }}"
rsync_path: "{{ ansistrano_rsync_path | default(omit) }}"
Output (added line breaks):
fatal: [dev_host_site]: FAILED! => {"changed": false, "cmd":
"/usr/bin/rsync --delay-updates -F --compress --delete-after --archive
--rsh='/usr/bin/ssh -S none -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null'
--no-perms --no-g --chmod=ugo=rwX --omit-dir-times --exclude=/infra/vault_pass.txt
--quiet --out-format='<<CHANGED>>%i %n%L'
/srv/Projects/site/infra/..
{{ deploy_user }}@84.84.84.84:/srv/site-dev/releases/20210930114615Z",
"msg": "Warning: Permanently added '84.84.84.84' (ECDSA) to the list of known hosts.\r\n
}}@84.84.84.84: Permission denied (publickey).\r\n
rsync: connection unexpectedly closed (0 bytes received so far) [sender]\n
rsync error: unexplained error (code 255) at io.c(235) [sender=3.1.3]\n", "rc": 255}
As you can see, text {{ deploy_user }}
inserted as is, even with spaces, and so SSH login }}
is used.
ISSUE TYPE
- Bug Report
COMPONENT NAME
synchronize
ANSIBLE VERSION
ansible [core 2.11.5]
config file = /srv/Projects/Examples/ansistrano-deploy/ansible.cfg
configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /opt/pipx/venvs/ansible/lib/python3.9/site-packages/ansible
ansible collection location = /root/.ansible/collections:/usr/share/ansible/collections
executable location = /opt/pipx/bin/ansible
python version = 3.9.5 (default, May 19 2021, 11:32:47) [GCC 9.3.0]
jinja version = 3.0.1
libyaml = True
COLLECTION VERSION
Collection Version
------------- -------
ansible.posix 1.3.0
CONFIGURATION
ANSIBLE_PIPELINING(/srv/Projects/site/infra/ansible.cfg) = True
DEFAULT_FORKS(/srv/Projects/site/infra/ansible.cfg) = 1
DEFAULT_HOST_LIST(/srv/Projects/site/infra/ansible.cfg) = ['/srv/Projects/site/infra/hosts.yml']
DEFAULT_JINJA2_NATIVE(/srv/Projects/site/infra/ansible.cfg) = True
DEFAULT_MANAGED_STR(/srv/Projects/site/infra/ansible.cfg) = This file is managed by Ansible, changes will be lost. Source: {file}
DEFAULT_VAULT_PASSWORD_FILE(/srv/Projects/site/infra/ansible.cfg) = /srv/Projects/site/infra/vault_pass.txt
DIFF_ALWAYS(/srv/Projects/site/infra/ansible.cfg) = True
INTERPRETER_PYTHON(/srv/Projects/site/infra/ansible.cfg) = auto
OS / ENVIRONMENT
Ubuntu 20.04
I believe that this is because synchronize
uses an action plugin, and that's where the variable is retrieved, and it ends up being an instance of this issue:
- https://github.com/ansible/ansible/issues/73268
See also:
- https://github.com/ansible/ansible/pull/58288
So in fact, I don't know that it is possible (or recommended) to solve this within the synchronize
action plugin itself.
The way I would recommend fixing this is to have an explicit option in the plugin for setting the user. It can still default to reading the user from ansible_user
, but being able to set it as an explicit option offers better control. If it had that option (I will call it remote_user
), then you could invoke it this way:
- name: ANSISTRANO | RSYNC DIRECT | Rsync application files directly to remote release path
synchronize:
src: "{{ ansistrano_deploy_from }}"
dest: "{{ ansistrano_release_path.stdout }}"
set_remote_user: yes
recursive: yes
delete: yes
archive: yes
compress: yes
use_ssh_args: "{{ ansistrano_rsync_use_ssh_args | default(omit) }}"
rsync_opts: "{{ ansistrano_rsync_extra_params | default(omit) }}"
rsync_path: "{{ ansistrano_rsync_path | default(omit) }}"
remote_user: '{{ ansible_user }}'
(see last line)
If ansible_user
is set to a static value (no Jinja) then this would be redundant, but in the example in the issue report, this will work, because the Jinja templating will be resolved before the value is passed into the action.
To workaround this issue now, with no changes, you can use set_fact
:
- name: "Set the value of ansible_user"
set_fact:
ansible_user: '{{ ansible_user }}'
- name: ANSISTRANO | RSYNC DIRECT | Rsync application files directly to remote release path
synchronize:
src: "{{ ansistrano_deploy_from }}"
dest: "{{ ansistrano_release_path.stdout }}"
set_remote_user: yes
recursive: yes
delete: yes
archive: yes
compress: yes
use_ssh_args: "{{ ansistrano_rsync_use_ssh_args | default(omit) }}"
rsync_opts: "{{ ansistrano_rsync_extra_params | default(omit) }}"
rsync_path: "{{ ansistrano_rsync_path | default(omit) }}"
But this is not desirable in all cases. You will be setting the precedence of that value differently than it was previously, so you may it find it has an unexpected value in other places where you tried to override it without using set_fact
.
@Vanav Thank you for reporting this. Is there any chance to try the workaround that @briantist provided ?
For my use case I was able just to remove variable. But this is definitely unexpected behavior. Maybe it is also a security and data loss risk, because ansible_user
contents is passed to rsync
unquoted and as is:
/usr/bin/rsync /path/ {{ deploy_user }}@84.84.84.84:/path/
I just ran into this in a playbook where I was setting ansible_user
temporarily using vars:
in a block:
in my script to force synchronization to go to a specific user on the host.
In my case, the workaround by @briantist wouldn't work (as he notes, "not desirable in all cases"). Although the ability to set the remote_user
explicitly as he suggests would solve my problem, by removing the need to set the ansible_user
at all in that block.
My workaround right now is to set ansible_user
to a static value, but that creates a situation where I need to manually set it in a couple of different places, differently from how I use it in any of the rest of the plays.
Here's an example of what I'm doing:
- name: synchronize as {{bot_user}}
vars:
ansible_user: "{{ bot_user }}"
block:
- name: synchronize test files
synchronize:
src: ~/MyData
dest: "{{ bot_home }}/Development/Test/MapData"
delete: yes
owner: no
group: no
rsync_opts:
- --prune-empty-dirs
- --filter=. /tmp/files_to_copy
This worked in ansible==4.3.0
(should be ansible.posix version 1.2.0) and stopped working some time after that.
Not sure if this helps, though.
+1
ansible.posix==1.2.0
is no viable option because #222. Desperatly need a fix. This and #222 force us to stay at ansible==2.10.7
.
Found it! Thank you @jovial. #344 fixes the issue for me. Testet with ansible==5.5.0
.