diff --git a/plugins/communicators/winrm/communicator.rb b/plugins/communicators/winrm/communicator.rb
old mode 100644
new mode 100755
index b14b28efa..ade021aad
--- a/plugins/communicators/winrm/communicator.rb
+++ b/plugins/communicators/winrm/communicator.rb
@@ -136,10 +136,11 @@ module VagrantPlugins
error_key: nil, # use the error_class message key
good_exit: 0,
shell: :powershell,
+ interactive: false,
}.merge(opts || {})
opts[:good_exit] = Array(opts[:good_exit])
- command = wrap_in_scheduled_task(command) if opts[:elevated]
+ command = wrap_in_scheduled_task(command, opts[:interactive]) if opts[:elevated]
output = shell.send(opts[:shell], command, &block)
execution_output(output, opts)
end
@@ -193,9 +194,11 @@ module VagrantPlugins
# in place.
#
# @return The wrapper command to execute
- def wrap_in_scheduled_task(command)
+ def wrap_in_scheduled_task(command, interactive)
path = File.expand_path("../scripts/elevated_shell.ps1", __FILE__)
- script = Vagrant::Util::TemplateRenderer.render(path)
+ script = Vagrant::Util::TemplateRenderer.render(path, options: {
+ interactive: interactive,
+ })
guest_script_path = "c:/tmp/vagrant-elevated-shell.ps1"
file = Tempfile.new(["vagrant-elevated-shell", "ps1"])
begin
diff --git a/plugins/communicators/winrm/scripts/elevated_shell.ps1.erb b/plugins/communicators/winrm/scripts/elevated_shell.ps1.erb
old mode 100644
new mode 100755
index 17767e436..d98bc7554
--- a/plugins/communicators/winrm/scripts/elevated_shell.ps1.erb
+++ b/plugins/communicators/winrm/scripts/elevated_shell.ps1.erb
@@ -13,7 +13,7 @@ $task_xml = @'
{username}
- Password
+ <%= options[:interactive] ? 'InteractiveTokenOrPassword' : 'Password' %>
HighestAvailable
@@ -55,7 +55,7 @@ $schedule.Connect()
$task = $schedule.NewTask($null)
$task.XmlText = $task_xml
$folder = $schedule.GetFolder("\")
-$folder.RegisterTaskDefinition($task_name, $task, 6, $username, $password, 1, $null) | Out-Null
+$folder.RegisterTaskDefinition($task_name, $task, 6, $username, $password, <%= options[:interactive] ? 3 : 1 %>, $null) | Out-Null
$registered_task = $folder.GetTask("\$task_name")
$registered_task.Run($null) | Out-Null
@@ -71,7 +71,7 @@ function SlurpOutput($out_file, $cur_line) {
if (Test-Path $out_file) {
get-content $out_file | select -skip $cur_line | ForEach {
$cur_line += 1
- Write-Host "$_"
+ Write-Host "$_"
}
}
return $cur_line
diff --git a/plugins/communicators/winrm/shell.rb b/plugins/communicators/winrm/shell.rb
old mode 100644
new mode 100755
index 14da0adc2..48c1bc6d8
--- a/plugins/communicators/winrm/shell.rb
+++ b/plugins/communicators/winrm/shell.rb
@@ -23,6 +23,7 @@ module VagrantPlugins
HTTPClient::KeepAliveDisconnected,
WinRM::WinRMHTTPTransportError,
WinRM::WinRMAuthorizationError,
+ WinRM::WinRMWSManFault,
Errno::EACCES,
Errno::EADDRINUSE,
Errno::ECONNREFUSED,
diff --git a/plugins/provisioners/shell/config.rb b/plugins/provisioners/shell/config.rb
old mode 100644
new mode 100755
index 2f4957214..aaadb8081
--- a/plugins/provisioners/shell/config.rb
+++ b/plugins/provisioners/shell/config.rb
@@ -12,29 +12,32 @@ module VagrantPlugins
attr_accessor :keep_color
attr_accessor :name
attr_accessor :powershell_args
+ attr_accessor :elevated_interactive
def initialize
- @args = UNSET_VALUE
- @inline = UNSET_VALUE
- @path = UNSET_VALUE
- @upload_path = UNSET_VALUE
- @privileged = UNSET_VALUE
- @binary = UNSET_VALUE
- @keep_color = UNSET_VALUE
- @name = UNSET_VALUE
- @powershell_args = UNSET_VALUE
+ @args = UNSET_VALUE
+ @inline = UNSET_VALUE
+ @path = UNSET_VALUE
+ @upload_path = UNSET_VALUE
+ @privileged = UNSET_VALUE
+ @binary = UNSET_VALUE
+ @keep_color = UNSET_VALUE
+ @name = UNSET_VALUE
+ @powershell_args = UNSET_VALUE
+ @elevated_interactive = UNSET_VALUE
end
def finalize!
- @args = nil if @args == UNSET_VALUE
- @inline = nil if @inline == UNSET_VALUE
- @path = nil if @path == UNSET_VALUE
- @upload_path = "/tmp/vagrant-shell" if @upload_path == UNSET_VALUE
- @privileged = true if @privileged == UNSET_VALUE
- @binary = false if @binary == UNSET_VALUE
- @keep_color = false if @keep_color == UNSET_VALUE
- @name = nil if @name == UNSET_VALUE
- @powershell_args = "-ExecutionPolicy Bypass" if @powershell_args == UNSET_VALUE
+ @args = nil if @args == UNSET_VALUE
+ @inline = nil if @inline == UNSET_VALUE
+ @path = nil if @path == UNSET_VALUE
+ @upload_path = "/tmp/vagrant-shell" if @upload_path == UNSET_VALUE
+ @privileged = true if @privileged == UNSET_VALUE
+ @binary = false if @binary == UNSET_VALUE
+ @keep_color = false if @keep_color == UNSET_VALUE
+ @name = nil if @name == UNSET_VALUE
+ @powershell_args = "-ExecutionPolicy Bypass" if @powershell_args == UNSET_VALUE
+ @elevated_interactive = false if @elevated_interactive == UNSET_VALUE
if @args && args_valid?
@args = @args.is_a?(Array) ? @args.map { |a| a.to_s } : @args.to_s
@@ -78,6 +81,10 @@ module VagrantPlugins
errors << I18n.t("vagrant.provisioners.shell.args_bad_type")
end
+ if elevated_interactive && !privileged
+ errors << I18n.t("vagrant.provisioners.shell.interactive_not_elevated")
+ end
+
{ "shell provisioner" => errors }
end
diff --git a/plugins/provisioners/shell/provisioner.rb b/plugins/provisioners/shell/provisioner.rb
old mode 100644
new mode 100755
index 1b89d1f29..1abfc4889
--- a/plugins/provisioners/shell/provisioner.rb
+++ b/plugins/provisioners/shell/provisioner.rb
@@ -137,7 +137,7 @@ module VagrantPlugins
end
# Execute it with sudo
- comm.sudo(command, elevated: config.privileged) do |type, data|
+ comm.sudo(command, { elevated: config.privileged, interactive: config.elevated_interactive }) do |type, data|
handle_comm(type, data)
end
end
diff --git a/templates/locales/en.yml b/templates/locales/en.yml
old mode 100644
new mode 100755
index 9df64d907..05c8b83c8
--- a/templates/locales/en.yml
+++ b/templates/locales/en.yml
@@ -2037,6 +2037,7 @@ en:
running: "Running: %{script}"
runningas: "Running: %{local} as %{remote}"
upload_path_not_set: "`upload_path` must be set for the shell provisioner."
+ interactive_not_elevated: "To be interactive, it must also be privileged."
ansible:
errors:
diff --git a/test/unit/plugins/communicators/winrm/communicator_test.rb b/test/unit/plugins/communicators/winrm/communicator_test.rb
index e98f646b3..829170bd6 100644
--- a/test/unit/plugins/communicators/winrm/communicator_test.rb
+++ b/test/unit/plugins/communicators/winrm/communicator_test.rb
@@ -93,6 +93,15 @@ describe VagrantPlugins::CommunicatorWinRM::Communicator do
expect(subject.execute("dir", { elevated: true })).to eq(0)
end
+ it "wraps command in elevated and interactive shell script when elevated and interactive are true" do
+ expect(shell).to receive(:upload).with(kind_of(String), "c:/tmp/vagrant-elevated-shell.ps1")
+ expect(shell).to receive(:powershell) do |cmd|
+ expect(cmd).to eq("powershell -executionpolicy bypass -file \"c:/tmp/vagrant-elevated-shell.ps1\" " +
+ "-username \"vagrant\" -password \"password\" -encoded_command \"ZABpAHIAOwAgAGUAeABpAHQAIAAkAEwAQQBTAFQARQBYAEkAVABDAE8ARABFAA==\"")
+ end.and_return({ exitcode: 0 })
+ expect(subject.execute("dir", { elevated: true, interactive: true })).to eq(0)
+ end
+
it "can use cmd shell" do
expect(shell).to receive(:cmd).with(kind_of(String)).and_return({ exitcode: 0 })
expect(subject.execute("dir", { shell: :cmd })).to eq(0)
diff --git a/test/unit/plugins/provisioners/shell/config_test.rb b/test/unit/plugins/provisioners/shell/config_test.rb
index 946b4c2a8..582740162 100644
--- a/test/unit/plugins/provisioners/shell/config_test.rb
+++ b/test/unit/plugins/provisioners/shell/config_test.rb
@@ -85,6 +85,19 @@ describe "VagrantPlugins::Shell::Config" do
I18n.t("vagrant.provisioners.shell.args_bad_type")
])
end
+
+ it "returns an error if elevated_interactive is true but privileged is false" do
+ subject.path = file_that_exists
+ subject.elevated_interactive = true
+ subject.privileged = false
+ subject.finalize!
+
+ result = subject.validate(machine)
+
+ expect(result["shell provisioner"]).to eq([
+ I18n.t("vagrant.provisioners.shell.interactive_not_elevated")
+ ])
+ end
end
describe 'finalize!' do
diff --git a/website/docs/source/v2/provisioning/shell.html.md b/website/docs/source/v2/provisioning/shell.html.md
old mode 100644
new mode 100755
index ef4119668..783ce948e
--- a/website/docs/source/v2/provisioning/shell.html.md
+++ b/website/docs/source/v2/provisioning/shell.html.md
@@ -45,8 +45,9 @@ The remainder of the available options are optional:
defaults to "true".
* `privileged` (boolean) - Specifies whether to execute the shell script
- as a privileged user or not (`sudo`). By default this is "true". This has
- no effect for Windows guests.
+ as a privileged user or not (`sudo`). By default this is "true". Windows
+ guests use a scheduled task to run as a true administrator without the
+ WinRM limitations.
* `upload_path` (string) - Is the remote path where the shell script will
be uploaded to. The script is uploaded as the SSH user over SCP, so this
@@ -65,6 +66,11 @@ The remainder of the available options are optional:
* `powershell_args` (string) - Extra arguments to pass to `PowerShell`
if you're provisioning with PowerShell on Windows.
+* `elevated_interactive` (boolean) - Run an elevated script in interactive mode
+ on Windows. By default this is "false". Must also be `privileged`. Be sure to
+ enable auto-login for Windows as the user must be logged in for interactive
+ mode to work.
+
## Inline Scripts