Vagrant - Provisioning script not changing directory - shell

New to vagrant, please help!
Vagrantfile
Vagrant.configure("2") do |config|
config.vm.box = "laravel/homestead"
config.vm.provision "shell", path: "vm-setup/provision.sh"
end
vm-setup/provision.sh
# Update apt-get
apt-get -y update
# Install tree
apt-get install tree
# Create .bash_aliases
sudo echo 'alias cls="clear"' >> ~/.bash_aliases
sudo chsh -s $(which zsh) vagrant
cd /vagrant
provision.sh file runs fine. When I run "vagrant provision" it updates apt-get, installs tree and even changes the shell to ZSH.
But sudo echo 'alias cls="clear"' >> ~/.bash_aliases and cd /vagrant lines do not work, not sure why. When I vagrant ssh into the machine, I am being taken to root directory (/home/vagrant). I would like to start in /vagrant folder.

Vagrant's shell provisioner by default runs with privileged = true:
privileged (boolean) - Specifies whether to execute the shell script
as a privileged user or not (sudo). By default this is "true".
When you perform vagrant ssh you login to a VM as vagrant user.
That's why:
1.
# Create .bash_aliases
sudo echo 'alias cls="clear"' >> ~/.bash_aliases
It writes to root's ~/.bash_aliases and it is really there:
root#vagrant:~# id
uid=0(root) gid=0(root) groups=0(root)
root#vagrant:~# cat .bash_aliases
alias cls="clear"
Solution: write to vagrant's home folder:
# Create .bash_aliases
echo 'alias cls="clear"' >> /home/vagrant/.bash_aliases
chown vagrant:vagrant /home/vagrant/.bash_aliases
2.
cd /vagrant
This means that folder was changed in provision script, nothing else.
Solution: add this statement to vagrant's .bash_aliases as well:
echo 'cd vagrant' >> /home/vagrant/.bash_aliases
Your final vm-setup/provision.sh is:
# Update apt-get
apt-get -y update
# Install tree
apt-get install tree
# Create .bash_aliases
echo 'alias cls="clear"' >> /home/vagrant/.bash_aliases
echo 'cd /vagrant' >> /home/vagrant/.bash_aliases
chown vagrant:vagrant /home/vagrant/.bash_aliases
chsh -s $(which zsh) vagrant

Even not being the case, just for the sake of completion:
I had struggled many times while trying to use Vagrant as testing tool for setup scripts and just now I realized the underlying reason:
Using this Vagrantfile statement:
config.vm.provision "shell", path: "myScript.sh"
myScript.sh is inlined to the virtual machine standard input. This is good in the sense you don't need access to the actual script from inside the virtual machine (typically through /vagrant path).
...but it comes with the drawback that any relative path won't work properly.
Of course: We can adjust it to absolute path based on /vagrant. But this requires to modify the script we are trying to test.
So in this case (and in my opinion in any case we are not going to disable /vagrant share), it is a better solution to use "inline:" option with the "machine-internal" path:
config.vm.provision "shell", inline: "/vagrant/myScript.sh"
...This will inline this statement instead of the contents of the file and relative paths (or even "script path based" ones such as $(dirname "${0}")/relative/path) are going to work properly.
Additionally, if the setup script you are going to test is intended to be executed by non privileged users (for example if it is going to set up some user configuration we will expect to work just after a vagrant ssh -with vagrant user-) it is also a good idea to add the privileged: false option, pointed out by #Nickolay:
config.vm.provision "shell", inline: "/vagrant/myScript.sh", privileged: false

Related

Installing golang 1.10 on vagrant vbox

I am running the following script from a Vagrantfile and everything is working fine. In the end, I see the output go1.10 linux/amd64 as expected.
But, when I run vagrant ssh I get The program 'go' is currently not installed.
What is the difference between vagrant provision that was able to see go and vagrant ssh that was not able to see go?
config.vm.box = "ubuntu/xenial64"
config.vm.provision "shell" do |s|
s.inline = "
sudo apt-get update
export GOPATH=$HOME/work
sudo curl -O https://storage.googleapis.com/golang/go1.10.linux-amd64.tar.gz
sudo tar -xvf go1.10.linux-amd64.tar.gz
sudo mv go /usr/local
sudo echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.profile
export PATH=$PATH:/usr/local/go/bin
go version" # this row is working fine on the script but not after ssh
end
In order to make your changes to $PATH available to all users, you have to change it in the global profile, not the user's. Change the line
sudo echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.profile
to
sudo echo 'export PATH=$PATH:/usr/local/go/bin' >> /etc/profile

why doesn't vagrant provisioner modify ~/.bashrc?

How can I make the Vagrantfile append the contents of a file to the ~/.bashrc file ?
In my Vagrantfile, I am trying to append the contents of a file /vagrant/dev_env_config to the ~/.bashrc file.
When I run vagrant up it outputs the echo statement AND it outputs the expected contents of the ~/.bashrc file.... so I know it's reading the file dev_env_config and APPEARS to be appending it.
However, when I then run vagrant ssh and then cat ~/.bashrc the ~/.bashrc file is unmodified, it's the default ~/.bashrc file
In other words the mods to ~/.bashrc file are lost somewhere between when the vagrant provison runs and when I run vagrant ssh
# Vagrantfile
Vagrant.configure("2") do |config|
... various cmds to set box and network...
$install_user_vars = <<SCRIPT
sudo cat /vagrant/dev_env_config >> ~/.bashrc
echo "*** here is the .bashrc file:"
cat ~/.bashrc
SCRIPT
config.vm.provision "shell", inline: $install_user_vars
end
I think what's happening is the provisioning script is run as root (or sudo), so the "~" home location is actually /root rather than the default user home location /home/vagrant.
I can think of a couple ways to solve this:
First (and probably easiest) is to be explicit about the .bashrc path, like:
# Vagrantfile
Vagrant.configure("2") do |config|
... various cmds to set box and network...
$install_user_vars = <<SCRIPT
sudo cat /vagrant/dev_env_config >> /home/vagrant/.bashrc
echo "*** here is the .bashrc file:"
cat /home/vagrant/.bashrc
SCRIPT
config.vm.provision "shell", inline: $install_user_vars
end
The second option could be to run this part of the provisioning script as a non-privileged user. See the 'privileged' option on the Shell Scripts docs page.
A primitive solution is to set the path to .bashrc explicitly. As a rule default username of a SSH user (which will be used for vagrant ssh action) is vagrant so:
$install_user_vars = <<SCRIPT
sudo cat /vagrant/dev_env_config >> /home/vagrant/.bashrc
echo "*** here is the .bashrc file:"
cat /home/vagrant/.bashrc
SCRIPT
Also I'm not sure that it's a necessary to use a sudo command (in sudo cat ...). Probably you don't need it, but it depends on which user is used to run a provision script. I guess it's also vagrant.
So if it's really a vagrant you could leave the path to .bashrc unmodified (~/.bashrc), but have to remove sudo cat ... command and use simple cat ... instead. And it's a more clean solution in my opinion. Because actually we shouldn't use sudo (root) permissions without need.

Install Oh My Zsh on a Vagrant Box as part of the bootstrap process

I'd like to add Oh My Zsh to my Vagrant bootstrap process, but a straight install isn't working.
via curl:
curl -L http://install.ohmyz.sh | sh
via wget:
wget --no-check-certificate http://install.ohmyz.sh -O - | sh
Found the solution:
# Added zsh shell.
sudo apt-get install zsh
wget --no-check-certificate https://github.com/robbyrussell/oh-my-zsh/raw/master/tools/install.sh -O - | sh
sudo chsh -s /bin/zsh vagrant
zsh
As an nice addition, so that your terminals don't look too similar on the different boxes
# Change the oh my zsh default theme.
sed -i 's/ZSH_THEME="robbyrussell"/ZSH_THEME="3den"/g' ~/.zshrc
Here's a complete Vagrantfile that installs Oh My Zsh on an Ubuntu 14.04.2 LTS box and sets it as the default shell for standard vagrant user.
This works with Vagrant 1.7.2. (Your milage may vary with different versions.) It uses the directions from the Manual Installation section of the Readme instead of trying to use the automatic scripts.
# -*- mode: ruby -*-
# vi: set ft=ruby :
VAGRANTFILE_API_VERSION = "2"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
# Pick a box to use:
config.vm.box = "ubuntu/trusty64"
############################################################
# Oh My ZSH Install section
# Install git and zsh prerequisites
config.vm.provision :shell, inline: "apt-get -y install git"
config.vm.provision :shell, inline: "apt-get -y install zsh"
# Clone Oh My Zsh from the git repo
config.vm.provision :shell, privileged: false,
inline: "git clone git://github.com/robbyrussell/oh-my-zsh.git ~/.oh-my-zsh"
# Copy in the default .zshrc config file
config.vm.provision :shell, privileged: false,
inline: "cp ~/.oh-my-zsh/templates/zshrc.zsh-template ~/.zshrc"
# Change the vagrant user's shell to use zsh
config.vm.provision :shell, inline: "chsh -s /bin/zsh vagrant"
############################################################
end
As a bonus, you can do a one time copy of your host machine's .zshrc file to the vagrant box with:
config.vm.provision "file", source: "~/.zshrc", destination: ".zshrc"
(Keep in mind, You may have to figure things that don't work initially because of differences between the host machine and the vagrant box's setups.)
I came here because had a same issue. After seeing some answer and trying it, mostly the zsh & oh-my-zsh got installed as root. The root will set his $SHELL with zsh. What I want is they are installed as user vagrant. The bootstrap was done by root when provisioning. So the logic is try to run install zsh & oh-my-zsh as user. Here is what I did after trying many times until I got what I wanted :
## In Vagrantfile try to call bootstrap.sh
config.vm.provision "shell", path: "bootstrap.sh"
## This is the bootstrap.sh
aptInstl() {
DEBIAN_FRONTEND=noninteractive apt-get install -qq -y $1 > /dev/null
}
install_zsh() {
aptInstl "zsh"
su -l vagrant -s "/bin/sh" \
-c "curl -fsSO https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh; chmod 755 install.sh; ./install.sh --unattended"
chsh -s /bin/zsh vagrant
}
install_miscellaneous() {
apt-get update > /dev/null
apt-get upgrade > /dev/null
for i in curl git; do
aptInstl "$i"
done
}
main() {
install_miscellaneous
install_zsh
}
main
And It works perfectly :)
When you are done try to vagrant ssh it will automatically logged you in with zsh shell and oh-my-zsh. Here is the complete file.

Setting up Shell Script for Vagrant Setup

Trying to write a shell script to setup my server environment on Ubuntu through Vagrant and am running into a problem where the script ends unexpectedly. I added the path to the shell script in Vagrant's provision config option.
Vagrant:
# Specify our provision script
config.vm.provision "shell", path: "scripts/bootstrap.sh"
My script:
#!/bin/bash
# Install dependencies for Ruby
sudo apt-get update
sudo apt-get install -y 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
# Setting up rbenv
echo 'Setting up rbenv'
git clone git://github.com/sstephenson/rbenv.git .rbenv
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(rbenv init -)"' >> ~/.bashrc
exec $SHELL
After running, I expect the repository to be cloned into the .rbenv folder and to have rbenv added to $PATH in ~/.bashrc along with the rbenv init function evaluated and put into ~/.bashrc. However, when the script is executed on Vagrant's provision step I end up with the script just cloning the git repository and then terminating without executing anything else in my script.
Output:
==> default: Processing triggers for libc-bin (2.19-0ubuntu6) ...
==> default: Setting up rbenv
==> default: Cloning into '.rbenv'...
And then the script terminates and ~/.bashrc is left unchanged. I was wondering how I can change my shell script so that it will perform the action I want (which is adding rbenv to ~/.bashrc). Any ideas?
As mklement0 said, the script does not run as the vagrant user: it runs as root.
If you want to run the script as the vagrant user you need privileged: false.
config.vm.provision :shell, privileged: false, path: "scripts/bootstrap.sh"
As mklement0 said: use set -xv to debug your provisioning scripts.
If you want to run as another user, don't forget that su user won't work: How do I use su to execute the rest of the bash script as that user?
If you want execute some script like vagrant user, try this.
su vagrant -l -c "echo Hello world"

Automatically chdir to vagrant directory upon "vagrant ssh"

So, I've got a bunch of vagrant VMs running some flavor of Linux (centos, ubuntu, whatever). I would like to automatically ensure that a "vagrant ssh" will also "cd /vagrant" so that no-one has to remember to do that whenever they log in.
I've figured out (duh!) that echo "\n\ncd /vagrant" >> /home/vagrant/.bashrc will do the trick. What I don't know is how to ensure that this only happens if the cd command isn't already there. I'm not a shell expert, so I'm completely confused here. :)
You can do this by using the config.ssh.extra_args setting in your Vagrantfile:
config.ssh.extra_args = ["-t", "cd /vagrant; bash --login"]
Then anytime you run vagrant ssh you will be in the /vagrant directory.
I put
echo "cd /vagrant_projects/my-project" >> /home/vagrant/.bashrc
in my provision.sh, and it works like a charm.
cd is a Bash shell built-in, as long as a shell is installed it should be there.
Also, be aware that ~/.bash_profile is for interactive login shell, if you add cd /vagrant in ~vagrant/.bashrc, it may NOT work.
Because distros like Ubuntu does NOT have this file -> ~/.bash_profile by default and instead use ~/.bashrc and ~/.profile
If someone creates a ~/.bash_profile for vagrant user on Ubuntu, ~vagrant/.bashrc will not be read.
You need to add cd /vagrant to your .bashrc in the vm. The best way to do this is in your provisioner script.
If you don't have a provisioner script, make one by adding this line to your Vagrantfile before end:
config.vm.provision "shell", path: "scripts/vagrant/provisioner.sh", privileged: false
Path is relative to the project root where the Vagrantfile is, and privileged depends on your project and what else is in your provisioner script which might need to be privileged. I use priveleged false and sudo explicitly when necessary.
And in the provisioner script:
if ! grep -q "cd /vagrant" ~/.bashrc ; then
echo "cd /vagrant" >> ~/.bashrc
fi
This will add cd /vagrant to .bashrc, but only if it isn't there already. This is useful if you reprovision, as it will prevent your .bashrc from getting cluttered.
Some answers mention a conflict with .bash_profile. If the above code doesn't work, you can try the same line with .bash_profile or .profile instead of .bashrc. However, I've been using vagrant with ubuntu guests. My Laravel/homestead box based on Ubuntu has a .bash_profile and a .profile but having cd /vagrant in .bashrc did work for me when using vagrant ssh without changing or deleting the other files.
You can add cd /vagrant to your .bashrc and it will run the command when you ssh. The /bashrc you want is in /home/vagrant (the user you login as when you vagrant ssh.) You can just stick the new line at the bottom of the file.
You can also do it this way:
vagrant ssh -c "cd /vagrant && bash"
And you could include it in a script to launch it (like ./vagrant-ssh).
May be this can help. Edit the Vagrantfile as replace your username with vagrant
`
config.vm.provision "shell" do |s|
s.inline = <<-SHELL
# Change directory automatically on ssh login
if ! grep -qF "cd /home/vagrant/ansible" /home/vagrant/.bashrc ;
then echo "cd /home/vagrant/ansible" >> /home/vagrant/.bashrc ; fi
chown vagrant. /home/vagrant/.bashrc
`
Ideally we just want to alter the vagrant ssh behaviour.
In my case, I wanted something that didn't affect any other processes in the environment, so we can do something like this in the vagrant file-
VAGRANT_COMMAND = ARGV[0]
if VAGRANT_COMMAND == "ssh"
config.ssh.extra_args = ["-t", "cd /vagrant; bash --login"]
end
You can use Ansible to assert that your .bashrc file contains cd /vagrant.
If you are not already using the Ansible provisioner for your VM, add the following lines to your Vagrantfile:
config.vm.provision "ansible_local" do |ansible|
ansible.playbook = "provisioning/playbook.yml"
end
And in your playbook, add the following task/play:
---
- hosts: all
gather_facts: no
tasks:
- name: chdir to vagrant directory
ansible.builtin.lineinfile:
path: /home/vagrant/.bashrc
line: cd /vagrant
According to this Q&A, I would recommend to modify .bashrc instead of .profile or .bash_profile.

Resources