I want to download some binaries like Helm and make them globally available using $PATH. To avoid downloading with root privileges or alternatively have two steps (download with standard user and move to some folder in $PATH like /usr/local/bin), my idea was to create $HOME/bin and add it to $PATH.
This blog article was used to add a custom path to /etc/environment. Then I reload it as described here. This is my POC playbook where I try to add exa:
- name: Test
  hosts: all
  vars:
    - bin: /home/vagrant/bin
  tasks:
  - name: Test task
    file:
      path: "{{bin}}"
      state: directory
  - name: Add {{bin}} to path
    become: yes
    lineinfile: >
        dest=/etc/environment
        state=present
        backrefs=yes
        regexp='PATH=(["]*)((?!.*?{{bin}}).*?)(["]*)$'
        line="PATH=\1\2:{{bin}}\3"
  - name: Check path1
    shell: echo $PATH
  - name: Download exa
    unarchive:
      src: https://github.com/ogham/exa/releases/download/v0.8.0/exa-linux-x86_64-0.8.0.zip
      dest: "{{bin}}"
      remote_src: yes
  - name: reload env file
    shell: for env in $( cat /etc/environment ); do export $(echo $env | sed -e 's/"//g'); done
  - name: Check path after reload env file
    shell: echo $PATH
  - name: Test exa from PATH
    shell: exa-linux-x86_64 --version
In the last task Test exa from PATH it throws the error:
"stderr": "/bin/sh: 1: exa-linux-x86_64: not found"
The echo $PATH commands remain both at
"stdout": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games"
But the modified /etc/environment works. When I go to SSH on the machine without Ansible, $PATH is fine and also exa-linux-x86_64 --version works:
~$ echo $PATH
/home/vagrant/bin:/home/vagrant/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/home/vagrant/bin:/snap/bin
Environment
Ubuntu 18.04 host system running Vagrant with a Ubuntu 16.04 box. Ansible was executed by Vagrant on Ubuntu 16.04.
Possible workarounds
Using separate env variable
When setting the environment variable like this
  - name: Test exa from PATH
    shell: exa-linux-x86_64 --version
    environment:
      PATH: "{{ ansible_env.PATH }}:{{bin}}"
it works. But I have to apply those lines at least on play-level. I would like to set it globally like /etc/environment does at regular shells. This questions seems to have the same target, but on Solaris. The answer seems to only set $PATH from the host, which isn't useful for me since the custom bin dir isn't there.
Use only absolute paths
  - name: Test exa from PATH
    shell: "{{bin}}/exa-linux-x86_64 --version"
This causes less overhead, but you've to remember prefixing your commands always with the path variable. Seems also error-prone
Understanding the problem
I want a real solution and realize what's causing the problem. It's not clear for me why $PATH modification is so hard on Ansible, where it can be done quite easily in the underlying Linux system. This question says we don't have an interactive session in Ansible. There seems to be no $PATH available. According to the documentation, we can archive this by passing -l to bash. So I found the following working:
  - name: Test exa from PATH
    shell: bash -l -c "exa-linux-x86_64 --version"
But the following results in an error:
  - name: Test exa from PATH
    shell: exa-linux-x86_64 --version
    args:
      executable: /bin/bash -l
Here Ansible breaks the command with wrong quoting of the args:
"'/bin/bash -l' -c 'exa-linux-x86_64 --version'"
This ticket recommends the Ansible team to fix this, so that we get an login shell with $PATH. Since 2014, no real solution was provided at all.
Questions
- What is the purpose of different shell types that get access to the modified $PATHor not?
- Why does Ansible complicate things? Wouldn't it be easier to provide a login shell that solves this issue? Are there reasons why they did what they did?
- How can we deal with the resulting problems? What is best practice?
 
     
    