88

It looks like passing environment variables when calling vagrant up is simple if you're using a Ruby provisioner:

VAR=123 vagrant up

In the Vagrantfile:

ENV['VAR']

How do I do this with the :shell provisioner? Simply doing this does not seem to work:

$VAR
Jay Prall
  • 5,295
  • 5
  • 49
  • 79
Abdullah Jibaly
  • 53,220
  • 42
  • 124
  • 197

12 Answers12

94

Since Vagrant 1.8.0 you can forget the ugly hacks from the other answers here. Just use the env option for the shell provisioner (docs).

Use it like this in your Vagrantfile:

config.vm.provision "shell", path: "provisionscript.sh", env: {"MYVAR" => "value"}

This will set the environment for the provisioning script only. If you need a persistent environment variable set for all processes in the VM, this is out of scope for Vagrant provisioning and look here: Shell environment variables in vagrant files are only passed on first up.

Community
  • 1
  • 1
gertvdijk
  • 24,056
  • 6
  • 41
  • 67
  • In Vagrant 1.8.5, the `env` parameter only seems to work when using a `path` script, not an `inline` script. – spyle Aug 28 '16 at 13:08
  • 2
    @spyle I'm running Vagrant 1.8.5 (with VirtualBox 5.1.2 on Mac OS X 10.11.6) and `env` works with `inline`. – jonatan Sep 05 '16 at 13:16
  • Hmm... am I missing something here? This is not working for me: `config.vm.provision :shell, path: "bootstrap.sh", env: {"MYSQL_DB_USERNAME"=>"django", "MYSQL_DB_PASSWORD"=>"supersecretpasswordwasreplaced"}` On the other side `os.environ['MYSQL_DB_USERNAME']` gives a key error :( – Tadhg Oct 24 '16 at 21:07
  • 1
    @Tadhg Double check your Vagrant version. And perhaps ask a new question if it doesn't work? It might be an unrelated issue (e.g. Vagrantfile version or environment that isn't preserved in your chain of tools). – gertvdijk Oct 25 '16 at 08:55
  • Version is good. You're correct, looking at the doc, it seems that this config is only supposed to work at provisioning time, so I asked this as a new question http://stackoverflow.com/questions/40270391/shell-environment-variables-in-vagrant-files-are-only-passed-on-first-up – Tadhg Oct 26 '16 at 19:14
  • "it seems that this config is only supposed to work at provisioning time" -> yeah, it's a `config.vm.provision` configuration option... and this question is actually about provisioning. :) – gertvdijk Oct 27 '16 at 07:20
  • this only explains how you send it but not how you use it.. Can you use it inside `<<-SHELL ... SHELL` ? If not can you combine that with a normal version? For me it only outputs `os.environ['variable']` directly without evaluating it. I have this: `sudo sed -i -e "s/-Xms.*/os.environ['ELASTIC_XMS']/g" /etc/elasticsearch/jvm.options` – OZZIE Jul 12 '18 at 11:40
  • @OZZIE: If you are trying to use it in the Vagrantfile itself, it makes no sense to use *environment* variables. Just use plain variables instead. Environment variables are supposed to be consumed in the process spawned here - the provisioning script. You can access them in any way that's suitable for that scripting language of the script invoked. – gertvdijk Jul 26 '18 at 21:32
44

It's not ideal, but I got this to work for now:

config.vm.provision "shell" do |s|
    s.inline = "VAR1 is $1 and VAR2 is $2"
    s.args   = "#{ENV['VAR1']} #{ENV['VAR2']}"
end
birnbaum
  • 4,718
  • 28
  • 37
Abdullah Jibaly
  • 53,220
  • 42
  • 124
  • 197
  • 3
    You may also replace `s.inline` by `s.path` and use the same script you would have passed with `:path => ""`. (My provisioner is hundreds of lines and therefore ill-suited to inlining.) – msanford Jan 14 '14 at 19:23
  • 3
    Alternatively, using the ruby method with an `:args => ""` parameter, [like so](http://stackoverflow.com/questions/15461898/passing-variable-to-a-shell-script-provisioner-in-vagrant). – msanford Jan 16 '14 at 14:51
  • 4
    Should you really have a comma inside `s.args`? – Marius Gedminas May 20 '15 at 13:40
20

For posterity (aka in case I google it again)... It's possible to pass key-value pairs via env:

box.vm.provision :shell do |s|
  s.env = {AWS_ACCESS_KEY:ENV['AWS_ACCESS_KEY'], AWS_SECRET_KEY:ENV['AWS_SECRET_KEY']}
  s.path = 'scripts/bootstrap.sh'
end

Then reference them in your script:

export AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY}
export AWS_SECRET_ACCESS_KEY=${AWS_SECRET_KEY}

Bonus feature:

Vagrant will handle quoting for environment variable values, but the keys remain untouched

Al Belsky
  • 1,544
  • 13
  • 15
16

I came with this solution for CentOS based provisioning: placing all my required envioronment variables in /etc/profile.d/vagrant.sh file and then it's accessed in any provision script.

in short:

  $before_script = <<SCRIPT
  echo # vagrant profile script > /etc/profile.d/vagrant.sh
  echo export ENV_VAR1=foo.com/bar >> /etc/profile.d/vagrant.sh
  echo export ENV_VAR2=bar.com/foo >> /etc/profile.d/vagrant.sh
  chmod +x /etc/profile.d/vagrant.sh
SCRIPT

  $after_script = <<SCRIPT
    rm -rf /etc/profile.d/vagrant.sh
SCRIPT

  config.vm.provision "shell", inline: $before_script
  config.vm.provision "shell", path: "build.sh"
  config.vm.provision "shell", inline: $after_script

Complete Vagrantfile can be found here https://gist.github.com/bivas/6192d6e422f8ff87c29d

Bivas
  • 1,484
  • 1
  • 11
  • 17
8

You can use #{ENV['VAR']} inside an inline script, e.g.:

config.vm.provision "shell", inline: <<-END
  ...
  # Install my dotfiles are there.  If you're in a hurry you can do
  # SKIP_DOTFILES=1 vagrant up
  if ! [ -d /home/vagrant/dotfiles ] && [ -z '#{ENV['SKIP_DOTFILES']}']; then
    if ! [ -x /usr/bin/git ]; then
      DEBIAN_FRONTEND=noninteractive apt-get install -y git
    fi
    su - vagrant -c 'git clone https://github.com/mgedmin/dotfiles'
    su - vagrant -c 'dotfiles/install.sh'
  fi
  ...
  END

Example taken from a working Vagrantfile.

This has some disadvantages: if $VAR contains single quotes, things will break.

Marius Gedminas
  • 11,010
  • 4
  • 41
  • 39
7

Incase someone ends up looking for how to set variables in the provisioning script's environment, this worked for me.

config.vm.provision :shell, :inline => <<-SH
  export GRAPHITE_HOST=192.168.33.10
  /vagrant/install_app_with_monitoring.sh
SH

Note that this assumes you're sharing your working directory as /vagrant on the VM but this should be the default.

Mat Schaffer
  • 1,634
  • 1
  • 15
  • 24
  • but why not do this inside `install_app_with_monitoring.sh`? – Stephan Bijzitter Oct 26 '16 at 07:17
  • Mainly to keep knowledge in the same place. You can see an example here https://github.com/forty9ten/monitoring-with-graphite/blob/master/Vagrantfile#L8-L29 By keeping the var in the Vagrantfile it's easy to ensure parity between `private_network` and `GRAPHITE_HOST`. Could even take it a step further and use `export GRAPHITE_HOST=#{ip}` to reference a single common ruby var. – Mat Schaffer Oct 28 '16 at 03:22
6

Most of these answers seems to be outdated. With Vagrant 2.1.1 this worked for me:

  VAGRANTFILE_API_VERSION = "2" //...

  machine.vm.provision "shell", 
    env: {
      "ELASTIC_XMS" => servers["elastic"]["memory_xms"], 
      "ELASTIC_XMX" => servers["elastic"]["memory_xmx"]
    }, 
    inline: "sed -i -e \"s/-Xms.*/$ELASTIC_XMS/g\" /etc/elasticsearch/jvm.options"
OZZIE
  • 6,609
  • 7
  • 55
  • 59
2

The vagrant-env plugin does exactly this. With it, you can add environment variables to .env file in the local directory which will be loaded in Vagrant file. I suggest to keep .env in your .gitignore, this way you have your privacy guaranteed.

Shairon Toledo
  • 2,024
  • 16
  • 18
1

Here's how I have it working.

I went from using the vagrant puppet provisioner way to just using the shell provisioner. I did this mainly because I wanted to puppet not to run as root, shell provider gives you :privileged => false.

MY OLD WAY:

config.vm.provision :puppet do |puppet|
  puppet.module_path = ENV.fetch('MODULES_PATH', 'modules')
  puppet.manifests_path = ENV.fetch('MANIFESTS_PATH', 'manifests')
  puppet.manifest_file  = ENV.fetch('MANIFEST_FILE', 'site.pp')
  puppet.options = "--debug"
end

MY NEW WAY:

config.vm.provision :shell, :privileged => false do |shell|
  shell.inline = "puppet apply --debug --modulepath '/vagrant/#{ENV.fetch('MODULES_PATH', 'modules')}' --detailed-exitcodes '/vagrant/#{ENV.fetch('MANIFESTS_PATH', 'manifests')}/#{ENV.fetch('MANIFEST_FILE', 'site.pp')}'"
end
Kuberchaun
  • 29,160
  • 7
  • 51
  • 59
1

You can specify for shell using inlinein your Vagrantfile file:

config.vm.provision "shell", inline: %Q(/usr/bin/env FOO=1 BAR=1 bash /path/to/script.sh)

Or load some extra variables from YAML file:

require 'yaml'
dir = File.dirname(File.expand_path(__FILE__))
vconfig = YAML::load_file("#{dir}/config.yml")
config.vm.provision "shell", inline: %Q(/usr/bin/env FOO=#{vconfig['foo']} bash /path/to/script.sh)

Alternatively you may implement some optional arguments from the command line, e.g.:

# Parse optional arguments.
opts = GetoptLong.new(
  [ '--foo',  GetoptLong::OPTIONAL_ARGUMENT ], # With optional parameter.
  [ '--bar',  GetoptLong::OPTIONAL_ARGUMENT ], # With optional parameter.files.
)
opts.each do |opt, arg|
  case opt
    when '--foo'
      foo==arg
    when '--bar'
      bar=arg
  end
end

then use: opt['--foo'].to_s.

See also: How to pass parameter on Vagrant up and have it in the scope of Chef cookbook?

Florian
  • 2,562
  • 5
  • 25
  • 35
kenorb
  • 155,785
  • 88
  • 678
  • 743
-3

this worked for me

VAGRANTFILE_API_VERSION = "2"

kettle_dir = ENV['KETTLE_DIR']
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
   config.vm.synced_folder kettle_dir, "/pentaho"
   config.vm.box = "ubuntu/trusty64"
end
itb
  • 7
  • Can you put in some detail to indicate why this is the correct answer? – rfornal Jan 13 '15 at 14:59
  • 1
    This uses an environment variable in the Vagrantfile, but doesn't actually set one for the provisioner as asked in the question. – btubbs Jan 14 '15 at 23:12
-3

On a ubutnu box I simply did the following in my bootstrap.sh:

echo "DBHOST=localhost" >> /etc/environment
echo "DBNAME=foo" >> /etc/environment
echo "DBUSER=root" >> /etc/environment
echo "DBPASSWD=root" >> /etc/environment
yerachw
  • 47
  • 5