How to run Ansible tasks as a specific user - ruby

Part of the Ansible playbook I'm currently writing involves creating a new user as root, installing rvm for that specific user, then switching to them in order to run rvm. It doesn't seem to take, however. Here's what I'm currently running.
---
- name: Install pexpect
yum:
name: pexpect
state: latest
- name: Create rvm group
group:
name: rvm
state: present
- name: Create rvmuser
user:
name: rvmuser
state: present
group: rvm
password: "{{vault_rvm_password}}"
- name: Download signing key
shell: "command curl -sSL https://rvm.io/mpapis.asc | gpg2 --import -"
become: True
become_user: rvmuser
- name: RVM single-user install
shell: "curl -L https://get.rvm.io | bash -s stable"
become: True
become_user: rvmuser
- name: Install Ruby
shell: "rvm install 2.2.1"
become: True
become_user: rvmuser
(These particular shell steps appear to be the preferred way of installing a specific version of Ruby; I tried using yum to install an RPM, but then gem fails.)
Basically, when it gets to the "Install Ruby" step, it fails because it can't find rvm, which is a strong indication that it's still trying to run the command as root. Here's the error:
TASK [ruby : Install Ruby] *****************************************************
fatal: [10.121.250.21]: FAILED! => {"changed": true, "cmd": "rvm install 2.2.1", "delta": "0:00:00.007545", "end": "2017-04-12 16:14:35.974732", "failed": true, "rc": 127, "start": "2017-04-12 16:14:35.967187", "stderr": "/bin/sh: rvm: command not found", "stdout": "", "stdout_lines": [], "warnings": []}
Am I doing this incorrectly, or is something else going wrong behind the scenes?
EDIT: The user is being created correctly, I've confirmed. After running the top five steps, I can SSH into the machine, switch to rvmuser and see that which rvm returns a path.

That looks like a path issue, i.e. the newly created rvmuser does not have rvm in its $PATH:
Try full path to rvm in this task:
- name: Install Ruby
shell: "rvm install 2.2.1"
become: True
become_user: rvmuser

Related

Ansible shell command doesn't seem to have access to the user's shell environment

Im trying to use a ruby installed via RBENV. I have confirmed that RBENV works as desired and produces the correct ruby version which can be called via the terminal
ruby -v returns: ruby 3.1.0p0 (2021-12-25 revision fb4df44d16) [x86_64-linux] which is the desired version
bundler -v returns: Bundler version 2.3.10 which is also the desired version.
However the following tasks in my playbook fail
- block:
- name: 'set ruby version'
shell: echo eval "$(rbenv init -)" >> ~/.zshrc
- name: 'set global ruby verison'
shell: 'rbenv global {{ ruby_version }}'
- name: 'restart shell'
shell: $shell
- name: 'Bundle install'
shell:
chdir: "{{ app_path }}"
cmd: bundle install
become: yes
become_user: '{{ deploy_user}}'
I get the following output when trying to execute these tasks in my playbook
fatal: [45.77.186.234]: FAILED! => {"changed": true, "cmd": "bundle install", "delta": "0:00:00.001967", "end": "2022-03-25 00:33:48.231712", "msg": "non-zero return code", "rc": 127, "start": "2022-03-25 00:33:48.229745", "stderr": "/bin/sh: 1: bundle: not found", "stderr_lines": ["/bin/sh: 1: bundle: not found"], "stdout": "", "stdout_lines": []}
On the docs page for ansible.builtin.shell the 3rd bullet point under synopsis indicates that the shell command is executed using the user's shell environment. I can't see anywhere that I'm supposed to define which shell specifically, I wonder if my use of ZSH is where the problem is?
At this point I've tried everything I can think of, and now Im asking for help. How do I run a shell command against the user's shell environment?
I ended up having to execute chained zsh commands to get this to work for me.
- name: Install fastlane gem
command: zsh -lc "source ~/.zshrc && gem install fastlane"
I didn't have to do this before I updated my MacOS system to 12.3.1, for what it's worth. It seems like some default behavior changed for shell execution.

Changing user and installing ruby

My Ansible server is communicating as root user with agent but I need to install ruby as different user i.e deploy. So I am trying to switch the user to deploy using 'become' to install ruby but I am facing issue. Its seems that when i am trying to switch the user and running the command its unable to use .bashrc file of deploy user. Below is my YML file
---
- hosts: test1
become: true
tasks:
- name: adding node.js repository
shell: curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
- name: adding yarn pubkey
shell: curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
- name: adding yarn repo
shell: echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
- name: update cache
apt: update_cache=true
- name: install all the below list of packages
apt: name={{ item }} update_cache=true
with_items:
- git-core
- curl
- zlib1g-dev
- build-essential
- libssl-dev
- libreadline-dev
- libyaml-dev
- libsqlite3-dev
- sqlite3
- libxml2-dev
- libxslt1-dev
- libcurl4-openssl-dev
- software-properties-common
- libffi-dev
- nodejs
- yarn
- name: change to deploy home directory
shell: cd
become: true
become_user: deploy
- name: getting repo from git
shell: git clone https://github.com/rbenv/rbenv.git ~/.rbenv
become: true
become_user: deploy
- name: copy path
shell: echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
become: true
become_user: deploy
- name: copy eval
shell: echo 'eval "$(rbenv init -)"' >> ~/.bashrc
become: true
become_user: deploy
- name: execute shell
shell: exec $SHELL
become: true
become_user: deploy
- name: ruby repo
shell: git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
become: true
become_user: deploy
- name: copy paths
shell: echo 'export PATH="$HOME/.rbenv/plugins/ruby-build/bin:$PATH"' >> ~/.bashrc
become: true
become_user: deploy
- name: shell execute
shell: exec $SHELL
become: true
become_user: deploy
- name: install ruby
shell: rbenv install 2.4.4
become: true
become_user: deploy
- name: set global
shell: rbenv global 2.4.4
become: true
become_user: deploy
I am getting the below error:
TASK [install ruby] ***************
fatal: [host1]: FAILED! => {"changed": true, "cmd": "rbenv install
2.4.4", "delta": "0:00:00.003186", "end": "2018-09-25 15:43:23.224716", "msg": "non-zero return code", "rc": 127, "start":
"2018-09-25 15:43:23.221530", "stderr": "/bin/sh: 1: rbenv: not
found", "stderr_lines": ["/bin/sh: 1: rbenv: not found"], "stdout":
"", "stdout_lines": []}
But when I am providing the path manually in command for rbenv its working fine. As shown below:
- name: install ruby
shell: /home/deploy/.rbenv/bin/rbenv install 2.4.4
become: true
become_user: deploy
Can you please tell me why its behaving like this?
I need to install bundler also using gem. I am switching to user 'deploy' but its going to check the root user directory instead of deploy user and giving error. Please refer the below YML part for gem and error:
- name: install bundler
shell: gem install bundler
become: true
become_user: deploy
Below is the error for bundler:
TASK [install bundler] *********
fatal: [host1]: FAILED! => {"changed": true, "cmd": "gem install
bundler", "delta": "0:00:02.396195", "end": "2018-09-25
16:21:18.703899", "msg": "non-zero return code", "rc": 1, "start":
"2018-09-25 16:21:16.307704", "stderr": "ERROR: While executing gem
... (Gem::FilePermissionError)\n You don't have write permissions
for the /var/lib/gems/2.3.0 directory.", "stderr_lines": ["ERROR:
While executing gem ... (Gem::FilePermissionError)", " You don't
have write permissions for the /var/lib/gems/2.3.0 directory."],
"stdout": "", "stdout_lines": []}
Please help me to fix the issue.
Thanks.
That playbook does not do what you think it does, because every one of those tasks is effectively its own ssh connection. So, this:
- name: change to deploy home directory
shell: cd
become: true
become_user: deploy
- name: execute shell
shell: exec $SHELL
... is equivalent to ssh root#the-host "su deploy -c 'cd; exit'"; ssh root#the-host "su deploy -c 'exec $SHELL; exit'" and so forth.
You'll very like also want to move all of those tasks that run as deploy into their own playbook or role, and include them with those become: true and become: deploy at the include or role level to keep from repeating that text all over the tasks.
But even if you don't do that, the shortest path to success is to collapse all 10 of those steps into just one shell: | block to rescue your sanity and reinforce that they all need to happen in the same shell session
- name: install ruby using rbenv
become: true
become_user: deploy
shell: |
set -e # <-- stop running this script if something fails
cd $HOME
export ... # etc etc
args:
creates: /home/deploy/.rbenv/versions/2.4.4/bin/ruby
Optionally including that args: creates: business will give ansible a fighting chance to skip that step if it has already provisioned rbenv. You will, of course, likely want to update that to guard the step in a way that is more relevant to what you're trying to do.

Ansible - pip not found

I am getting this error:
TASK [pip] *********************************************************************
failed: [default] (item=urllib3) =>
{"changed": false, "item": "urllib3",
"msg": "Unable to find any of pip2, pip to use. pip needs to be installed."}
Upon a suggestion I run following command:
ansible default -a "which pip"
I get an error:
default | FAILED | rc=1 >>
non-zero return code
So I guess that means no pip installed. I tried installing pip using:
ansible default -a "easy_install pip"
I get the following error:
default | FAILED | rc=2 >>
[Errno 2] No such file or directory
Any ideas?
UPDATE
In play_local.yaml, I have the following task:
- name: Prepare system
hosts: default
become: yes
gather_facts: false
pre_tasks:
- raw: sudo apt-get -y install python python-setuptools python-pip build-essential libssl-dev libffi-dev python-dev easyinstall pip
- file: path=/etc/sudoers.d/ssh-auth-sock state=touch mode=0440
#- lineinfile: line='Defaults env_keep += "SSH_AUTH_SOCK"' path=/etc/sudoers.d/ssh-auth-sock
- replace:
path: /etc/apt/sources.list
regexp: 'br.'
replace: ''
Shouldn't this task install pip?
Seems like pip is not installed, you can use the following task to install it:
- name: Install pip
apt:
name: python-pip
update_cache: yes
state: present
May be pip is hashed. Meaning pip is installed at path x (may be /usr/local/bin/pip), however, cached at path y (may be /usr/bin/pip). You can confirm that from - ansible default -m shell -a ‘type pip’. To resolve this you’ll need to run - ansible default -m shell -a ‘hash -r’.
BTW, you can also use command module instead of shell.
I've just met the same problem on a brand new CentOS 7. Solved through installing setuptools with yum first and then pip with easy_install as below :
ansible default -b -m yum -a "name=python-setuptools state=present"
ansible default -b -m easy_install -a "name=pip state=present"
For Debian Based systems (RUN THIS ON CLIENT SYSTEMS):
first install package python-is-python3 then add alias for pip echo alias pip=pip3 >> ~/.bashrc
I know my solution would be dumb but It works.
# pip-fix.yml
- name: pip fix
hosts: all
become: true
tasks:
- name: install python-is-python3
apt: name=python-is-python3 update_cache=yes state=present
- name: creating alias
shell: echo alias pip=pip3 >> ~/.bashrc
- name: test and upgrade pip
pip: name=pip state=latest
tags:
- packages
run using ansible-playbook pip-fix.yml
Ansible Not finding / Installing Pip
While not using the author's original details, i think this might help them or others.
I was getting the following error for installing pip:
via Ansible:
FAILED! => {"changed": false, "cmd": "/bin/easy_install --upgrade pip", "failed": true, "msg": "Couldn't find index page for 'pip' (maybe misspelled?)
directly on the host:
Searching for pip
Reading https://pypi.python.org/simple/pip/
Couldn't find index page for 'pip' (maybe misspelled?)
Scanning index of all packages (this may take a while)
Reading https://pypi.python.org/simple/
No local packages or download links found for pip
error: Could not find suitable distribution for
Requirement.parse('pip')
I attempted to use the above hash -r answer:
(NOTE: help hash... hash -r == forget all remembered locations)
- name: forget easy_install path
shell: hash -r
become: true
however this was not a solve for me.
I found via another post: 'pip install' fails for every package ("Could not find a version that satisfies the requirement") that this was the final fix:
curl https://bootstrap.pypa.io/get-pip.py | python
or
- name: manually install pip
shell: curl https://bootstrap.pypa.io/pip/2.7/get-pip.py | python
become: true
NOTE: i changed the pip version. Also, there is a better way to do a curl in ansible, this is simply an example
I then followed with the easy_install pip latest. to ensure it was up to date.
below fix worked for me
#ln -s /usr/local/bin/pip /usr/bin/pip

Installing specific apt version with ansible

I used an ansible playbook to install git:
---
- hosts: "www"
tasks:
- name: Update apt repo
apt: update_cache=yes
- name: Install dependencies
apt: name={{item}} state=installed
with_items:
- git
I checked the installed versions:
$ git --version
git version 1.9.1
But adding these to the ansible playbook: apt: name=git=1.9.1 state=installed
and rerunning results in the following error:
fatal: [46.101.94.110]: FAILED! => {"cache_update_time": 0,
"cache_updated": false, "changed": false, "failed": true, "msg":
"'/usr/bin/apt-get -y -o "Dpkg::Options::=--force-confdef" -o
"Dpkg::Options::=--force-confold" install 'git=1.9.1'' failed:
E: Version '1.9.1' for 'git' was not found\n", "stderr": "E: Version
'1.9.1' for 'git' was not found\n", "stdout": "Reading package
lists...\nBuilding dependency tree...\nReading state
information...\n", "stdout_lines": ["Reading package lists...",
"Building dependency tree...", "Reading state information..."]}
Git package with that specific version is as follows:
git=1:1.9.1-1ubuntu0.2
Your task should be:
apt: name=git=1:1.9.1-1ubuntu0.2 state=present
Regards
You don't need two tasks for updating cache and installing. Your playbook should look like:
---
- hosts: "www"
tasks:
- name: Install dependencies
apt:
name:
- git=1:1.9.1-1ubuntu0.2
state: installed
update_cache: yes
note that Ansible supports wildcarding so you don't necessarily need the full version string

Using Ansible, how can I install PythonBrew system-wide?

I'm trying to create a playbook with Ansible (v 1.3.3) to install Pythonbrew system-wide on a Debian server following the instructions in the Pythonbrew readme file.
I am able to get Pythonbrew installed but I cannot install the specific version of Python that I want with it. I suspect the issue has to do with the shell environment Ansible is running under.
Here's my playbook script:
- name: Install and configure PythonBrew
hosts: dev
user: root
vars_files:
- vars.yml
gather_facts: false
tasks:
- name: Install PythonBrew Debian packages
apt: pkg=${item} state=installed update-cache=yes
with_items: ${pythonbrew_packages}
- name: Install PythonBrew system-wide
shell: curl -kL http://xrl.us/pythonbrewinstall | bash creates=/usr/local/pythonbrew executable=/bin/bash
- name: Update bashrc for PythonBrew
lineinfile:
dest=~/.bashrc
regexp='^'
line='[[ -s $HOME/.pythonbrew/etc/bashrc ]] && source $HOME/.pythonbrew/etc/bashrc'
state=present
create=True
- name: Install python binary
shell: pythonbrew install -v ${python_version} executable=/bin/bash
When I run this playbook, it fails with the following output
failed: [devserver] => {"changed": true, "cmd": "pythonbrew
install -v 2.7.3 ", "delta": "0:00:00.016639", "end": "2013-10-11
15:21:40.989677", "rc": 127, "start": "2013-10-11 15:21:40.973038"}
stderr: /bin/bash: pythonbrew: command not found
I've been tweaking things for the last hour or so to no avail. Does anybody have any suggestions for fixing this?
By peeking at the PythonBrew install script, I was able to figure this out. (And just in time for the deprecation of PythonBrew!)
Here's the playbook that installs PythonBrew without manual intervention. This may be of interest to anyone trying to script PythonBrew to install automatically.
vars.yml
#
# Python/PythonBrew Settings
# TODO: replace old-style Ansible ${vars} with jinja-style {{ vars }}
#
project_name: MY_PROJECT
python:
version: 2.7.3
pythonbrew:
root: /usr/local/pythonbrew
bashrc_path: $HOME/.pythonbrew/etc/bashrc
packages:
- curl
- zlib1g-dev
- libsqlite3-dev
- libssl-dev
- libxml2
- libxml2-dev
- libxslt1-dev
- libmysqlclient-dev
- libbz2-dev
pythonbrew.yml
---
#
# Install and Configure PythonBrew
#
- name: Install and configure PythonBrew
hosts: MY_HOST
user: root
vars_files:
- vars.yml
gather_facts: false
tasks:
- name: Install PythonBrew Debian packages
apt: pkg=${item} state=installed update-cache=yes
with_items: ${pythonbrew.packages}
- name: Install PythonBrew system-wide
shell: curl -kL http://xrl.us/pythonbrewinstall | bash
executable=/bin/bash
creates=${pythonbrew.root}
- name: Update bashrc for PythonBrew
lineinfile:
dest=/root/.bashrc
regexp='^'
line='[[ -s ${pythonbrew.bashrc_path} ]] && source ${pythonbrew.bashrc_path}'
state=present
create=True
# This step allows install to continue without new shell. Pulled from:
# https://github.com/utahta/pythonbrew/blob/master/pythonbrew/installer/pythonbrewinstaller.py#L91
- name: Install python binary
shell: export PYTHONBREW_ROOT=${pythonbrew.root}; source ${pythonbrew.root}/etc/bashrc; pythonbrew install ${python.version}
executable=/bin/bash
- name: Switch to python version
shell: export PYTHONBREW_ROOT=${pythonbrew.root}; source ${pythonbrew.root}/etc/bashrc; pythonbrew switch ${python.version}
executable=/bin/bash

Resources