91

I'm looking for a way to pass parameter to Chef cookbook like:

$ vagrant up some_parameter

And then use some_parameter inside one of the Chef cookbooks.

kenorb
  • 155,785
  • 88
  • 678
  • 743
Wojciech Bednarski
  • 6,033
  • 9
  • 49
  • 73

5 Answers5

120

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.

Draco Ater
  • 20,820
  • 8
  • 62
  • 86
78

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.

kenorb
  • 155,785
  • 88
  • 678
  • 743
  • Have you actually tried this and confirmed to have it working? For I have tried unsuccessfully with the `optparse` library - and therefore I am a bit skeptical this would work. – BogdanSorlea Jul 20 '15 at 15:50
  • 1
    I'm using it all day since I posted. It works very well ! What's your problem ? – Benjamin Gauthier Jul 21 '15 at 17:56
  • 14
    It seems that the options are not listed in the `opts` not processed: `vagrant --custom-option=option destroy -f` `vagrant: invalid option -- f` – Renat Zaripov Aug 07 '15 at 09:21
  • 2
    Yes, this works, and imho is more elegant than the first answer. – davidav Sep 01 '15 at 12:34
  • @BenjaminGauthier Are the options that are matched in the "opts.each do" block destroyed, i.e. vagrant isn't confused by them afterwards? – Jérémie Nov 14 '15 at 13:56
  • 2
    @BenjaminGauthier The docs say "The empty option -- (two minus symbols) is used to end option processing.". So `vagrant --custom-option=option -- up` should be enough – CESCO Jan 20 '16 at 06:19
  • This fails when you do `vagrant provision --provision-with` – Radu Aug 08 '16 at 21:31
  • This seems to work with Vagrant 1.8.7, including the standard `--provision-with` option after the custom one: `vagrant --custom-option=foo -- --provision-with=shell up` (note the use of `--` to stop processing custom options). More testing needed though. – RichVel Nov 29 '16 at 11:06
  • 1
    Was anyone able to away with out using `vagrant -- destroy -f` seems that now the `-f` doesn't pass the force flag at all, still need to type `[y/N]`. I got it working with a default parameters when running `vagrant up` but the `destroy` requires `--` any ideas? – MMT Feb 01 '17 at 14:25
  • See here for a solution how to add the native Vagrant option, which would remove the problem with e.g. `-f`: https://gist.github.com/ProxiBlue/0b977416f6c6d20c4f7bb562a9ae64ad – Stefan Walther May 30 '17 at 14:40
  • Invalid option issue can be fixed by adding some try catch logic. `begin 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 rescue StandardError => boom end` – Brian van Rooijen Aug 25 '17 at 12:50
  • As reported in [this issue in Vagrant](https://github.com/hashicorp/vagrant/issues/2064), this solution does not seem to work with all versions of Vagrant and Ruby. – CristianCantoro Nov 16 '17 at 13:28
  • 3
    This does not work with Vagrant 2 anymore. It does not accept any parameters beside its own. – Jens Baitinger Mar 14 '18 at 07:58
24

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
Community
  • 1
  • 1
tsuriga
  • 675
  • 5
  • 7
  • This doesn't seem to work for me. I always get the error _An invalid option was specified_. Doing `puts ARGV` displays correct array after removal of extra custom arguments. – majkinetor Mar 09 '15 at 07:47
  • 1
    Same here, it does not work... I put a `puts "#{ARGV}"` line in `vagrant/embedded/gems/gems/vagrant-1.7.2/lib/vagrant/plugin/v2/command.rb` and it prints that line before the removal of the relevant args in the Vagrantfile, thus meaning that the removal is futile as the ARGV is passed to the validator that outputs `An invalid option was specified` before any operations can take place on ARGV. – BogdanSorlea Jul 20 '15 at 15:47
9

@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,

4
Vagrant.configure("2") do |config|

    class Username
        def to_s
            print "Virtual machine needs you proxy user and password.\n"
            print "Username: " 
            STDIN.gets.chomp
        end
    end

    class Password
        def to_s
            begin
            system 'stty -echo'
            print "Password: "
            pass = URI.escape(STDIN.gets.chomp)
            ensure
            system 'stty echo'
            end
            pass
        end
    end

    config.vm.provision "shell", env: {"USERNAME" => Username.new, "PASSWORD" => Password.new}, inline: <<-SHELL
        echo username: $USERNAME
        echo password: $PASSWORD
SHELL
    end
end
Suraj Rao
  • 29,388
  • 11
  • 94
  • 103
sophia
  • 41
  • 1
  • 1
    This is a good solution but can you show how to use this non-interactively? Is it possible to pipe the args for example? – Fuzzy Logic Oct 11 '21 at 22:33