pldimitrov
pldimitrov

Reputation: 1727

Not possible to source .bashrc with Ansible

I can SSH to the remote host and do a source /home/username/.bashrc — everything works fine.
However if I do:

- name: source bashrc
  action: command source /home/username/.bashrc

I get:

failed: [hostname] => {"cmd": ["source", "/home/username/.bashrc"], "failed": true, "rc": 2}
msg: [Errno 2] No such file or directory

I have no idea what I'm doing wrong.

Upvotes: 108

Views: 102702

Answers (10)

Steve Midgley
Steve Midgley

Reputation: 2334

You have two options to use source with Ansible. One is with the shell module and /bin/sh (the default in Ansible).
source is called . in /bin/sh.

So, your command would be:

- name: source bashrc
  shell: . /home/username/.bashrc && [the actual command you want run]

Note you have to run a command after sourcing .bashrc because each SSH session is distinct — every Ansible command runs in a separate SSH transaction.

Your second option is to force Ansible shell to use bash and, then, you can use the source command:

- name: source bashrc
  shell: source /home/username/.bashrc && [the actual command you want run]
  args:
     executable: /bin/bash

Finally, I'll note that you may want to actually source /etc/profile if you're on Ubuntu or similar, which more completely simulates a local login.

Upvotes: 115

Zlopez
Zlopez

Reputation: 700

I found become as best solution:

- name: Source .bashrc
  shell: . .bashrc
  become: true

You can change the user by adding (default: root):

- name: Source .bashrc
  shell: . .bashrc
  become: true
  become-user: {your_remote_user}

More info here: Ansible become

Upvotes: 2

Juraj Michalak
Juraj Michalak

Reputation: 1216

Many responses recommend to source ~/.bashrc but main problem is that ansible shell is not interactive and ~/.bashrc implementation by default ignores non interactive shell (check its beginning).

The best solution for executing commands as user after its ssh interactive login I found is:

- hosts: all
  tasks:
    - name: source user profile file
      #become: yes
      #become_user: my_user  # in case you want to become different user (make sure acl package is installed)
      shell: bash -ilc 'which python' # example command which prints
      register: which_python
    - debug:
      var: which_python

bash: '-i' means interactive shell, so .bashrc won't be ignored '-l' means login shell which sources full user profile

Upvotes: 11

realtebo
realtebo

Reputation: 25637

My 2 cents, i circumnavigated the problem sourcing ~/.nvm/nvm.sh into ~/.profile and then using sudo -iu as suggested in another answer.

Tried on January 2018 vs Ubuntu 16.04.5

- name: Installing Nvm 
  shell: >
    curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.34.0/install.sh | bash
  args:
    creates: "/home/{{ ansible_user }}/.nvm/nvm.sh"
  tags:
    - nodejs    

- name: Source nvm in ~/.profile
  sudo: yes
  sudo_user: "{{ ansible_user }}"
  lineinfile: >
    dest=~/.profile
    line="source ~/.nvm/nvm.sh"
    create=yes
  tags: 
    - nodejs
  register: output    

- name: Installing node 
  command: sudo -iu {{ ansible_user }} nvm install --lts
  args:
     executable: /bin/bash
  tags:
    - nodejs    

Upvotes: 1

Rico
Rico

Reputation: 61521

So command will only run executables. source per se is not an executable. (It's a builtin shell command). Is there any reason why you want to source a full environment variable?

There are other ways to include environment variables in Ansible. For example, the environment directive:

- name: My Great Playbook
  hosts: all
  tasks:
    - name: Run my command
      sudo: no
      action: command <your-command>
      environment:
          HOME: /home/myhome

Another way is to use the shell Ansible module:

- name: source bashrc
  sudo: no
  action: shell source /home/username/.bashrc && <your-command>

or

- name: source bashrc
  sudo: no   
  shell: source /home/username/.bashrc && <your-command>

In these cases, the shell instance/environment will terminate once the Ansible step is run.

Upvotes: 25

Hearen
Hearen

Reputation: 7828

I've tried all the options above with ansible 2.4.1.0 and no one works until another two and here is the detail to re-produce the case.

$ cat ~/.bash_aliases 
alias ta="echo 'this is test for ansible interactive shell'";

And this is the ansible test:

- name: Check the basic string operations
  hosts: 127.0.0.1 
  connection: local

  tasks:
  - name: Test Interactive Bash Failure
    shell: ta
    ignore_errors: True

  - name: Test Interactive Bash Using Source
    shell: source ~/.bash_aliases && ta
    args:
      executable: /bin/bash
    ignore_errors: yes

  - name: Test Interactive Bash Using .
    shell: . ~/.bash_aliases && ta
    ignore_errors: yes

  - name: Test Interactive Bash Using /bin/bash -ci
    shell: /bin/bash -ic 'ta'
    register: result
    ignore_errors: yes

  - debug: msg="{{ result }}"

  - name: Test Interactive Bash Using sudo -ui
    shell: sudo -ui hearen ta
    register: result
    ignore_errors: yes

  - name: Test Interactive Bash Using ssh -tt localhost /bin/bash -ci
    shell: ssh -tt localhost /bin/bash -ci 'ta'
    register: result
    ignore_errors: yes

And this is the result:

$ ansible-playbook testInteractiveBash.yml 
 [WARNING]: Could not match supplied host pattern, ignoring: all

 [WARNING]: provided hosts list is empty, only localhost is available


PLAY [Check the basic string operations] ************************************************************************************************************************************************

TASK [Gathering Facts] ******************************************************************************************************************************************************************
ok: [127.0.0.1]

TASK [Test Interactive Bash Failure] ****************************************************************************************************************************************************
fatal: [127.0.0.1]: FAILED! => {"changed": true, "cmd": "ta", "delta": "0:00:00.001341", "end": "2018-10-31 10:11:39.485897", "failed": true, "msg": "non-zero return code", "rc": 127, "start": "2018-10-31 10:11:39.484556", "stderr": "/bin/sh: 1: ta: not found", "stderr_lines": ["/bin/sh: 1: ta: not found"], "stdout": "", "stdout_lines": []}
...ignoring

TASK [Test Interactive Bash Using Source] ***********************************************************************************************************************************************
fatal: [127.0.0.1]: FAILED! => {"changed": true, "cmd": "source ~/.bash_aliases && ta", "delta": "0:00:00.002769", "end": "2018-10-31 10:11:39.588352", "failed": true, "msg": "non-zero return code", "rc": 127, "start": "2018-10-31 10:11:39.585583", "stderr": "/bin/bash: ta: command not found", "stderr_lines": ["/bin/bash: ta: command not found"], "stdout": "", "stdout_lines": []}
...ignoring

TASK [Test Interactive Bash Using .] ****************************************************************************************************************************************************
fatal: [127.0.0.1]: FAILED! => {"changed": true, "cmd": ". ~/.bash_aliases && ta", "delta": "0:00:00.001425", "end": "2018-10-31 10:11:39.682609", "failed": true, "msg": "non-zero return code", "rc": 127, "start": "2018-10-31 10:11:39.681184", "stderr": "/bin/sh: 1: ta: not found", "stderr_lines": ["/bin/sh: 1: ta: not found"], "stdout": "", "stdout_lines": []}
...ignoring

TASK [Test Interactive Bash Using /bin/bash -ci] ****************************************************************************************************************************************
changed: [127.0.0.1]

TASK [debug] ****************************************************************************************************************************************************************************
ok: [127.0.0.1] => {
    "msg": {
        "changed": true, 
        "cmd": "/bin/bash -ic 'ta'", 
        "delta": "0:00:00.414534", 
        "end": "2018-10-31 10:11:40.189365", 
        "failed": false, 
        "rc": 0, 
        "start": "2018-10-31 10:11:39.774831", 
        "stderr": "", 
        "stderr_lines": [], 
        "stdout": "this is test for ansible interactive shell", 
        "stdout_lines": [
            "this is test for ansible interactive shell"
        ]
    }
}

TASK [Test Interactive Bash Using sudo -ui] *********************************************************************************************************************************************
 [WARNING]: Consider using 'become', 'become_method', and 'become_user' rather than running sudo

fatal: [127.0.0.1]: FAILED! => {"changed": true, "cmd": "sudo -ui hearen ta", "delta": "0:00:00.007906", "end": "2018-10-31 10:11:40.306128", "failed": true, "msg": "non-zero return code", "rc": 1, "start": "2018-10-31 10:11:40.298222", "stderr": "sudo: unknown user: i\nsudo: unable to initialize policy plugin", "stderr_lines": ["sudo: unknown user: i", "sudo: unable to initialize policy plugin"], "stdout": "", "stdout_lines": []}
...ignoring

TASK [Test Interactive Bash Using ssh -tt localhost /bin/bash -ci] **********************************************************************************************************************
hearen@localhost's password: 
changed: [127.0.0.1]

PLAY RECAP ******************************************************************************************************************************************************************************
127.0.0.1                  : ok=8    changed=6    unreachable=0    failed=0  

There are two options worked:

  • shell: /bin/bash -ic 'ta'
  • shell: ssh -tt localhost /bin/bash -ci 'ta' but this one requires password input locally.

Upvotes: 6

gwerner
gwerner

Reputation: 151

I was experiencing this same issue when trying to get virtualenvwrapper to work on an Ubuntu server. I was using Ansible like this:

- name: Make virtual environment
  shell: source /home/username/.bashrc && makevirtualenv virenvname
  args:
    executable: /bin/bash

but the source command was not working.

Eventually I discovered that the .bashrc file has a few lines at the top of the file that prevent source from working when called by Ansible:

# If not running interactively, don't do anything
case $- in
    *i*) ;;
      *) return;;
esac

I commented out those lines in .bashrc and everything worked as expected after that.

Upvotes: 12

kwin wng
kwin wng

Reputation: 105

The right way should be:

- hosts: all
  tasks:
    - name: source bashrc file
      shell: "{{ item }}"
      with_items:
         - source ~/.bashrc
         - your other command

Note: it's test in ansible 2.0.2 version

Upvotes: -3

vikas027
vikas027

Reputation: 5742

Well I tried the listed answers but those didn't worked for me while installing ruby through rbenv. I had to source below lines from /root/.bash_profile

PATH=$PATH:$HOME/bin:$HOME/.rbenv/bin:$HOME/.rbenv/plugins/ruby-build/bin
export PATH
eval "$(rbenv init -)"

Finally, I came up with this

- shell: sudo su - root -c 'rbenv install -v {{ ruby_version }}'

One can use this with any command.

- shell: sudo su - root -c 'your command'

Upvotes: 3

Clempat
Clempat

Reputation: 508

I know this answer come too late but I have seen in enough code you can use the sudo option -i so:

- name: source bashrc
  shell: sudo -iu {{ansible_user_id}} [the actual command you want run]

As said in the documentation

The -i (simulate initial login) option runs the shell specified by the password database entry of the target user as a login shell.  This means that login-specific
               resource files such as .profile or .login will be read by the shell.  If a command is specified, it is passed to the shell for execution via the shell's -c option.
               If no command is specified, an interactive shell is executed.  sudo attempts to change to that user's home directory before running the shell.  It also initializes
               the environment to a minimal set of variables, similar to what is present when a user logs in.  The Command environment section below documents in detail how the -i
               option affects the environment in which a command is run.

Upvotes: 21

Related Questions