Merge pull request #10528 from chrisroberts/e-ps-elevated
Add support for running elevated commands using the powershell command
This commit is contained in:
commit
023238b3d1
|
@ -26,6 +26,10 @@ module VagrantPlugins
|
||||||
o.on("-c", "--command COMMAND", "Execute a powershell command directly") do |c|
|
o.on("-c", "--command COMMAND", "Execute a powershell command directly") do |c|
|
||||||
options[:command] = c
|
options[:command] = c
|
||||||
end
|
end
|
||||||
|
|
||||||
|
o.on("-e", "--elevated", "Execute a powershell command with elevated permissions") do |c|
|
||||||
|
options[:elevated] = true
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Parse out the extra args to send to the ps session, which
|
# Parse out the extra args to send to the ps session, which
|
||||||
|
@ -40,8 +44,8 @@ module VagrantPlugins
|
||||||
argv = parse_options(opts)
|
argv = parse_options(opts)
|
||||||
return if !argv
|
return if !argv
|
||||||
|
|
||||||
# Check if the host even supports ps remoting
|
# Elevated option enabled means we can only execute commands
|
||||||
raise Errors::HostUnsupported if !@env.host.capability?(:ps_client)
|
raise Errors::ElevatedNoCommand if !options[:command] && options[:elevated]
|
||||||
|
|
||||||
# Execute ps session if we can
|
# Execute ps session if we can
|
||||||
with_target_vms(argv, single_target: true) do |machine|
|
with_target_vms(argv, single_target: true) do |machine|
|
||||||
|
@ -49,12 +53,12 @@ module VagrantPlugins
|
||||||
raise Vagrant::Errors::VMNotCreatedError
|
raise Vagrant::Errors::VMNotCreatedError
|
||||||
end
|
end
|
||||||
|
|
||||||
if machine.config.vm.communicator != :winrm
|
if options[:command]
|
||||||
raise VagrantPlugins::CommunicatorWinRM::Errors::WinRMNotReady
|
if machine.config.vm.communicator != :winrm
|
||||||
end
|
raise VagrantPlugins::CommunicatorWinRM::Errors::WinRMNotReady
|
||||||
|
end
|
||||||
|
|
||||||
if !options[:command].nil?
|
out_code = machine.communicate.execute(options[:command].dup, elevated: options[:elevated]) do |type,data|
|
||||||
out_code = machine.communicate.execute(options[:command].dup) do |type,data|
|
|
||||||
machine.ui.detail(data) if type == :stdout
|
machine.ui.detail(data) if type == :stdout
|
||||||
end
|
end
|
||||||
if out_code == 0
|
if out_code == 0
|
||||||
|
@ -63,6 +67,9 @@ module VagrantPlugins
|
||||||
next
|
next
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Check if the host even supports ps remoting
|
||||||
|
raise Errors::HostUnsupported if !@env.host.capability?(:ps_client)
|
||||||
|
|
||||||
ps_info = VagrantPlugins::CommunicatorWinRM::Helper.winrm_info(machine)
|
ps_info = VagrantPlugins::CommunicatorWinRM::Helper.winrm_info(machine)
|
||||||
ps_info[:username] = machine.config.winrm.username
|
ps_info[:username] = machine.config.winrm.username
|
||||||
ps_info[:password] = machine.config.winrm.password
|
ps_info[:password] = machine.config.winrm.password
|
||||||
|
@ -78,7 +85,7 @@ module VagrantPlugins
|
||||||
begin
|
begin
|
||||||
@env.host.capability(:ps_client, ps_info)
|
@env.host.capability(:ps_client, ps_info)
|
||||||
ensure
|
ensure
|
||||||
if !result["PreviousTrustedHosts"].nil?
|
if result["PreviousTrustedHosts"]
|
||||||
reset_ps_remoting_for(machine, ps_info)
|
reset_ps_remoting_for(machine, ps_info)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -17,6 +17,10 @@ module VagrantPlugins
|
||||||
class PowerShellError < PSCommandError
|
class PowerShellError < PSCommandError
|
||||||
error_key(:powershell_error)
|
error_key(:powershell_error)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class ElevatedNoCommand < PSCommandError
|
||||||
|
error_key(:elevated_no_command)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,13 +6,18 @@ en:
|
||||||
Resetting WinRM TrustedHosts to their original value.
|
Resetting WinRM TrustedHosts to their original value.
|
||||||
|
|
||||||
errors:
|
errors:
|
||||||
|
elevated_no_command: |-
|
||||||
|
A command must be provided when the --elevated flag is provided for
|
||||||
|
the powershell command. Please provide a command when using the
|
||||||
|
--elevated flag and try again.
|
||||||
|
|
||||||
host_unsupported: |-
|
host_unsupported: |-
|
||||||
Your host does not support PowerShell. A remote PowerShell connection
|
Your host does not support PowerShell. A remote PowerShell connection
|
||||||
can only be made from a windows host.
|
can only be made from a windows host.
|
||||||
|
|
||||||
ps_remoting_undetected: |-
|
ps_remoting_undetected: |-
|
||||||
Unable to establish a remote PowerShell connection with the guest.
|
Unable to establish a remote PowerShell connection with the guest.
|
||||||
Check if the firewall rules on the guest allow connections to the
|
Check if the firewall rules on the guest allow connections to the
|
||||||
Windows remote management service.
|
Windows remote management service.
|
||||||
|
|
||||||
powershell_error: |-
|
powershell_error: |-
|
||||||
|
|
|
@ -0,0 +1,142 @@
|
||||||
|
require File.expand_path("../../../../base", __FILE__)
|
||||||
|
|
||||||
|
require Vagrant.source_root.join("plugins/commands/powershell/command")
|
||||||
|
|
||||||
|
describe VagrantPlugins::CommandPS::Command do
|
||||||
|
include_context "unit"
|
||||||
|
|
||||||
|
let(:iso_env) do
|
||||||
|
# We have to create a Vagrantfile so there is a root path
|
||||||
|
env = isolated_environment
|
||||||
|
env.vagrantfile("")
|
||||||
|
env.create_vagrant_env
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:guest) { double("guest") }
|
||||||
|
let(:host) { double("host") }
|
||||||
|
let(:config) {
|
||||||
|
double("config",
|
||||||
|
vm: double("vm", communicator: communicator_name),
|
||||||
|
winrm: double("winrm", username: winrm_username, password: winrm_password)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
let(:communicator_name) { :winrm }
|
||||||
|
let(:winrm_info) { {host: winrm_host, port: winrm_port} }
|
||||||
|
let(:winrm_username) { double("winrm_username") }
|
||||||
|
let(:winrm_password) { double("winrm_password") }
|
||||||
|
let(:winrm_host) { double("winrm_host") }
|
||||||
|
let(:winrm_port) { double("winrm_port") }
|
||||||
|
|
||||||
|
let(:remoting_ready_result) { {} }
|
||||||
|
|
||||||
|
let(:machine) { iso_env.machine(iso_env.machine_names[0], :dummy) }
|
||||||
|
|
||||||
|
let(:argv) { [] }
|
||||||
|
|
||||||
|
subject { described_class.new(argv, iso_env) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(subject).to receive(:with_target_vms) { |&block| block.call machine }
|
||||||
|
allow(iso_env).to receive(:host).and_return(host)
|
||||||
|
allow(host).to receive(:capability?).with(:ps_client).and_return(true)
|
||||||
|
|
||||||
|
allow(machine.communicate).to receive(:ready?).and_return(true)
|
||||||
|
allow(machine).to receive(:config).and_return(config)
|
||||||
|
|
||||||
|
allow(VagrantPlugins::CommunicatorWinRM::Helper).to receive(:winrm_info).and_return(winrm_info)
|
||||||
|
allow(subject).to receive(:ready_ps_remoting_for).and_return(remoting_ready_result)
|
||||||
|
allow(host).to receive(:capability).with(:ps_client, any_args)
|
||||||
|
|
||||||
|
# Ignore loading up translations
|
||||||
|
allow_any_instance_of(Vagrant::Errors::VagrantError).to receive(:translate_error)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#execute" do
|
||||||
|
context "when communicator is not ready" do
|
||||||
|
before { expect(machine.communicate).to receive(:ready?).and_return(false) }
|
||||||
|
|
||||||
|
it "should raise error that machine is not created" do
|
||||||
|
expect { subject.execute }.to raise_error(Vagrant::Errors::VMNotCreatedError)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when communicator is not winrm" do
|
||||||
|
let(:communicator_name) { :ssh }
|
||||||
|
|
||||||
|
context "when command is provided" do
|
||||||
|
let(:argv) { ["-c", "command"] }
|
||||||
|
|
||||||
|
it "should raise an error that winrm is not ready" do
|
||||||
|
expect { subject.execute }.to raise_error(VagrantPlugins::CommunicatorWinRM::Errors::WinRMNotReady)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when no command is provided" do
|
||||||
|
it "should create a powershell session" do
|
||||||
|
expect(host).to receive(:capability).with(:ps_client, any_args)
|
||||||
|
subject.execute
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when host does not support ps_client" do
|
||||||
|
before { allow(host).to receive(:capability?).with(:ps_client).and_return(false) }
|
||||||
|
|
||||||
|
context "when no command is provided" do
|
||||||
|
it "should raise an error for unsupported host" do
|
||||||
|
expect { subject.execute }.to raise_error(VagrantPlugins::CommandPS::Errors::HostUnsupported)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when command is provided" do
|
||||||
|
let(:argv) { ["-c", "command"] }
|
||||||
|
|
||||||
|
it "should execute command when command is provided" do
|
||||||
|
expect(machine.communicate).to receive(:execute).with("command", any_args).and_return(0)
|
||||||
|
subject.execute
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with command provided" do
|
||||||
|
let(:argv) { ["-c", "command"] }
|
||||||
|
|
||||||
|
it "executes the command on the guest" do
|
||||||
|
expect(machine.communicate).to receive(:execute).with("command", any_args).and_return(0)
|
||||||
|
subject.execute
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with elevated flag" do
|
||||||
|
let(:argv) { ["-e", "-c", "command"] }
|
||||||
|
|
||||||
|
it "should execute the command with elevated option" do
|
||||||
|
expect(machine.communicate).to receive(:execute).
|
||||||
|
with("command", hash_including(elevated: true)).and_return(0)
|
||||||
|
subject.execute
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with elevated flag and no command" do
|
||||||
|
let(:argv) { ["-e"] }
|
||||||
|
|
||||||
|
it "should raise error that command must be provided" do
|
||||||
|
expect { subject.execute }.to raise_error(VagrantPlugins::CommandPS::Errors::ElevatedNoCommand)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should start a new session" do
|
||||||
|
expect(host).to receive(:capability).with(:ps_client, any_args)
|
||||||
|
subject.execute
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when setup returns PreviousTrustedHosts" do
|
||||||
|
let(:remoting_ready_result) { {"PreviousTrustedHosts" => true} }
|
||||||
|
|
||||||
|
it "should reset the powershell remoting" do
|
||||||
|
expect(subject).to receive(:reset_ps_remoting_for)
|
||||||
|
subject.execute
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue