adds a ps command to vagrant that drops the user into a remote powershell shell
This commit is contained in:
parent
b420da0678
commit
c60a020096
|
@ -0,0 +1,116 @@
|
|||
require "optparse"
|
||||
|
||||
require_relative "../../communicators/winrm/helper"
|
||||
|
||||
module VagrantPlugins
|
||||
module CommandPS
|
||||
class Command < Vagrant.plugin("2", :command)
|
||||
def self.synopsis
|
||||
"connects to machine via powershell remoting"
|
||||
end
|
||||
|
||||
def execute
|
||||
options = {}
|
||||
|
||||
opts = OptionParser.new do |o|
|
||||
o.banner = "Usage: vagrant ps [-- extra ps args]"
|
||||
|
||||
o.separator ""
|
||||
o.separator "Options:"
|
||||
o.separator ""
|
||||
|
||||
o.on("-c", "--command COMMAND", "Execute a powershell command directly") do |c|
|
||||
options[:command] = c
|
||||
end
|
||||
end
|
||||
|
||||
# Parse out the extra args to send to the ps session, which
|
||||
# is everything after the "--"
|
||||
split_index = @argv.index("--")
|
||||
if split_index
|
||||
options[:extra_args] = @argv.drop(split_index + 1)
|
||||
@argv = @argv.take(split_index)
|
||||
end
|
||||
|
||||
# Parse the options and return if we don't have any target.
|
||||
argv = parse_options(opts)
|
||||
return if !argv
|
||||
|
||||
# Check if the host even supports ps remoting
|
||||
raise Errors::HostUnsupported if !@env.host.capability?(:ps_client)
|
||||
|
||||
# Execute ps session if we can
|
||||
with_target_vms(argv, single_target: true) do |machine|
|
||||
if !machine.communicate.ready?
|
||||
raise Vagrant::Errors::VMNotCreatedError
|
||||
end
|
||||
|
||||
if machine.config.vm.communicator != :winrm #|| !machine.provider.capability?(:winrm_info)
|
||||
raise VagrantPlugins::CommunicatorWinRM::Errors::WinRMNotReady
|
||||
end
|
||||
|
||||
if !options[:command].nil?
|
||||
out_code = machine.communicate.execute options[:command]
|
||||
if out_code == 0
|
||||
machine.ui.detail("Command: #{options[:command]} executed succesfully with output code #{out_code}.")
|
||||
end
|
||||
break
|
||||
end
|
||||
|
||||
ps_info = VagrantPlugins::CommunicatorWinRM::Helper.winrm_info(machine)
|
||||
ps_info[:username] = machine.config.winrm.username
|
||||
ps_info[:password] = machine.config.winrm.password
|
||||
# Extra arguments if we have any
|
||||
ps_info[:extra_args] = options[:extra_args]
|
||||
|
||||
result = ready_ps_remoting_for machine, ps_info
|
||||
|
||||
machine.ui.detail(
|
||||
"Creating powershell session to #{ps_info[:host]}:#{ps_info[:port]}")
|
||||
machine.ui.detail("Username: #{ps_info[:username]}")
|
||||
|
||||
begin
|
||||
@env.host.capability(:ps_client, ps_info)
|
||||
ensure
|
||||
if !result["PreviousTrustedHosts"].nil?
|
||||
reset_ps_remoting_for machine, ps_info
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def ready_ps_remoting_for(machine, ps_info)
|
||||
machine.ui.output(I18n.t("vagrant_ps.detecting"))
|
||||
script_path = File.expand_path("../scripts/enable_psremoting.ps1", __FILE__)
|
||||
args = []
|
||||
args << "-hostname" << ps_info[:host]
|
||||
args << "-port" << ps_info[:port].to_s
|
||||
args << "-username" << ps_info[:username]
|
||||
args << "-password" << ps_info[:password]
|
||||
result = Vagrant::Util::PowerShell.execute(script_path, *args)
|
||||
if result.exit_code != 0
|
||||
raise Errors::PowershellError,
|
||||
script: script_path,
|
||||
stderr: result.stderr
|
||||
end
|
||||
|
||||
result_output = JSON.parse(result.stdout)
|
||||
raise Errors::PSRemotingUndetected if !result_output["Success"]
|
||||
result_output
|
||||
end
|
||||
|
||||
def reset_ps_remoting_for(machine, ps_info)
|
||||
machine.ui.output(I18n.t("vagrant_ps.reseting"))
|
||||
script_path = File.expand_path("../scripts/reset_trustedhosts.ps1", __FILE__)
|
||||
args = []
|
||||
args << "-hostname" << ps_info[:host]
|
||||
result = Vagrant::Util::PowerShell.execute(script_path, *args)
|
||||
if result.exit_code != 0
|
||||
raise Errors::PowershellError,
|
||||
script: script_path,
|
||||
stderr: result.stderr
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,18 @@
|
|||
module VagrantPlugins
|
||||
module CommandPS
|
||||
module Errors
|
||||
# A convenient superclass for all our errors.
|
||||
class PSError < Vagrant::Errors::VagrantError
|
||||
error_namespace("vagrant_ps.errors")
|
||||
end
|
||||
|
||||
class HostUnsupported < PSError
|
||||
error_key(:host_unsupported)
|
||||
end
|
||||
|
||||
class PSRemotingUndetected < PSError
|
||||
error_key(:ps_remoting_undetected)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,31 @@
|
|||
require "vagrant"
|
||||
|
||||
module VagrantPlugins
|
||||
module CommandPS
|
||||
autoload :Errors, File.expand_path("../errors", __FILE__)
|
||||
|
||||
class Plugin < Vagrant.plugin("2")
|
||||
name "ps command"
|
||||
description <<-DESC
|
||||
The ps command opens a remote powershell session to the
|
||||
machine if it supports powershell remoting.
|
||||
DESC
|
||||
|
||||
command("ps") do
|
||||
require File.expand_path("../command", __FILE__)
|
||||
init!
|
||||
Command
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def self.init!
|
||||
return if defined?(@_init)
|
||||
I18n.load_path << File.expand_path(
|
||||
"templates/locales/command_ps.yml", Vagrant.source_root)
|
||||
I18n.reload!
|
||||
@_init = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,60 @@
|
|||
Param(
|
||||
[string]$hostname,
|
||||
[string]$port,
|
||||
[string]$username,
|
||||
[string]$password
|
||||
)
|
||||
# If we are in this script, we know basic winrm is working
|
||||
# If the user is not using a domain acount and chances are
|
||||
# they are not, PS Remoting will not work if the guest is not
|
||||
# listed in the trusted hosts.
|
||||
|
||||
$encrypted_password = ConvertTo-SecureString $password -asplaintext -force
|
||||
$creds = New-Object System.Management.Automation.PSCredential (
|
||||
"$hostname\\$username", $encrypted_password)
|
||||
|
||||
$result = @{
|
||||
Success = $false
|
||||
PreviousTrustedHosts = $null
|
||||
}
|
||||
try {
|
||||
invoke-command -computername $hostname `
|
||||
-Credential $creds `
|
||||
-Port $port `
|
||||
-ScriptBlock {} `
|
||||
-ErrorAction Stop
|
||||
$result.Success = $true
|
||||
} catch{}
|
||||
|
||||
if(!$result.Success) {
|
||||
$newHosts = @()
|
||||
$result.PreviousTrustedHosts=(
|
||||
Get-Item "wsman:\localhost\client\trustedhosts").Value
|
||||
$hostArray=$result.PreviousTrustedHosts.Split(",").Trim()
|
||||
if($hostArray -contains "*") {
|
||||
$result.PreviousTrustedHosts = $null
|
||||
}
|
||||
elseif(!($hostArray -contains $hostname)) {
|
||||
$strNewHosts = $hostname
|
||||
if($result.PreviousTrustedHosts.Length -gt 0){
|
||||
$strNewHosts = $result.PreviousTrustedHosts + "," + $strNewHosts
|
||||
}
|
||||
Set-Item -Path "wsman:\localhost\client\trustedhosts" `
|
||||
-Value $strNewHosts -Force
|
||||
|
||||
try {
|
||||
invoke-command -computername $hostname `
|
||||
-Credential $creds `
|
||||
-Port $port `
|
||||
-ScriptBlock {} `
|
||||
-ErrorAction Stop
|
||||
$result.Success = $true
|
||||
} catch{
|
||||
Set-Item -Path "wsman:\localhost\client\trustedhosts" `
|
||||
-Value $result.PreviousTrustedHosts -Force
|
||||
$result.PreviousTrustedHosts = $null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Write-Output $(ConvertTo-Json $result)
|
|
@ -0,0 +1,12 @@
|
|||
Param(
|
||||
[string]$hostname
|
||||
)
|
||||
|
||||
$trustedHosts = (
|
||||
Get-Item "wsman:\localhost\client\trustedhosts").Value.Replace(
|
||||
$hostname, '')
|
||||
$trustedHosts = $trustedHosts.Replace(",,","")
|
||||
if($trustedHosts.EndsWith(",")){
|
||||
$trustedHosts = $trustedHosts.Substring(0,$trustedHosts.length-1)
|
||||
}
|
||||
Set-Item "wsman:\localhost\client\trustedhosts" -Value $trustedHosts -Force
|
|
@ -0,0 +1,50 @@
|
|||
require "pathname"
|
||||
require "tmpdir"
|
||||
|
||||
require "vagrant/util/subprocess"
|
||||
|
||||
module VagrantPlugins
|
||||
module HostWindows
|
||||
module Cap
|
||||
class PS
|
||||
def self.ps_client(env, ps_info)
|
||||
logger = Log4r::Logger.new("vagrant::hosts::windows")
|
||||
|
||||
command = <<-EOS
|
||||
$plain_password = "#{ps_info[:password]}"
|
||||
$username = "#{ps_info[:username]}"
|
||||
$port = "#{ps_info[:port]}"
|
||||
$hostname = "#{ps_info[:host]}"
|
||||
$password = ConvertTo-SecureString $plain_password -asplaintext -force
|
||||
$creds = New-Object System.Management.Automation.PSCredential ("$hostname\\$username", $password)
|
||||
function prompt { kill $PID }
|
||||
Enter-PSSession -ComputerName $hostname -Credential $creds -Port $port
|
||||
EOS
|
||||
|
||||
logger.debug("Starting remote powershell with command:\n#{command}")
|
||||
command = command.chars.to_a.join("\x00").chomp
|
||||
command << "\x00" unless command[-1].eql? "\x00"
|
||||
if(defined?(command.encode))
|
||||
command = command.encode('ASCII-8BIT')
|
||||
command = Base64.strict_encode64(command)
|
||||
else
|
||||
command = Base64.encode64(command).chomp
|
||||
end
|
||||
|
||||
args = ["-NoProfile"]
|
||||
args << "-ExecutionPolicy"
|
||||
args << "Bypass"
|
||||
args << "-NoExit"
|
||||
args << "-EncodedCommand"
|
||||
args << command
|
||||
if ps_info[:extra_args]
|
||||
args << ps_info[:extra_args]
|
||||
end
|
||||
|
||||
# Launch it
|
||||
Vagrant::Util::Subprocess.execute("powershell", *args)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -20,6 +20,11 @@ module VagrantPlugins
|
|||
require_relative "cap/rdp"
|
||||
Cap::RDP
|
||||
end
|
||||
|
||||
host_capability("windows", "ps_client") do
|
||||
require_relative "cap/ps"
|
||||
Cap::PS
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
en:
|
||||
vagrant_ps:
|
||||
detecting: |-
|
||||
Detecting if a remote powershell connection can be made with the guest...
|
||||
reseting: |-
|
||||
Reseting WinRM TrustedHosts to their original value.
|
||||
|
||||
errors:
|
||||
host_unsupported: |-
|
||||
Your host does not support powershell. A remote powershell connection
|
||||
can only be made from a windows host.
|
||||
|
||||
ps_remoting_undetected: |-
|
||||
Unable to establish a remote powershell connection with the guest.
|
||||
Check if the firewall rules on the guest allow connections to the
|
||||
windows remote management service.
|
Loading…
Reference in New Issue