Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

user input on provision #2662

Closed
mathroc opened this issue Dec 16, 2013 · 17 comments
Closed

user input on provision #2662

mathroc opened this issue Dec 16, 2013 · 17 comments

Comments

@mathroc
Copy link

mathroc commented Dec 16, 2013

I'd like to ask developers for their name & email when bringing up the vm to have a fully configured git as soon as the vm is up (name & email from git config is also used in my capistrano deployment script)

for now, I only have dirty workaround : either asking to fill in a .gitignored file or running vagrant ssh git config --global user.name <My Name> and vagrant ssh git config --global user.email <my.name@my.org>

both could be forgotten.

ideally I'd like to use something like read -p 'enter your email: ' email in scripts. but i understand it can be difficult to implement. an alternative would be to let vagrant read user input and make it available as env variables in the guest, eg:

Vagrant.configure("2") do |config|
  config.vm.env "dev_email", :ask => "enter your email: ", :once => true
end

before any provisonning, vagrant would then ask the user for its email and make it available as dev_email env variable inside the guest.

the :once option would mean to only ask this once and store it somewhere, that would require some sort of option (eg vagrant provision --reset-user-env) to be able to edit those env variables

note that config.vm.env could be used for other purposes, eg:

Vagrant.configure("2") do |config|
  config.vm.env "APP_ENV", :value => "test"
end

I don't know what would be the best alternative to real user input so this is just my proposition to get the discussion started.

what do you think about it?

@mitchellh
Copy link
Contributor

I think this would be a good plugin as a proof of concept. Otherwise, I recommend just using environmental variables and checking for them in the Vagrantfile using Ruby.

@bdw429s
Copy link

bdw429s commented Jul 14, 2015

Was there even a proof of concept created? I'm also in need of collecting information from the user. This is a direct requirement from my client's security team that provisioning passwords not be stored anywhere but entered by the user at run time.

@Ajedi32
Copy link

Ajedi32 commented Jul 15, 2015

@bdclark Worst-case, you could always just write a bit of Ruby code to prompt the user in the Vagrantfile itself. I'm assuming that would work. E.g. (print "Enter your password: "; gets)

@bdw429s
Copy link

bdw429s commented Jul 15, 2015

I actually found a proof of concept out there that used some Ruby. However, the biggest issue was that it runs EVERY time you do a vagrant command-- not just on provisioning.

module MySvc
  def self.password
    begin
      system 'stty -echo'
      print 'MySvc Password: '
      ; pass = $stdin.gets.chomp; puts "\n"
    ensure
      system 'stty echo'
    end
    pass
  end
end

# Init log file
config.vm.provision :shell, :path => "provisioners/setup-log.sh", :privileged => true, :args => [ MySvc.password() ]

@Ajedi32
Copy link

Ajedi32 commented Jul 16, 2015

I don't have a solution for the "always prompt" bit (though perhaps you could check ARGV or inspect the internal state of Vagrant to see what the command used was? Not sure.), but fyi you don't need the stty hack. Just require 'io/console', then use STDIN.noecho(&:gets). Does basically the same thing, but it's much cleaner.

The main problem I have with using Vagrant is that this prompt will likely be needed when I'm running Chef without Vagrant too. I'd like to put this in my Chef recipes, but Vagrant doesn't forward input from the console to Chef.

@mpchlets
Copy link

I have kind of dirty way to accomplish what you need in the Vagrantfile:

  if Dir.glob("#{File.dirname(__FILE__)}/.vagrant/machines/#{vm_name}/*").empty?
    print "Username: "
    username = STDIN.gets.chomp
    print "Email: "
    email = STDIN.gets.chomp
    cmd = "git config --global user.name #{username}; git config --global user.email #{email}"
    config.vm.provision :shell, :inline => cmd, :privileged => false
  end

The if statement checks to see if this is the first run or not. Then we get the info we need, and run the provisioner.

@LongLiveCHIEF
Copy link

If we're using vagrant to set environment variables (say for example a proxy password), then there are cases where you would want to get that info some way directly from the user, prior to the provisioners being executed.

For example, we have a script that sets up proxy and no proxy usage, including password. There is no way to automatically retrieve the users password... they have to physically enter it. This only works under certain conditions (must be using specific shell, on one of two specific host OS's configured the same way so the shell will use the script).

turning this into a vagrant provisioner would be much simpler, allowing our devs to use purely vagrant boxes to code, and not having to rely on the host fs/configs at all.

Having each developer go into their own host system and set their password somewhere is counter to the "Tao of Vagrant", and also causes issues when you have developers that don't know how to do these things, but just need them to work on the vagrant boxes.

(You'd be surprised at how many developers don't know anything at all about system configs).

@ldong
Copy link

ldong commented Oct 20, 2015

@bdw429s Cool POC.

Too bad that Vagrant provision itself is not reading from stdin, so I guess the proper way of handling the inputs are reading from a file.

@LongLiveCHIEF
Copy link

I'm working on a POC that has a thin wrapper around Vagrant where you can prompt for user input, and sequence stages of Vagrant provisioning. This is one of our biggest use cases for the development of that tool (comes with a custom provisioner and provider also).

I'll try to remember to update this issue when we officially can release it.

@Pysis868
Copy link

@LongLiveCHIEF Make it yet?
@mpchlets Where do you get vm_name from?

@LongLiveCHIEF
Copy link

Make it yet?

Most of it. Haven't been able to release it as OS yet, but hopefully will be able to before end of the year.

@LongLiveCHIEF
Copy link

However, what I can share, is that you could create your own convention for doing this quite easily, by creating a base box, and using an include file that looks in the relative vagrant directory for some other file, and loads that file.

This file could be a manifest file.

what you can then also do, is crate a small shell script on your path that you call vg-up or something like that, and it would see if there is a manifest file (the same one your base box would look for), and check for any nil/null values, and prompt for those... and write them to the file, and run vagrant up for you.

The stuff I wrote is far more comprehensive and capable, but I found that those two tricks together allowed me to accomplish quite a bit across all of my systems, with only about 50 minutes of effort.

@rollerd
Copy link

rollerd commented Jan 5, 2017

Building off of @Ajedi32 and @mpchlets solution, you can check for the arg to the vagrant command to prevent the script from running on say a vagrant status in a directory where a machine wasn't created yet:

vagrant_arg = ARGV[0]
if Dir.glob("#{File.dirname(__FILE__)}/.vagrant/machines/#{vm_name}/*").empty? and vagrant_arg == 'up'
...
end

@gza
Copy link

gza commented Sep 12, 2017

Just to share a solution that fixes the "bigissue" of #2662 (comment)

input is only asked on provision execution

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

note 1: tricks this code line https://github.com/mitchellh/vagrant/blob/master/plugins/provisioners/shell/provisioner.rb#L53
note 2: tested on v 1.9.5, windows binary in a "git for windows" terminal (with winpty): should work on unix

@toddbu
Copy link

toddbu commented Oct 1, 2017

The idea of using Ruby to collect the username and password is a good first start however its problematic if the username and/or password are entered incorrectly. Getting back to what the OP said about using read -p 'enter your email: ' email inside the provisioner, this would allow the script to handle password failures and ask for the password again

@charles-d-burton
Copy link

gza's solution doesn't seem to work if you have special characters. Ruby encodes them to their codes rather than passes the characters directly.

@ghost
Copy link

ghost commented Mar 30, 2020

I'm going to lock this issue because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues.

If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.

@ghost ghost locked and limited conversation to collaborators Mar 30, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests