How to pass parameter on 'vagrant up' and have it in the scope of Vagrantfile?

It is possible to read variables from ARGV and then remove them from it before proceeding to configuration phase. It feels icky to modify ARGV but I couldn't find any other way for command-line options.

Vagrantfile

# Parse options
options = {}
options[:port_guest] = ARGV[1] || 8080
options[:port_host] = ARGV[2] || 8080
options[:port_guest] = Integer(options[:port_guest])
options[:port_host] = Integer(options[:port_host])

ARGV.delete_at(1)
ARGV.delete_at(1)

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  # Create a forwarded port mapping for web server
  config.vm.network :forwarded_port, guest: options[:port_guest], host: options[:port_host]

  # Run shell provisioner
  config.vm.provision :shell, :path => "provision.sh", :args => "-g" + options[:port_guest].to_s + " -h" + options[:port_host].to_s

 

provision.sh

port_guest=8080
port_host=8080

while getopts ":g:h:" opt; do
    case "$opt" in
        g)
            port_guest="$OPTARG" ;;
        h)
            port_host="$OPTARG" ;;
    esac
done

You cannot pass any parameter to vagrant. The only way is to use environment variables

MY_VAR='my value' vagrant up

And use ENV['MY_VAR'] in recipe.


You also can include the GetoptLong Ruby library that allows you to parse command line options.

Vagrantfile

require 'getoptlong'

opts = GetoptLong.new(
  [ '--custom-option', GetoptLong::OPTIONAL_ARGUMENT ]
)

customParameter=''

opts.each do |opt, arg|
  case opt
    when '--custom-option'
      customParameter=arg
  end
end

Vagrant.configure("2") do |config|
             ...
    config.vm.provision :shell do |s|
        s.args = "#{customParameter}"
    end
end

Then, you can run :

$ vagrant --custom-option=option up
$ vagrant --custom-option=option provision

Note: Make sure that the custom option is specified before the vagrant command to avoid an invalid option validation error.

More information about the library here.


@benjamin-gauthier 's GetoptLong solution is really neat, fits in with the ruby and vagrant paradigm well.

It however, needs one extra line to fix clean handling of the vagrant arguments, such as vagrant destroy -f.

require 'getoptlong'

opts = GetoptLong.new(
  [ '--custom-option', GetoptLong::OPTIONAL_ARGUMENT ]
)

customParameter=''

opts.ordering=(GetoptLong::REQUIRE_ORDER)   ### this line.

opts.each do |opt, arg|
  case opt
    when '--custom-option'
      customParameter=arg
  end
end

which allows this block of code to pause when the custom options are processed. so now, vagrant --custom-option up --provision or vagrant destroy -f are cleanly handled.

Hope this helps,