Skip to content

Instantly share code, notes, and snippets.

@DrPsychick
Last active May 9, 2024 18:36
Show Gist options
  • Save DrPsychick/847ea32c876fda2337e79096a8a23176 to your computer and use it in GitHub Desktop.
Save DrPsychick/847ea32c876fda2337e79096a8a23176 to your computer and use it in GitHub Desktop.
macOS / OSX ansible path test

INFO

  • remote SSH shell on macOS does not use /etc/paths
  • this is not an issue of ansible, it is the way macOS processes config files on remote shells:
  • see ssh localhost echo \$PATH

ANSIBLE executes remote shell /bin/sh

  • ...if not specified otherwise and macOS does not read any startup files
  • example: [...]localhost '/bin/sh -c '"'"'sudo -H -S -n -u test /bin/sh -c '"'"'"'"'"[...]
  • even /bin/bash does not read any startup files by default

Documentation

  • see man sh

... When invoked as an interactive shell with the name sh, bash looks for the variable ENV, expands its value if it is defined, and uses the expanded value as the name of a file to read and execute....

  • and

When bash is started non-interactively, to run a shell script, for example, it looks for the variable BASH_ENV in the environment, expands its value if it appears there, and uses the expanded value as the name of a file to read and execute.

Tests

required to SSH to localhost

echo "localhost" > inventory

execute local as current user (no ssh, just exec)

ansible localhost -a 'echo $PATH'

  • you will see the full path of current shell

local through ssh as root

ansible -c ssh -i inventory localhost -a 'echo $PATH'

  • you will see the standard PATH=/usr/bin:/bin:/usr/sbin:/sbin

as user

ansible -b --become-user=test -c ssh -i inventory localhost -a 'echo $PATH'

  • no change, still only the default path

SOLUTION

  • add BASH_ENV=~/.profile into the environment of the playbook/role,
  • unfortunately that does not work with ad-hoc commands, so we need a playbook
  • simple playbook test_env_playbook.yml
---
- name: Test ENV
  hosts: localhost
  environment:
    BASH_ENV: "~/.profile"
    # also possible, but not using hosts config PATH: "{{ ansible_env.PATH }}:/usr/local/bin"
  tasks:
    - name: Echo PATH
      shell: echo $PATH
      args:
        executable: /bin/bash
      become: yes
      become_user: "test"
  • run it (very verbose) ansible -i inventory test_env_playbook.yml -vvv

RESULTS

  • .profile or .bash_profile or .bash_rc are not read by default, neither are system wide config files
  • ENV is not used when executed with /bin/sh, although it says so in man bash
  • BASH_ENV is loaded when executable is /bin/bash
  • ansible command module always uses /bin/sh so you need to use the shell module
  • --> set BASH_ENV to either global /etc/profile or user ~/.profile as desired

My setup

  • I create a ~/.profile on the root and user accounts
  • I load defaults from my user profile: echo 'source /etc/profile' >> ~/.profile
  • Benefit: I can use for example python/pip installed in my user Library directory
  • Downside: the ansible pip module does not work if its installed in /usr/local/bin folder, so you need to use the shell module.
    • To make the pip module work again, you have to use PATH environment for the role
@Kylmakalle
Copy link

Kylmakalle commented Nov 9, 2021

Thank you for your effort. My solution on this - just hardcode the environment globally in a playbook. (BASH_ENV didn't work for me for some reason)

Pros - it works. Cons - you must exactly know which PATH you're trying to achieve.

For basic stuff like rbenv, homebrew (w/ Apple Silicon /opt/ supported paths) I have setup below

environment:
    PATH: "/Users/{{ ansible_user }}/.rbenv/shims:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/sbin:/usr/local/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
    RBENV_SHELL: "zsh"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment