diff --git a/lib/vagrant-google/action.rb b/lib/vagrant-google/action.rb index 7616df8..c9c6892 100644 --- a/lib/vagrant-google/action.rb +++ b/lib/vagrant-google/action.rb @@ -82,6 +82,15 @@ def self.action_read_ssh_info end end + # This action is called to setup the Windows user/password on the machine. + def self.action_setup_winrm_password + Vagrant::Action::Builder.new.tap do |b| + b.use ConfigValidate + b.use ConnectGoogle + b.use SetupWinrmPassword + end + end + # This action is called to read the state of the machine. The # resulting state is expected to be put into the `:machine_state_id` # key. @@ -182,6 +191,7 @@ def self.action_reload autoload :MessageNotCreated, action_root.join("message_not_created") autoload :MessageWillNotDestroy, action_root.join("message_will_not_destroy") autoload :ReadSSHInfo, action_root.join("read_ssh_info") + autoload :SetupWinrmPassword, action_root.join('setup_winrm_password') autoload :ReadState, action_root.join("read_state") autoload :RunInstance, action_root.join("run_instance") autoload :StartInstance, action_root.join("start_instance") diff --git a/lib/vagrant-google/action/run_instance.rb b/lib/vagrant-google/action/run_instance.rb index fc12d90..5880933 100644 --- a/lib/vagrant-google/action/run_instance.rb +++ b/lib/vagrant-google/action/run_instance.rb @@ -14,6 +14,7 @@ require "log4r" require 'vagrant/util/retryable' require 'vagrant-google/util/timer' +require 'vagrant-google/action/setup_winrm_password' module VagrantPlugins module Google @@ -287,10 +288,26 @@ def call(env) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize env[:interrupted] = true end + # Parse out the image project in case it was not set + # and check if it is part of a public windows project + img_project = image.split("/")[6] + is_windows_image = img_project.eql?("windows-cloud") || img_project.eql?("windows-sql-cloud") + + # Reset the password if a windows image unless flag overrides + setup_winrm_password = zone_config.setup_winrm_password + if setup_winrm_password.nil? && is_windows_image + setup_winrm_password = true + end + + if setup_winrm_password + env[:ui].info("Setting up WinRM Password") + env[:action_runner].run(Action.action_setup_winrm_password, env) + end + unless env[:terminated] - env[:metrics]["instance_ssh_time"] = Util::Timer.time do - # Wait for SSH to be ready. - env[:ui].info(I18n.t("vagrant_google.waiting_for_ssh")) + env[:metrics]["instance_comm_time"] = Util::Timer.time do + # Wait for Comms to be ready. + env[:ui].info(I18n.t("vagrant_google.waiting_for_comm")) while true # If we're interrupted just back out break if env[:interrupted] @@ -298,8 +315,8 @@ def call(env) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize sleep 2 end end - @logger.info("Time for SSH ready: #{env[:metrics]["instance_ssh_time"]}") - env[:ui].info(I18n.t("vagrant_google.ready_ssh")) unless env[:interrupted] + @logger.info("Time for Comms ready: #{env[:metrics]["instance_comm_time"]}") + env[:ui].info(I18n.t("vagrant_google.ready_comm")) unless env[:interrupted] end # Terminate the instance if we were interrupted diff --git a/lib/vagrant-google/action/setup_winrm_password.rb b/lib/vagrant-google/action/setup_winrm_password.rb new file mode 100644 index 0000000..551f89f --- /dev/null +++ b/lib/vagrant-google/action/setup_winrm_password.rb @@ -0,0 +1,72 @@ +# Copyright 2015 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Changes: +# April 2019: Modified example found here: +# https://github.com/GoogleCloudPlatform/compute-image-windows/blob/master/examples/windows_auth_python_sample.py +# to enable WinRM with vagrant. + +module VagrantPlugins + module Google + module Action + # Sets up a temporary WinRM password using Google's method for + # establishing a new password over encrypted channels. + class SetupWinrmPassword + def initialize(app, env) + @app = app + @logger = Log4r::Logger.new("vagrant_google::action::setup_winrm_password") + end + + def setup_password(env, instance, zone, user) + # Setup + compute = env[:google_compute] + server = compute.servers.get(instance, zone) + password = server.reset_windows_password(user) + + env[:ui].info("Temp Password: #{password}") + + password + end + + def call(env) + # Get the configs + zone = env[:machine].provider_config.zone + zone_config = env[:machine].provider_config.get_zone_config(zone) + + instance = zone_config.name + user = env[:machine].config.winrm.username + pass = env[:machine].config.winrm.password + + # Get Temporary Password, set WinRM password + temp_pass = setup_password(env, instance, zone, user) + env[:machine].config.winrm.password = temp_pass + + # Wait for WinRM To be Ready + env[:ui].info("Waiting for WinRM To be ready") + env[:machine].communicate.wait_for_ready(60) + + # Use WinRM to Change Password to one in Vagrantfile + env[:ui].info("Changing password from temporary to winrm password") + winrmcomm = VagrantPlugins::CommunicatorWinRM::Communicator.new(env[:machine]) + cmd = "net user #{user} #{pass}" + opts = { elevated: true } + winrmcomm.test(cmd, opts) + + # Update WinRM password to reflect updated one + env[:machine].config.winrm.password = pass + end + end + end + end +end diff --git a/lib/vagrant-google/action/start_instance.rb b/lib/vagrant-google/action/start_instance.rb index 5e660de..1d30aec 100644 --- a/lib/vagrant-google/action/start_instance.rb +++ b/lib/vagrant-google/action/start_instance.rb @@ -65,9 +65,9 @@ def call(env) @logger.info("Time to instance ready: #{env[:metrics]["instance_ready_time"]}") unless env[:interrupted] - env[:metrics]["instance_ssh_time"] = Util::Timer.time do - # Wait for SSH to be ready. - env[:ui].info(I18n.t("vagrant_google.waiting_for_ssh")) + env[:metrics]["instance_comm_time"] = Util::Timer.time do + # Wait for Comms to be ready. + env[:ui].info(I18n.t("vagrant_google.waiting_for_comm")) while true # If we're interrupted then just back out break if env[:interrupted] @@ -76,7 +76,7 @@ def call(env) end end - @logger.info("Time for SSH ready: #{env[:metrics]["instance_ssh_time"]}") + @logger.info("Time for Comms ready: #{env[:metrics]["instance_comm_time"]}") # Ready and booted! env[:ui].info(I18n.t("vagrant_google.ready")) diff --git a/lib/vagrant-google/config.rb b/lib/vagrant-google/config.rb index 83135f0..ed0318f 100644 --- a/lib/vagrant-google/config.rb +++ b/lib/vagrant-google/config.rb @@ -178,6 +178,11 @@ class Config < Vagrant.plugin("2", :config) # rubocop:disable Metrics/ClassLengt # @return [Array] attr_accessor :additional_disks + # (Optional - Override default WinRM setup before for Public Windows images) + # + # @return [Boolean] + attr_accessor :setup_winrm_password + def initialize(zone_specific=false) @google_json_key_location = UNSET_VALUE @google_project_id = UNSET_VALUE @@ -210,6 +215,7 @@ def initialize(zone_specific=false) @service_accounts = UNSET_VALUE @service_account = UNSET_VALUE @additional_disks = [] + @setup_winrm_password = UNSET_VALUE # Internal state (prefix with __ so they aren't automatically # merged) @@ -382,6 +388,9 @@ def finalize! # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedC # Default IAM service account @service_account = nil if @service_account == UNSET_VALUE + # Default Setup WinRM Password + @setup_winrm_password = nil if @setup_winrm_password == UNSET_VALUE + # Config option service_accounts is deprecated if @service_accounts @scopes = @service_accounts diff --git a/locales/en.yml b/locales/en.yml index 31f0427..5f5535a 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -14,8 +14,8 @@ en: Instance is not created. Please run `vagrant up` first. ready: |- Machine is booted and ready for use! - ready_ssh: |- - Machine is ready for SSH access! + ready_comm: |- + Machine is ready for Communicator access! rsync_not_found_warning: |- Warning! Folder sync disabled because the rsync binary is missing. Make sure rsync is installed and the binary can be found in the PATH. @@ -31,8 +31,8 @@ en: Waiting for GCP operation '%{name}' to finish... waiting_for_ready: |- Waiting for instance to become "ready"... - waiting_for_ssh: |- - Waiting for SSH to become available... + waiting_for_comm: |- + Waiting for Communicator to become available... warn_networks: |- Warning! The Google provider doesn't support any of the Vagrant high-level network configurations (`config.vm.network`). They diff --git a/vagrant-google.gemspec b/vagrant-google.gemspec index 28dff5f..e53cf96 100644 --- a/vagrant-google.gemspec +++ b/vagrant-google.gemspec @@ -28,7 +28,7 @@ Gem::Specification.new do |s| s.required_rubygems_version = ">= 1.3.6" s.rubyforge_project = "vagrant-google" - s.add_runtime_dependency "fog-google", "~> 1.9.1" + s.add_runtime_dependency "fog-google", "~> 1.10.0" # This is a restriction to avoid errors on `failure_message_for_should` # TODO: revise after vagrant_spec goes past >0.0.1 (at master@e623a56)