In my vagrantfile I use two lines of rudy code to get the username and password from the console and pass it on to the shell script for creating an additional user account instead of the default vagrant
This works well for vagrant up but it beats me by prompting the same when I run any other vagrant commands like vagrant status. Is there a way to catch if the vagrant file is called with up command?
require 'io/console'
print "Please enter username: "
#username = STDIN.gets
print "Please enter password: "
#password = STDIN.noecho(&:gets)
Vagrant.configure("2") do |config|
config.vm.box = "generic/debian10"
["Node 01"].each do |node_name|
config.vm.define node_name do |node|
node.vm.provision "shell" do |p|
p.args = [#username, #password]
p.path = "create_user.sh"
end
end
end
end
UPDATE
After #Matt Schuchard's suggestion, I've tried triggers; However, the global variables that are captured inside the trigger block are not shared with the place where I pass them into the shell script (p.args = [#username, #password])
config.trigger.before :up do |trigger|
if #credential_captured == nil # I've used this to prevent executing the following block per each vm
print "Please enter username: "
#username = STDIN.gets
print "Please enter password: "
#password = STDIN.noecho(&:gets)
#credential_captured = true
end
end
Sprinkle Some Plain Ruby into the Vagrantfile
The best practice would really be to put an idempotent provisioning block at the end that picks up your information from the environment or some other fixed location rather than trying to do this as interactive user input within the Vagrantfile. However, if you really want to do it the way you're doing it, just remember that a Vagrantfile is mostly just a DSL on top of Ruby.
You can get the status of a given virtual machine with vagrant status. For a single node, you could just wrap your current prompts at the top in a conditional. For example:
# assuming a single node named "default"
if %x(vagrant status default) =~ /running/
# prompt here
end
If you're running multiple nodes, then I'd suggest wrapping your prompts into a method and moving your conditional down into the body of the shell provisioning block you already have. For example:
def prompt_user
# prompt here
end
# ...
if `vagrant status #{node_name}` =~ running
prompt_user unless #username && #password
node.vm.provision 'shell' do |p|
# provision your node here
end
end
There are likely ways to make this code more efficient, and probably other ways to do what you want, but this approach is fairly straightforward and should at least get you headed in the right direction.
Related
tldr; I need to run a command as admin and retrieve the output
I've already created a bat file that does the job but I want to translate it to pure ruby as a learning exercise.
Using backticks I can get the output from the command, unfortunately the output tells me I lack the user privileges. Through this site I've found how to poke UAC in the right way, but I can't seem to get it to recognise the command I want which is (bcdedit /v)
I've read through https://learn.microsoft.com/en-us/windows/desktop/shell/shell-shellexecute & Detect if running with administrator privileges under Windows XP
and numerous stack overflow posts with similar questions but haven't found clear guidelines to do this.
def running_in_admin_mode?
(`reg query HKU\\S-1-5-19 2>&1` =~ /ERROR/).nil?
end
unless running_in_admin_mode?
shell = WIN32OLE.new('Shell.Application')
shell.ShellExecute("ruby", File.expand_path(__FILE__), nil, 'runas')
exit
end
update: so this sort of works. It flashes up the output I want in an IRB shell, but opens infinite new shells which locks up the computer.
require 'win32ole'
shell = WIN32OLE.new('Shell.Application')
shell.ShellExecute("ruby", File.expand_path(__FILE__), nil, 'runas')
response = `bcdedit /v`
p response
exit
Update: giving up doing it for now. Instead just checking the responses. Code is not completely functional yet, but here is where I am incase someone wants to pitch in.
require 'win32/registry'
require 'win32ole'
p 'Device Guard v0.1'
p 'Checking for requisite permissions'
def disable
p 'Would you like me to disable Docker and enable VMWare? [Yes] or [No]'
case (gets.downcase.chomp)
when 'yes'
puts "Disabling docker"
`bcdedit /set hypervisorlaunchtype off` # seems to work ok
Win32::Registry::HKEY_CURRENT_USER.open('SOFTWARE\Microsoft\Windows\CurrentVersion\Run') do |reg|
value = reg['Docker for Windows'] # need to save the location of the executable for next time
reg.delete_value('Docker for Windows') # Access denied even when run as admin :()
end
when 'no'
recheck
else
puts 'Instructions unclear'
disable
end
end
def enable
p 'Would you like me to disable VMWare and enable Docker? [Yes] or [No]'
case (gets.downcase.chomp)
when 'yes'
Win32::Registry::HKEY_CURRENT_USER.open('SOFTWARE\Microsoft\Windows\CurrentVersion\Run') do |reg|
docker_location = reg['Docker for Windows']
p docker_location
`bcdedit /set hypervisorlaunchtype Auto`
end
when 'no'
recheck
else
puts "Instructions unclear"
enable
end
end
def recheck
p 'Well what do you want me to do then? [Recheck] or [Exit]'
case (gets.downcase.chomp)
when 'recheck'
startup
when 'exit'
puts "bye then"
exit
else
puts "Instructions unclear"
recheck
end
end
def check_hypervisor(response)
edited_response = response.match('hypervisorlaunchtype(.*)')[1]
if edited_response.end_with?(" Off")
p 'Hypervisor Launch Type is disabled'
p 'This means VMWare should run and Docker is disabled'
enable
elsif edited_response.end_with?(" Auto")
p 'Hypervisor Launch Type is enabled'
p 'This means that Docker should run and VMWare is disabled'
disable
end
end
def startup
response = `bcdedit /v`
unless response.include?("Windows Boot Manager")
no_access
end
unless response.include?("Access is denied")
check_hypervisor(response)
end
end
def no_access
p 'I am not running as admin. Please run me from a shell with admin privilages'
exit
end
startup
I'm wanting to run a separate provisioning block in my Vagrant file on the newly provisioned server. At present when I run this from my CI server
vagrant up
the following blocks are executed successfully
config.vm.provider :linode do |provider, override|
#creates a new instance etc .. the following block runs on this instance
end
config.vm.provision :chef_solo do |chef|
chef.provisioning_path = "/tmp/deploy"
chef.cookbooks_path = ["cookbooks"]
chef.add_recipe = "mydeployagent"
end
now i want to run a separate provisioner afterwards. (a separate task in CI server) i.e.
config.vm.provision :chef_solo do |chef|
chef.provisioning_path = "/tmp/deploy"
chef.cookbooks_path = ["cookbooks"]
chef.add_recipe = "mydeploydatabaseagent"
end
I'm trying to figure out what I need to
run vagrant up so that it only executes the 1st provision block
run vagrant so that it will only run the 2nd provisioner block on the instance that was created in 1.
Thanks in advance
From Vagrant docs.
Vagrant.configure("2") do |config|
# ... other configuration
config.vm.provision "bootstrap", type: "shell" do |s|
s.inline = "echo hello"
end
end
The --provision-with flag can be used if you only want to run a
specific provisioner if you have multiple provisioners specified. For
example, if you have a shell and Puppet provisioner and only want to
run the shell one, you can do vagrant provision --provision-with
shell. The arguments to --provision-with can be the provisioner type
(such as "shell") or the provisioner name (such as "bootstrap" from
above).
Chef is about managing a desired state so you should be wanting it to ensure your two recipes are run at each time (and they should be idempotent).
I don't know any way to tell vagrant to use twice the same provisoner with different parameters (I can only think about ugly hacks in the vagrant file for that)
If the second recipe depends on the first one to have been executed then you may add a guard to skip this second recipe until the first one has run.
With chef-solo you may do something like this:
mydeployagent/recipes/default.rb
[...Whatever your recipe has to do (agent install, configuration, etc...]
# On a particular resource on your first recipe,
# trigger a notification to make a file which will be used as a guard for second recipe
remote_file "/opt/target/mydeplaoyagent" do
source [.. your source..]
notifies :create,"file[/opt/mydeployagent.initialized]"
end
file "/opt/mydeployagent.initialized" do
action :nothing
end
mydeploydatabaseagent/recipes/default.rb
#start the recipe by a guard to avoid doing anythng if first recipe has not been run
return unless ::File::exists?("/opt/mydeployagent.initialized")
[... your second recipe code here ...]
And in your vagrant file your can add the two recipes to your provisioner like:
config.vm.provision :chef_solo do |chef|
chef.provisioning_path = "/tmp/deploy"
chef.cookbooks_path = ["cookbooks"]
chef.add_recipe = "mydeployagent"
chef.add_recipe = "mydeploydatabaseagent"
end
Obviously the guard can be used on top of the first recipe too if it is not idempotent, but I highly encourage to rethink it to be able to run multiples times, you'll be happy to have it running when you'll have a configuration change to propagate in a file managed by this recipe (and trust me, you will have an update like this to manage one day).
I have a script that logs into 1000+ wireless units and audits and them. Some of their passwords have been changed. I would like to skip these units without specifically telling ruby which ips to skip. The script is multithreaded and gets hung up because when ruby sshes into a unit and uses the wrong password it will ask for the password and the whole script comes to a halt.
*How to skip or rescue wrong password attempts for ruby net/ssh
Net::SSH.start('ip','user',password: 'pass', paranoid: false) do |ssh|
end
with the incorrect password will prompt you like so
admin#192.168.1.1's password:
In latest net-ssh (2.10.0.beta1), the new option :non_interactive was added to ignore the password prompt. Here are the details.
It works for me. For your case it should be
Net::SSH.start('ip','user',password: 'pass', paranoid: false, :non_interactive=>true) do |ssh|
end
options = {
password: password,
number_of_password_prompts: 0,
timeout: SSH_TIMEOUT
}
try to use 'number_of_password_prompts: 0,', It may be perfect
module Net::SSH::PromptMethods::Clear
def prompt(prompt,echo = false)
raise Error, "Details"
# $stdout.print(prompt)
# $stdout.flush
# $stdin.gets.chomp
end
end
Ended up using something similar to this
I want to display some text on the screen when running vagrant up (or vagrant provision, etc.) if and only if provisioning is being done. (For vagrant up it is only run the first time, or if specifically forced with --provision.)
How can this be done?
Adding a shell provisioner is probably the easiest solution, with the small cost that it is executed on the VM over SSH.
Another option is to use the vagrant-host-shell plugin:
Vagrant.configure('2') do |config|
# other config and provisioners
# [...]
config.vm.provision :host_shell, inline: 'echo "Provisioned!"'
end
If you like over-engineering, you can even make your own plugin in Vagrantfile. ;)
class EchoPlugin < Vagrant.plugin('2')
class EchoAction
def initialize(app, env)
#app = app
end
def call(env)
#app.call(env)
puts "Provisioned!"
end
end
name 'echo'
action_hook 'echo' do |hook|
hook.before Vagrant::Action::Builtin::Provision, EchoAction
end
end
Vagrant.configure('2') do |config|
# ...
end
According to the Vagrant issue #7043 where somebody wanted to use #env[:provision_enabled] to see if provisioning is being run. It was answered that you could also check the arguments your Vagrantfile was called with:
This is not currently possible because the Vagrantfile is parsed before the environment is created. This information is available to
provisioners and plugins, but not the Vagrantfile itself because of
the load ordering. In other words, that #env doesn't exist until after
all Vagrantfile's have been parsed, and unfortunately that's a hard
requirement because information in the Vagrantfile determines the way
that object is created. It's a catch-22 for your use case.
One possible alternative is to inspect ARGV in your Vagrantfile.
Something like:
if ARGV.include?("up") || (ARGV.include?("reload") && ARGV.include?("--provision"))
...
end
Example usage
I added two functions to the bottom of my Vagrantfile:
def provisioned?(vm_name='default', provider='virtualbox')
File.exists?(File.join(File.dirname(__FILE__),".vagrant/machines/#{vm_name}/#{provider}/action_provision"))
end
def explicit_provisioning?()
(ARGV.include?("reload") && ARGV.include?("--provision")) || ARGV.include?("provision")
end
Which I can use around any statement in my Vagrantfile:
if (not provisioned?) || explicit_provisioning?
...
end
I'm not sure if I understood your question correctly, but if you want to show a text message if and only if provisioning runs, and you already know that provisioning runs only on first vagrant up and when forcing it using the --provision switch - then why not just add the output of the message to the provisioning itself?
This could be as simple as using a shell provisioner and running an echo command inside of that.
As Vagrant supports multiple provisioners within one Vagrantfile and is able to run all of them when provisioning a virtual machine, this is a dead-easy step, no matter whether you use the shell provisioner anyway, or if you use any other provisioner.
So, I've been working on a Ruby script that blocks reddit during my school hours (useful stuff). Here's the code:
require 'fileutils'
puts "-----------------------------------"
puts "Welcome to the hosts file modifier!"
puts "-----------------------------------"
puts "Option A: Use modified hosts"
puts "Option B: Use original hosts"
puts "Option C: Do nothing"
puts "Please enter your choice: "
input = gets.chomp.downcase
t = Time.now
# Time.now is used is conjunction with function 'original', in option 'b'
def modified
# This function copies the modified (redditblocking) hosts file from Documents to /etc
puts "Moving original hosts file out of /etc"
FileUtils.mv('/etc/hosts', '/Users/(usernameobscured)/Documents/OriginalHosts/hosts')
puts "Done. Now copying modified hosts to /etc"
FileUtils.cp('/Users/(usernameobscured)/Documents/ModifiedHosts/hosts', '/etc/hosts')
puts "Done"
end
def original
# This function deletes the modified hosts file from /etc (since we have a copy in Documents)
# and then moves the original hosts file back to /etc
puts "Deleting modified hosts file from /etc"
FileUtils.rm_rf('etc/hosts')
puts "Done. Now copying original hosts to /etc"
FileUtils.mv('/Users/(usernameobscured)/Documents/OriginalHosts/hosts', '/etc/hosts')
puts "Done"
end
def nothing
# This does... nothing. Literally.
puts "Doing nothing"
end
if input == 'a'
modified
end
if input == 'b'
# Here's when using Time.now becomes helpful: if the hour of the day is less than 5PM,
# then the original hosts file can't be moved back (don't wanna be on reddit during school hours!)
if t.hour > 17
original
elsif t.hour < 17
puts "Too early to use original hosts file. Come back at 5PM"
end
end
if input == 'c'
# Nothing...
nothing
end
As you can see, it moves a modified hosts file from my Documents folder to /etc. The problem I'm having though, as per OS X/Unix security measures, is that I have to run the script via sudo or logged in as root. This is a minor nuisance, however, it's one that I believe can be fixed within the code. How can I get superuser privileges, OR write access to /etc temporarily, via my ruby script, so that I can simply run the script without sudo/root?
Per Unix security model, it is not possible to gain root access without some sort of external intervention (setuid set to the executable, running as root user). Otherwise we would have a gaping security hole.
I am not clear what is exactly your issue of using sudo or rvmsudo or against setting the script setuid (it is possible to configure sudo to not require password for narrowly defined set of commands).
I would just suggest making the various versions of host files group writable by a group that you are member of.
According to this site : http://ruby.about.com/od/rubyversionmanager/qt/Rvm-And-Sudo.htm
you can start executing the script using the rvmsudo command. In your terminal window or shell script:
rvmsudo ruby blockreddit.rb