├── .bundle └── config ├── lib ├── version.rb ├── vagrant-winrm │ ├── errors.rb │ ├── plugin.rb │ └── commands │ │ ├── winrm_config.rb │ │ ├── winrm.rb │ │ └── winrm_upload.rb └── vagrant-winrm.rb ├── Gemfile ├── templates └── winrm_config │ └── config.erb ├── .gitignore ├── .travis.yml ├── locales └── en.yml ├── Rakefile ├── LICENSE ├── vagrant-winrm.gemspec ├── spec ├── spec_helper.rb └── vagrant-winrm │ └── commands │ ├── winrm_upload_spec.rb │ ├── winrm_config_spec.rb │ └── winrm_spec.rb └── README.md /.bundle/config: -------------------------------------------------------------------------------- 1 | --- 2 | BUNDLE_PATH: vendor/bundle 3 | BUNDLE_DISABLE_SHARED_GEMS: '1' 4 | -------------------------------------------------------------------------------- /lib/version.rb: -------------------------------------------------------------------------------- 1 | module VagrantPlugins 2 | module VagrantWinRM 3 | VERSION = '0.7.0' 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gemspec 4 | 5 | group :development do 6 | gem 'vagrant', git: 'https://github.com/mitchellh/vagrant.git' 7 | end 8 | -------------------------------------------------------------------------------- /templates/winrm_config/config.erb: -------------------------------------------------------------------------------- 1 | Host <%= host_key %> 2 | HostName <%= winrm_host %> 3 | Port <%= winrm_port %> 4 | User <%= winrm_user %> 5 | Password <%= winrm_password %> 6 | RDPPort <%= rdp_port %> 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | .yardoc 6 | Gemfile.lock 7 | InstalledFiles 8 | _yardoc 9 | coverage 10 | doc/ 11 | lib/bundler/man 12 | pkg 13 | rdoc 14 | spec/reports 15 | test/tmp 16 | test/version_tmp 17 | tmp 18 | *.bundle 19 | *.so 20 | *.o 21 | *.a 22 | mkmf.log 23 | 24 | # Bundler vendor gems to vendor/ 25 | vendor/ 26 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 2.0.0 4 | deploy: 5 | provider: rubygems 6 | api_key: 7 | secure: gzvD+LH/hVa7RmW3C9Vf6v+asnClSj3I5EHpghwnG7oxJNCiKW1N3zTHPE64M+GhOus9kXlL2/oy358kSNyKAv2eEiqifMs5+hrFAqIU8jEPG+jvaVhBsVpmKedEZEc4ZDgn0Y71Et3+U8+VAEK7iAOQmgKSlVWF1qJxMV7lXSw= 8 | gem: vagrant-winrm 9 | on: 10 | tags: true 11 | all_branches: true 12 | repo: criteo/vagrant-winrm 13 | -------------------------------------------------------------------------------- /locales/en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | vagrant_winrm: 3 | errors: 4 | winrm_not_ready: |- 5 | Vagrant WinRM communicator is not yet ready. 6 | 7 | config_error: |- 8 | The current machine is not configured to communicate through WinRM. 9 | 10 | Communicator: %{communicator} 11 | tempfolder_error: |- 12 | Unable to retrieve a temporary folder on the remote machine through WinRM. 13 | 14 | Communicator: %{communicator} 15 | -------------------------------------------------------------------------------- /lib/vagrant-winrm/errors.rb: -------------------------------------------------------------------------------- 1 | module VagrantPlugins 2 | module VagrantWinRM 3 | module Errors 4 | # A convenient superclass for all our errors. 5 | class WinRMError < Vagrant::Errors::VagrantError 6 | error_namespace('vagrant_winrm.errors') 7 | end 8 | 9 | class ConfigurationError < WinRMError 10 | error_key(:config_error) 11 | end 12 | 13 | class TempFolderError < WinRMError 14 | error_key(:tempfolder_error) 15 | end 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'bundler/setup' 3 | require 'rspec/core/rake_task' 4 | 5 | # Change to the directory of this file. 6 | Dir.chdir(File.expand_path('../', __FILE__)) 7 | 8 | # For gem creation and bundling 9 | require 'bundler/gem_tasks' 10 | 11 | # Run the unit test suite 12 | RSpec::Core::RakeTask.new do |task| 13 | task.pattern = 'spec/**/*_spec.rb' 14 | task.rspec_opts = ['--color', '-f documentation'] 15 | task.rspec_opts << '-tunit' 16 | end 17 | 18 | # Default task is to run tests 19 | task :default => 'spec' 20 | -------------------------------------------------------------------------------- /lib/vagrant-winrm.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant' 2 | 3 | module VagrantPlugins 4 | module VagrantWinRM 5 | 6 | # This returns the path to the source of this plugin. 7 | # 8 | # @return [Pathname] 9 | def self.source_root 10 | @source_root ||= Pathname.new(File.expand_path('../../', __FILE__)) 11 | end 12 | 13 | # This initializes the internationalization strings. 14 | def self.setup_i18n 15 | I18n.load_path << File.expand_path('locales/en.yml', source_root) 16 | I18n.reload! 17 | end 18 | end 19 | end 20 | 21 | VagrantPlugins::VagrantWinRM.setup_i18n() 22 | require 'vagrant-winrm/plugin' 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Author:: Baptiste Courtois () 2 | 3 | Copyright (C) 2014, Baptiste Courtois 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | -------------------------------------------------------------------------------- /lib/vagrant-winrm/plugin.rb: -------------------------------------------------------------------------------- 1 | require 'vagrant' 2 | 3 | module VagrantPlugins 4 | module VagrantWinRM 5 | autoload :Errors, File.expand_path('../errors', __FILE__) 6 | 7 | class Plugin < Vagrant.plugin('2') 8 | name 'winrm' 9 | description <<-DESC 10 | This plugin extends Vagrant WinRM features and add new commands. 11 | DESC 12 | 13 | command 'winrm-config' do 14 | require_relative 'commands/winrm_config' 15 | WinRMConfig 16 | end 17 | 18 | command 'winrm-upload' do 19 | require_relative 'commands/winrm_upload' 20 | WinRMUpload 21 | end 22 | 23 | command 'winrm' do 24 | require_relative 'commands/winrm' 25 | WinRM 26 | end 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /vagrant-winrm.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | require File.expand_path('../lib/version', __FILE__) 3 | 4 | Gem::Specification.new do |s| 5 | s.name = 'vagrant-winrm' 6 | s.version = VagrantPlugins::VagrantWinRM::VERSION 7 | s.platform = Gem::Platform::RUBY 8 | s.authors = ['Baptiste Courtois'] 9 | s.email = ['b.courtois@criteo.fr'] 10 | s.homepage = 'https://github.com/criteo/vagrant-winrm/' 11 | s.summary = 'A Vagrant 1.6+ plugin extending WinRM communication features.' 12 | s.description = 'A Vagrant 1.6+ plugin that adds new command to extends WinRM communication features.' 13 | s.license = 'Apache 2.0' 14 | 15 | s.required_rubygems_version = '>= 2.0.0' 16 | 17 | s.add_dependency 'minitar', '~> 0.5' 18 | 19 | s.add_development_dependency 'rake' 20 | s.add_development_dependency 'rspec_junit_formatter' 21 | s.add_development_dependency 'rspec-core', '~> 3.0' 22 | s.add_development_dependency 'rspec-expectations', '~> 3.0' 23 | s.add_development_dependency 'rspec-mocks', '~> 3.0' 24 | 25 | s.add_development_dependency 'bundler', '~> 1.0' 26 | 27 | s.files = `git ls-files`.split("\n") 28 | s.executables = `git ls-files`.split("\n").map { |f| f =~ /^bin\/(.*)/ ? $1 : nil }.compact 29 | s.require_path = 'lib' 30 | end 31 | -------------------------------------------------------------------------------- /lib/vagrant-winrm/commands/winrm_config.rb: -------------------------------------------------------------------------------- 1 | require 'optparse' 2 | require 'vagrant/util/safe_puts' 3 | require 'vagrant/../../plugins/communicators/winrm/helper' 4 | 5 | module VagrantPlugins 6 | module VagrantWinRM 7 | class WinRMConfig < Vagrant.plugin('2', :command) 8 | include Vagrant::Util::SafePuts 9 | 10 | def self.synopsis 11 | 'outputs winrm configuration to connect to the machine like ssh-config' 12 | end 13 | 14 | def execute 15 | options = {} 16 | 17 | opts = OptionParser.new do |o| 18 | o.banner = 'Usage: vagrant winrm-config [options] [name]' 19 | o.separator 'Options:' 20 | 21 | o.on('--host NAME', 'Name the host for the config') do |h| 22 | options[:host] = h 23 | end 24 | end 25 | 26 | # Parse the options and return if we don't have any target. 27 | argv = parse_options(opts) 28 | return unless argv 29 | 30 | with_target_vms(argv) do |machine| 31 | 32 | variables = { 33 | host_key: options[:host] || machine.name || 'vagrant', 34 | winrm_host: VagrantPlugins::CommunicatorWinRM::Helper.winrm_info(machine)[:host], 35 | winrm_port: VagrantPlugins::CommunicatorWinRM::Helper.winrm_info(machine)[:port], 36 | winrm_user: machine.config.winrm.username, 37 | winrm_password: machine.config.winrm.password, 38 | rdp_port: machine.config.rdp.port 39 | } 40 | 41 | # Render the template and output directly to STDOUT 42 | template = "#{VagrantPlugins::VagrantWinRM.source_root}/templates/winrm_config/config" 43 | safe_puts(Vagrant::Util::TemplateRenderer.render(template, variables)) 44 | safe_puts 45 | end 46 | 47 | # Success, exit status 0 48 | 0 49 | end 50 | end 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /lib/vagrant-winrm/commands/winrm.rb: -------------------------------------------------------------------------------- 1 | require 'optparse' 2 | require 'vagrant/util/safe_puts' 3 | 4 | module VagrantPlugins 5 | module VagrantWinRM 6 | class WinRM < Vagrant.plugin('2', :command) 7 | def self.synopsis 8 | 'connects to machine via WinRM' 9 | end 10 | 11 | def execute 12 | options = { shell: :powershell } 13 | 14 | opts = OptionParser.new do |o| 15 | o.banner = 'Usage: vagrant winrm [options] [name]' 16 | o.separator 'Options:' 17 | 18 | o.on('-c', '--command COMMAND', 'Execute a WinRM command directly') do |c| 19 | options[:command] = Array.new if options[:command].nil? 20 | options[:command].push c 21 | end 22 | 23 | o.on('-e', '--elevated', 'Run all commands with elevated credentials') do |e| 24 | options[:elevated] = true 25 | end 26 | 27 | o.on('-s', '--shell SHELL', [:powershell, :cmd], 'Use the specified shell (powershell, cmd)') do |s| 28 | options[:shell] = s 29 | end 30 | 31 | o.on('--plugin-version', 'Print the version of the plugin and exit') do 32 | options[:version] = true 33 | end 34 | end 35 | 36 | # Parse the options and return if we don't have any target. 37 | argv = parse_options(opts) 38 | return unless argv 39 | 40 | if options[:version] 41 | require "#{VagrantPlugins::VagrantWinRM.source_root}/lib/version" 42 | safe_puts "Vagrant-winrm plugin #{VERSION}" 43 | return 0 44 | end 45 | 46 | return 0 unless options[:command] 47 | 48 | # Execute the actual WinRM command 49 | with_target_vms(argv, single_target: true) do |vm| 50 | 51 | raise Errors::ConfigurationError, { :communicator => vm.config.vm.communicator } if vm.config.vm.communicator != :winrm 52 | 53 | exit_code = 0 54 | @logger.debug("Executing a batch of #{options[:command].length} on remote machine with #{options[:shell]}") 55 | 56 | options[:command].each do |c| 57 | @logger.debug("Executing command: #{c}") 58 | exit_code |= vm.communicate.execute(c, shell: options[:shell], elevated: options[:elevated], error_check: false) { |type, data| (type == :stderr ? $stderr : $stdout).print data } 59 | end 60 | return exit_code 61 | end 62 | end 63 | end 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # This file was generated by the `rspec --init` command. Conventionally, all 2 | # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. 3 | # Require this file using `require "spec_helper"` to ensure that it is only 4 | # loaded once. 5 | # 6 | require 'rspec' 7 | require './lib/vagrant-winrm.rb' 8 | require './lib/vagrant-winrm/plugin.rb' 9 | require './lib/vagrant-winrm/plugin.rb' 10 | require './lib/vagrant-winrm/commands/winrm.rb' 11 | require './lib/vagrant-winrm/commands/winrm_config.rb' 12 | require './lib/vagrant-winrm/commands/winrm_upload.rb' 13 | 14 | def mock_env 15 | let(:idx) { double('idx') } 16 | let(:communicator) { double('communicator') } 17 | let(:winrm_config) { 18 | double('winrm_config', host: 'winrm_super_host', port: 32424, username: 'usern@me', password: 'p4ssw0rd').tap do |config| 19 | allow(config).to receive(:[]) { |key| config.send(key) } 20 | end 21 | } 22 | let(:rdp_config) { 23 | double('rdp_config', port: 4321, search_port: 98765).tap do |config| 24 | allow(config).to receive(:[]) { |key| config.send(key) } 25 | end 26 | } 27 | let(:config_vm) { double('config_vm', communicator: :winrm) } 28 | let(:machine_config) { double('machine_config', winrm: winrm_config, rdp: rdp_config, vm: config_vm) } 29 | 30 | let(:provider) { 31 | double('provider', to_sym: :virtualbox).tap do |provider| 32 | allow(provider).to receive(:capability?).and_return(true) 33 | allow(provider).to receive(:capability).with(:winrm_info).and_return(winrm_config) 34 | end 35 | } 36 | 37 | let(:machine) { 38 | double( 39 | 'machine', 40 | config: machine_config, 41 | name: 'vagrant', 42 | provider: provider, 43 | config: machine_config, 44 | communicate: communicator, 45 | ui: double('ui', opts: {}), 46 | state: nil 47 | ) 48 | } 49 | let(:env) { double('env', root_path: '', home_path: '', ui_class: '', machine_names: [machine.name], active_machines: [machine], machine_index: idx, default_provider: provider) } 50 | end 51 | 52 | # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration 53 | RSpec.configure do |config| 54 | config.run_all_when_everything_filtered = true 55 | config.filter_run :focus 56 | 57 | # Run specs in random order to surface order dependencies. If you find an 58 | # order dependency and want to debug it, you can fix the order by providing 59 | # the seed, which is printed after each run. 60 | # --seed 1234 61 | config.order = 'random' 62 | end 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vagrant-WinRM 2 | 3 | This is a [Vagrant][vagrant_dl] 1.6+ plugin that adds new command to extends WinRM communication features. 4 | 5 | **NOTE:** This plugin requires Vagrant 1.6+ 6 | 7 | ## Features 8 | 9 | * Execute remote command (even with elevated credentials) 10 | * Upload files 11 | * Retrieve WinRM configuration 12 | 13 | ## Installation 14 | 15 | Install using standard Vagrant plugin installation methods: 16 | 17 | vagrant plugin install vagrant-winrm 18 | 19 | Please read the [Plugin usage][plugin_usage] page for more details. 20 | 21 | ## Usage 22 | 23 | ### winrm 24 | 25 | This command allows you to execute arbitrary remote commands through WinRM. 26 | 27 | vagrant winrm -c "pre-install.bat" -c "install.bat" -c "post-install.bat" Windows2008VM 28 | 29 | The following command run the given command with local elevated credentials 30 | vagrant winrm -e -c "winrm get winrm/config Windows2008VM 31 | 32 | ### winrm-upload 33 | 34 | This command allows you to upload a file or a directory to your machine through WinRM. 35 | 36 | vagrant winrm-upload "c:\mylocalFolder" "d:\" Windows2008VM 37 | 38 | ## winrm-config 39 | 40 | This command prints the current WinRM configuration of your machine. 41 | 42 | ```bash 43 | $ vagrant winrm-config --host "CustomHostname" Windows2008VM 44 | Host CustomHostname 45 | HostName Windows2008VM.vagrant.up 46 | Port 5985 47 | User vagrant 48 | Password vagrant 49 | RDPPort 3389 50 | ``` 51 | 52 | ## Development 53 | 54 | * Source hosted at [Github][repo] 55 | * Report issues/questions/feature requests on [Github Issues][issues] 56 | 57 | Pull requests are very welcome! Make sure your patches are well tested. 58 | Ideally create a topic branch for every separate change you make. For 59 | example: 60 | 61 | 1. Fork the repo 62 | 2. Create your feature branch (`git checkout -b my-new-feature`) 63 | 3. Commit your changes (`git commit -am 'Added some feature'`) 64 | 4. Push to the branch (`git push origin my-new-feature`) 65 | 5. Create new Pull Request 66 | 67 | ## Authors 68 | 69 | Created and maintained by [Baptiste Courtois][author] () 70 | 71 | ## License 72 | 73 | Apache 2.0 (see [LICENSE][license]) 74 | 75 | 76 | [author]: https://github.com/Annih 77 | [issues]: https://github.com/criteo/vagrant-winrm/issues 78 | [license]: https://github.com/criteo/vagrant-winrm/blob/master/LICENSE 79 | [repo]: https://github.com/criteo/vagrant-winrm 80 | [plugin_usage]: http://docs.vagrantup.com/v2/plugins/usage.html 81 | 82 | [vagrant_dl]: http://downloads.vagrantup.com/ 83 | -------------------------------------------------------------------------------- /spec/vagrant-winrm/commands/winrm_upload_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'stringio' 3 | 4 | describe VagrantPlugins::VagrantWinRM::WinRMUpload, :unit => true do 5 | 6 | =begin ############ 7 | # Here we mock! 8 | =end ############## 9 | mock_env 10 | 11 | before do 12 | # Mock the local env creation 13 | allow(machine).to receive(:vagrant_env).with('', { :ui_class => '' }).and_return env 14 | 15 | # Mock the index to include only our machine 16 | allow(idx).to receive(:release).with machine 17 | allow(idx).to receive(:include?).with(any_args) do |name| 18 | 'vagrant' == name 19 | end 20 | allow(idx).to receive(:get).with(any_args) do |name| 21 | env.machine(name.to_sym, :virtualbox) 22 | end 23 | 24 | # Add our machine to the environment 25 | allow(env).to receive(:machine) do |name, provider| 26 | machine if :vagrant == name 27 | end 28 | end 29 | 30 | =begin ############ 31 | # Here we test! 32 | =end ############## 33 | describe 'execute' do 34 | it 'raises error ''invalid usage'' on bad usage' do 35 | c = VagrantPlugins::VagrantWinRM::WinRMUpload.new([], env) 36 | expect { c.execute }.to raise_error(Vagrant::Errors::CLIInvalidUsage) 37 | end 38 | 39 | it 'raises error ''invalid options'' on unknown option' do 40 | c = VagrantPlugins::VagrantWinRM::WinRMUpload.new(['--unknown'], env) 41 | expect { c.execute }.to raise_error(Vagrant::Errors::CLIInvalidOptions) 42 | end 43 | 44 | it 'displays help message with option --help' do 45 | c = VagrantPlugins::VagrantWinRM::WinRMUpload.new(['--help'], env) 46 | expect { 47 | expect(c.execute).to be_nil 48 | }.to output.to_stdout 49 | end 50 | 51 | it 'raises error when communicator not winrm' do 52 | c = VagrantPlugins::VagrantWinRM::WinRMUpload.new(['source', 'destination'], env) 53 | expect(config_vm).to receive(:communicator).and_return :ssh 54 | 55 | expect { c.execute }.to raise_error(VagrantPlugins::VagrantWinRM::Errors::ConfigurationError, /not configured to communicate through WinRM/) 56 | end 57 | 58 | it 'raises error on unknown target' do 59 | c = VagrantPlugins::VagrantWinRM::WinRMUpload.new(['source', 'destination', 'unknownTarget'], env) 60 | expect { c.execute }.to raise_error Vagrant::Errors::VMNotFoundError 61 | end 62 | 63 | 64 | it 'passes source and destination to communicator with no target' do 65 | c = VagrantPlugins::VagrantWinRM::WinRMUpload.new(['source', 'destination'], env) 66 | expect(communicator).to receive(:upload).with('source', 'destination').and_return 0 67 | 68 | expect { 69 | expect(c.execute).to be_zero 70 | }.not_to output.to_stdout 71 | end 72 | 73 | it 'passes source and destination to communicator even with a specific target' do 74 | c = VagrantPlugins::VagrantWinRM::WinRMUpload.new(['source', 'destination', 'vagrant'], env) 75 | expect(communicator).to receive(:upload).with('source', 'destination').and_return 0 76 | expect { 77 | expect(c.execute).to be_zero 78 | }.not_to output.to_stdout 79 | end 80 | end 81 | end 82 | -------------------------------------------------------------------------------- /spec/vagrant-winrm/commands/winrm_config_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'stringio' 3 | 4 | describe VagrantPlugins::VagrantWinRM::WinRMConfig, :unit => true do 5 | 6 | =begin ############ 7 | # Here we mock! 8 | =end ############## 9 | mock_env 10 | 11 | before do 12 | # Mock the local env creation 13 | allow(machine).to receive(:vagrant_env).with('', { :ui_class => '' }).and_return env 14 | 15 | # Mock the index to include only our machine 16 | allow(idx).to receive(:release).with machine 17 | allow(idx).to receive(:include?).with(any_args) do |name| 18 | 'vagrant' == name 19 | end 20 | allow(idx).to receive(:get).with(any_args) do |name| 21 | env.machine(name.to_sym, :virtualbox) 22 | end 23 | 24 | # Add our machine to the environment 25 | allow(env).to receive(:machine) do |name, provider| 26 | machine if :vagrant == name 27 | end 28 | end 29 | 30 | =begin ############ 31 | # Here we test! 32 | =end ############## 33 | describe 'execute' do 34 | 35 | it 'displays help message with option --help' do 36 | c = VagrantPlugins::VagrantWinRM::WinRMConfig.new(['--help'], env) 37 | expect { 38 | expect(c.execute).to be_nil 39 | }.to output.to_stdout 40 | end 41 | 42 | it 'raises error on unknown target' do 43 | c = VagrantPlugins::VagrantWinRM::WinRMConfig.new(['unknownTarget'], env) 44 | expect { c.execute }.to raise_error(Vagrant::Errors::VMNotFoundError) 45 | end 46 | 47 | it 'raises error ''invalid options'' on unknown option' do 48 | c = VagrantPlugins::VagrantWinRM::WinRMConfig.new(['--unknown'], env) 49 | expect { c.execute }.to raise_error(Vagrant::Errors::CLIInvalidOptions) 50 | end 51 | 52 | it 'ouputs the WinRMConfig with no Target' do 53 | c = VagrantPlugins::VagrantWinRM::WinRMConfig.new([], env) 54 | begin 55 | $stdout = StringIO.new 56 | expect(c.execute).to be_zero 57 | expect($stdout.string).to match(/#{machine.name}/) 58 | expect($stdout.string).to match(/#{winrm_config.host}/) 59 | expect($stdout.string).to match(/#{winrm_config.port}/) 60 | expect($stdout.string).to match(/#{winrm_config.username}/) 61 | expect($stdout.string).to match(/#{winrm_config.password}/) 62 | expect($stdout.string).to match(/#{rdp_config.port}/) 63 | ensure 64 | $stdout = STDOUT 65 | end 66 | end 67 | 68 | it 'ouputs the WinRMConfig with target' do 69 | c = VagrantPlugins::VagrantWinRM::WinRMConfig.new(['vagrant'], env) 70 | begin 71 | $stdout = StringIO.new 72 | expect(c.execute).to be_zero 73 | expect($stdout.string).to match(/#{machine.name}/) 74 | expect($stdout.string).to match(/#{winrm_config.host}/) 75 | expect($stdout.string).to match(/#{winrm_config.port}/) 76 | expect($stdout.string).to match(/#{winrm_config.username}/) 77 | expect($stdout.string).to match(/#{winrm_config.password}/) 78 | expect($stdout.string).to match(/#{rdp_config.port}/) 79 | ensure 80 | $stdout = STDOUT 81 | end 82 | end 83 | 84 | it 'ouputs the WinRMConfig with custom host key when --host is provided' do 85 | c = VagrantPlugins::VagrantWinRM::WinRMConfig.new(['--host', 'custom_host_key', 'vagrant'], env) 86 | begin 87 | $stdout = StringIO.new 88 | expect(c.execute).to be_zero 89 | expect($stdout.string).to match(/custom_host_key/) 90 | expect($stdout.string).to match(/#{winrm_config.host}/) 91 | expect($stdout.string).to match(/#{winrm_config.port}/) 92 | expect($stdout.string).to match(/#{winrm_config.username}/) 93 | expect($stdout.string).to match(/#{winrm_config.password}/) 94 | expect($stdout.string).to match(/#{rdp_config.port}/) 95 | ensure 96 | $stdout = STDOUT 97 | end 98 | end 99 | end 100 | end 101 | -------------------------------------------------------------------------------- /lib/vagrant-winrm/commands/winrm_upload.rb: -------------------------------------------------------------------------------- 1 | require 'optparse' 2 | 3 | module VagrantPlugins 4 | module VagrantWinRM 5 | class WinRMUpload < Vagrant.plugin('2', :command) 6 | def self.synopsis 7 | 'upload file or directory to machine via WinRM' 8 | end 9 | 10 | def execute 11 | options = { temporary: false, compress: false } 12 | source, destination, argv = parse_args options 13 | 14 | return unless source || destination 15 | 16 | # Execute the actual WinRM 17 | with_target_vms(argv, single_target: true) do |vm| 18 | 19 | raise Errors::ConfigurationError, { :communicator => vm.config.vm.communicator } if vm.config.vm.communicator != :winrm 20 | 21 | tmp_dest = get_remote_temp_folder(vm) if options[:temporary] || options[:compress] 22 | 23 | dest_file = options[:temporary] ? ::File.join(tmp_dest, destination) : destination 24 | $stdout.print dest_file if options[:temporary] 25 | 26 | @logger.debug("Uploading #{source} to #{destination}") 27 | if options[:compress] 28 | source_is_dir = ::File.directory? source 29 | source = compress(source, source_is_dir) 30 | 31 | dest_dir = source_is_dir || dest_file.end_with?('/') || dest_file.end_with?('\\') ? dest_file : ::File.dirname(dest_file) 32 | remote_tgz_path = ::File.join(::File.dirname(tmp_dest), ::File.basename(source)) 33 | vm.communicate.upload(source, remote_tgz_path) 34 | return vm.communicate.execute("New-Item '#{dest_dir}' -type directory -force; tar -xzf '#{remote_tgz_path}' -C '#{dest_dir}'; ") 35 | else 36 | return vm.communicate.upload(source, dest_file) 37 | end 38 | end 39 | end 40 | 41 | 42 | private 43 | 44 | def compress(source, source_is_dir) 45 | require 'zlib' 46 | require 'tempfile' 47 | require 'archive/tar/minitar' 48 | 49 | cwd = Dir.pwd 50 | begin 51 | tmp = ::Tempfile.new(['script', '.tar.gz']) 52 | tmp.binmode 53 | tgz = Zlib::GzipWriter.new (tmp) 54 | 55 | Dir.chdir source_is_dir ? source : ::File.dirname(source) 56 | Archive::Tar::Minitar.pack(source_is_dir ? '.' : ::File.basename(source), tgz) 57 | 58 | tmp.path # returns the temporary file path 59 | ensure 60 | tgz.close if tgz && !tgz.closed? 61 | tmp.close if tmp && !tmp.closed? 62 | Dir.chdir cwd 63 | end 64 | end 65 | 66 | def parse_args(options) 67 | opts = OptionParser.new do |o| 68 | o.banner = <<-EOS 69 | Usage: 70 | \tvagrant winrm-upload [-c] [name] 71 | \tvagrant winrm-upload [-c] -t [name] 72 | EOS 73 | o.separator 'Options:' 74 | 75 | o.on('-t', '--temporary', 'Upload the source file to a temporary directory and return the path') do 76 | options[:temporary] = true 77 | end 78 | 79 | o.on('-c', '--compress', 'Use gzip compression to speed up the upload') do 80 | options[:compress] = true 81 | end 82 | end 83 | 84 | # Parse the options and return if we don't have any target. 85 | argv = parse_options opts 86 | return unless argv 87 | 88 | source = argv[0] 89 | if options[:temporary] 90 | min, max, destination = 1, 2, ::File.basename(argv[0]) 91 | else 92 | min, max, destination = 2, 3, argv[1] 93 | end 94 | 95 | if argv.empty? || argv.length > max || argv.length < min 96 | raise Vagrant::Errors::CLIInvalidUsage, help: opts.help.chomp 97 | end 98 | [source, destination, argv.drop(min)] 99 | end 100 | 101 | def get_remote_temp_folder(vm) 102 | dir = nil 103 | vm.communicate.execute('[System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) | Write-Host -NoNewline') do |type, data| 104 | raise Errors::TempFolderError, { :communicator => vm.config.vm.communicator } if type == :stderr || dir 105 | dir = data 106 | end 107 | dir 108 | end 109 | end 110 | end 111 | end 112 | -------------------------------------------------------------------------------- /spec/vagrant-winrm/commands/winrm_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'stringio' 3 | 4 | describe VagrantPlugins::VagrantWinRM::WinRM, :unit => true do 5 | 6 | =begin ############ 7 | # Here we mock! 8 | =end ############## 9 | mock_env 10 | 11 | before do 12 | # Mock the local env creation 13 | allow(machine).to receive(:vagrant_env).with('', { :ui_class => '' }).and_return env 14 | 15 | # Mock the index to include only our machine 16 | allow(idx).to receive(:release).with machine 17 | allow(idx).to receive(:include?).with(any_args) do |name| 18 | 'vagrant' == name 19 | end 20 | allow(idx).to receive(:get).with(any_args) do |name| 21 | env.machine(name.to_sym, :virtualbox) 22 | end 23 | 24 | # Add our machine to the environment 25 | allow(env).to receive(:machine) do |name, provider| 26 | machine if :vagrant == name 27 | end 28 | end 29 | 30 | =begin ############ 31 | # Here we test! 32 | =end ############## 33 | describe 'execute' do 34 | it 'does nothing with no option' do 35 | c = VagrantPlugins::VagrantWinRM::WinRM.new([], env) 36 | expect { 37 | expect(c.execute).to be_zero 38 | }.not_to output.to_stdout 39 | end 40 | 41 | it 'gives proper version with option --plugin-version' do 42 | c = VagrantPlugins::VagrantWinRM::WinRM.new(['--plugin-version'], env) 43 | expect { 44 | expect(c.execute).to be_zero 45 | }.to output("Vagrant-winrm plugin #{VagrantPlugins::VagrantWinRM::VERSION}\n").to_stdout 46 | end 47 | 48 | it 'displays help message with option --help' do 49 | c = VagrantPlugins::VagrantWinRM::WinRM.new(['--help'], env) 50 | expect { 51 | expect(c.execute).to be_nil 52 | }.to output.to_stdout 53 | end 54 | 55 | it 'raises error when communicator not winrm' do 56 | c = VagrantPlugins::VagrantWinRM::WinRM.new(['-c', 'dummyCommand'], env) 57 | expect(config_vm).to receive(:communicator).and_return :ssh 58 | 59 | expect { c.execute }.to raise_error(VagrantPlugins::VagrantWinRM::Errors::ConfigurationError, /not configured to communicate through WinRM/) 60 | end 61 | 62 | it 'raises error on unknown target' do 63 | c = VagrantPlugins::VagrantWinRM::WinRM.new(['-c', 'command1', 'unknownTarget'], env) 64 | expect { c.execute }.to raise_error(Vagrant::Errors::VMNotFoundError) 65 | end 66 | 67 | it 'raises error ''invalid options'' on unknown option' do 68 | c = VagrantPlugins::VagrantWinRM::WinRM.new(['--unknown'], env) 69 | expect { c.execute }.to raise_error(Vagrant::Errors::CLIInvalidOptions) 70 | end 71 | 72 | it 'passes commands to communicator with no target' do 73 | c = VagrantPlugins::VagrantWinRM::WinRM.new(['-c', 'command1', '--command', 'command2', '-c', 'command3', '--command', 'command4'], env) 74 | 75 | expect(communicator).to receive(:execute).ordered.with('command1', { shell: :powershell, elevated: nil }).and_return 0 76 | expect(communicator).to receive(:execute).ordered.with('command2', { shell: :powershell, elevated: nil }).and_return 0 77 | expect(communicator).to receive(:execute).ordered.with('command3', { shell: :powershell, elevated: nil }).and_return 0 78 | expect(communicator).to receive(:execute).ordered.with('command4', { shell: :powershell, elevated: nil }).and_return 0 79 | 80 | expect { 81 | expect(c.execute).to be_zero 82 | }.not_to output.to_stdout 83 | end 84 | 85 | it 'passes commands to communicator even with a specific target' do 86 | c = VagrantPlugins::VagrantWinRM::WinRM.new(['-c', 'command5', '--command', 'command6', '-c', 'command7', '--command', 'command8', 'vagrant'], env) 87 | expect(communicator).to receive(:execute).ordered.with('command5', { shell: :powershell, elevated: nil }).and_return 0 88 | expect(communicator).to receive(:execute).ordered.with('command6', { shell: :powershell, elevated: nil }).and_return 0 89 | expect(communicator).to receive(:execute).ordered.with('command7', { shell: :powershell, elevated: nil }).and_return 0 90 | expect(communicator).to receive(:execute).ordered.with('command8', { shell: :powershell, elevated: nil }).and_return 0 91 | expect { 92 | expect(c.execute).to be_zero 93 | }.not_to output.to_stdout 94 | end 95 | 96 | it 'uses communicator elevated feature when option -e is present' do 97 | c = VagrantPlugins::VagrantWinRM::WinRM.new(['-e', '-c', 'command'], env) 98 | 99 | expect(communicator).to receive(:execute).with('command', { shell: :powershell, elevated: true }).and_yield(:stdout, 'output message').and_return 0 100 | expect { 101 | expect(c.execute).to be_zero 102 | }.to output('output message').to_stdout 103 | end 104 | 105 | it 'redirects winrm outputs to stdout' do 106 | c = VagrantPlugins::VagrantWinRM::WinRM.new(['-c', 'command'], env) 107 | 108 | expect(communicator).to receive(:execute).with('command', { shell: :powershell, elevated: nil }).and_yield(:stdout, 'output message').and_return 0 109 | expect { 110 | expect(c.execute).to be_zero 111 | }.to output('output message').to_stdout 112 | end 113 | 114 | it 'redirects winrm errors to stderr' do 115 | c = VagrantPlugins::VagrantWinRM::WinRM.new(['-c', 'command'], env) 116 | 117 | expect(communicator).to receive(:execute).with('command', { shell: :powershell, elevated: nil }).and_yield(:stderr, 'error message').and_return 0 118 | expect { 119 | expect(c.execute).to be_zero 120 | }.to output('error message').to_stderr 121 | end 122 | end 123 | end 124 | --------------------------------------------------------------------------------