winrm 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING.txt +674 -0
- data/README +53 -0
- data/Rakefile +69 -0
- data/VERSION +1 -0
- data/lib/extensions/string.rb +37 -0
- data/lib/soap/exceptions/exceptions.rb +28 -0
- data/lib/soap/soap_provider.rb +36 -0
- data/lib/soap/winrm_service.rb +345 -0
- data/lib/winrm.rb +89 -0
- data/preamble +19 -0
- data/test/spec/powershell_tests.spec +24 -0
- data/test/spec/spec.opts +5 -0
- data/test/spec/test.ps1 +14 -0
- data/test/spec/wql_tests.spec +23 -0
- metadata +147 -0
data/README
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
--------------------------------------------------------------------------
|
2
|
+
Windows Remote Management (WinRM) for Ruby
|
3
|
+
--------------------------------------------------------------------------
|
4
|
+
This is a SOAP library that uses the functionality in Windows Remote
|
5
|
+
Management(WinRM) to call native object in Windows. This includes, but is
|
6
|
+
not limitted to, running batch scripts, powershell scripts and fetching WMI
|
7
|
+
variables. For more information on WinRM, please visit Microsoft's WinRM
|
8
|
+
site: http://msdn.microsoft.com/en-us/library/aa384426(v=VS.85).aspx
|
9
|
+
|
10
|
+
|
11
|
+
My Info:
|
12
|
+
BLOG: http://distributed-frostbite.blogspot.com/
|
13
|
+
Add me in LinkedIn: http://www.linkedin.com/in/danwanek
|
14
|
+
|
15
|
+
--------------------------------------------------------------------------
|
16
|
+
--------------------------------------------------------------------------
|
17
|
+
TO USE:
|
18
|
+
require 'winrm'
|
19
|
+
# See REQUIRED GEMS below
|
20
|
+
|
21
|
+
gem install -r winrm
|
22
|
+
|
23
|
+
REQUIRED GEMS: (These should automatically install with the winrm gem)
|
24
|
+
# Handsoap
|
25
|
+
gem install -r handsoap
|
26
|
+
# Nokogiri XML Parser
|
27
|
+
gem install -r nokogiri
|
28
|
+
# HttpClient
|
29
|
+
gem install -r httpclient
|
30
|
+
# NTLM Library
|
31
|
+
gem install -r rubyntlm
|
32
|
+
#UUID Library
|
33
|
+
gem install -r uuid
|
34
|
+
|
35
|
+
|
36
|
+
EXAMPLE:
|
37
|
+
require 'winrm'
|
38
|
+
WinRM::WinRM.endpoint = 'http://mywinrmhost:5985/wsman'
|
39
|
+
WinRM::WinRM.set_auth('user','password')
|
40
|
+
winrm = WinRM::WinRM.instance
|
41
|
+
output = winrm.winrm.run_powershell_script 'script.ps1'
|
42
|
+
puts output[:stdout]
|
43
|
+
|
44
|
+
--------------------------------------------------------------------------
|
45
|
+
DISCLAIMER: If you see something that could be done better or would like
|
46
|
+
to help out in the development of this code please feel free to clone the
|
47
|
+
'git' repository and send me patches:
|
48
|
+
git clone git://github.com/zenchild/WinRM.git
|
49
|
+
or add an issue on GitHub:
|
50
|
+
http://github.com/zenchild/WinRM/issues
|
51
|
+
|
52
|
+
Cheers!
|
53
|
+
--------------------------------------------------------------------------
|
data/Rakefile
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake/clean'
|
3
|
+
require 'rake/gempackagetask'
|
4
|
+
require 'date'
|
5
|
+
|
6
|
+
CLEAN.include("pkg")
|
7
|
+
CLEAN.include("doc")
|
8
|
+
|
9
|
+
GEMSPEC = Gem::Specification.new do |gem|
|
10
|
+
gem.name = "winrm"
|
11
|
+
gem.version = File.open('VERSION').readline.chomp
|
12
|
+
gem.date = Date.today.to_s
|
13
|
+
gem.platform = Gem::Platform::RUBY
|
14
|
+
gem.rubyforge_project = nil
|
15
|
+
|
16
|
+
gem.author = "Dan Wanek"
|
17
|
+
gem.email = "dan.wanek@gmail.com"
|
18
|
+
gem.homepage = "http://github.com/zenchild/WinRM"
|
19
|
+
|
20
|
+
gem.summary = 'Ruby library for Windows Remote Management'
|
21
|
+
gem.description = <<-EOF
|
22
|
+
Ruby library for Windows Remote Management
|
23
|
+
EOF
|
24
|
+
|
25
|
+
gem.files = `git ls-files`.split(/\n/)
|
26
|
+
gem.require_path = "lib"
|
27
|
+
gem.rdoc_options = %w(-x test/ -x examples/)
|
28
|
+
gem.extra_rdoc_files = %w(README COPYING.txt)
|
29
|
+
|
30
|
+
gem.required_ruby_version = '>= 1.8.7'
|
31
|
+
gem.add_runtime_dependency 'handsoap'
|
32
|
+
gem.add_runtime_dependency 'nokogiri'
|
33
|
+
gem.add_runtime_dependency 'httpclient'
|
34
|
+
gem.add_runtime_dependency 'rubyntlm'
|
35
|
+
gem.add_runtime_dependency 'uuid'
|
36
|
+
end
|
37
|
+
|
38
|
+
Rake::GemPackageTask.new(GEMSPEC) do |pkg|
|
39
|
+
pkg.need_tar = true
|
40
|
+
end
|
41
|
+
|
42
|
+
task :default => [:buildgem]
|
43
|
+
|
44
|
+
desc "Build the gem without a version change"
|
45
|
+
task :buildgem => [:clean, :repackage]
|
46
|
+
|
47
|
+
desc "Build the gem, but increment the version first"
|
48
|
+
task :newrelease => [:versionup, :clean, :repackage]
|
49
|
+
|
50
|
+
|
51
|
+
desc "Increment the version by 1 minor release"
|
52
|
+
task :versionup do
|
53
|
+
ver = up_min_version
|
54
|
+
puts "New version: #{ver}"
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
def up_min_version
|
59
|
+
f = File.open('VERSION', 'r+')
|
60
|
+
ver = f.readline.chomp
|
61
|
+
v_arr = ver.split(/\./).map do |v|
|
62
|
+
v.to_i
|
63
|
+
end
|
64
|
+
v_arr[2] += 1
|
65
|
+
ver = v_arr.join('.')
|
66
|
+
f.rewind
|
67
|
+
f.write(ver)
|
68
|
+
ver
|
69
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
@@ -0,0 +1,37 @@
|
|
1
|
+
#############################################################################
|
2
|
+
# Copyright © 2010 Dan Wanek <dan.wanek@gmail.com>
|
3
|
+
#
|
4
|
+
#
|
5
|
+
# This file is part of WinRM.
|
6
|
+
#
|
7
|
+
# WinRM is free software: you can redistribute it and/or
|
8
|
+
# modify it under the terms of the GNU General Public License as published
|
9
|
+
# by the Free Software Foundation, either version 3 of the License, or (at
|
10
|
+
# your option) any later version.
|
11
|
+
#
|
12
|
+
# WinRM is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
15
|
+
# Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU General Public License along
|
18
|
+
# with WinRM. If not, see <http://www.gnu.org/licenses/>.
|
19
|
+
#############################################################################
|
20
|
+
|
21
|
+
# Custom extensions to the class String
|
22
|
+
class String
|
23
|
+
# Change CamelCased strings to ruby_cased strings
|
24
|
+
# It uses the lookahead assertion ?= In this case it basically says match
|
25
|
+
# anything followed by a capital letter, but not the capital letter itself.
|
26
|
+
# @see http://www.pcre.org/pcre.txt The PCRE guide for more details
|
27
|
+
def ruby_case
|
28
|
+
self.split(/(?=[A-Z])/).join('_').downcase
|
29
|
+
end
|
30
|
+
|
31
|
+
# Change a ruby_cased string to CamelCased
|
32
|
+
def camel_case
|
33
|
+
self.split(/_/).map { |i|
|
34
|
+
i.sub(/^./) { |s| s.upcase }
|
35
|
+
}.join
|
36
|
+
end
|
37
|
+
end # String
|
@@ -0,0 +1,28 @@
|
|
1
|
+
#############################################################################
|
2
|
+
# Copyright © 2010 Dan Wanek <dan.wanek@gmail.com>
|
3
|
+
#
|
4
|
+
#
|
5
|
+
# This file is part of WinRM.
|
6
|
+
#
|
7
|
+
# WinRM is free software: you can redistribute it and/or
|
8
|
+
# modify it under the terms of the GNU General Public License as published
|
9
|
+
# by the Free Software Foundation, either version 3 of the License, or (at
|
10
|
+
# your option) any later version.
|
11
|
+
#
|
12
|
+
# WinRM is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
15
|
+
# Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU General Public License along
|
18
|
+
# with WinRM. If not, see <http://www.gnu.org/licenses/>.
|
19
|
+
#############################################################################
|
20
|
+
|
21
|
+
module WinRM
|
22
|
+
module SOAP
|
23
|
+
# Generic WinRM SOAP Error
|
24
|
+
class WinRMWebServiceError < StandardError
|
25
|
+
end
|
26
|
+
end # SOAP
|
27
|
+
end # WinRM
|
28
|
+
|
@@ -0,0 +1,36 @@
|
|
1
|
+
#############################################################################
|
2
|
+
# Copyright © 2010 Dan Wanek <dan.wanek@gmail.com>
|
3
|
+
#
|
4
|
+
#
|
5
|
+
# This file is part of WinRM.
|
6
|
+
#
|
7
|
+
# WinRM is free software: you can redistribute it and/or
|
8
|
+
# modify it under the terms of the GNU General Public License as published
|
9
|
+
# by the Free Software Foundation, either version 3 of the License, or (at
|
10
|
+
# your option) any later version.
|
11
|
+
#
|
12
|
+
# WinRM is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
15
|
+
# Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU General Public License along
|
18
|
+
# with WinRM. If not, see <http://www.gnu.org/licenses/>.
|
19
|
+
#############################################################################
|
20
|
+
require 'handsoap'
|
21
|
+
|
22
|
+
module WinRM
|
23
|
+
module SOAP
|
24
|
+
NS_ADDRESSING ='a' # http://schemas.xmlsoap.org/ws/2004/08/addressing
|
25
|
+
NS_CIMBINDING ='b' # http://schemas.dmtf.org/wbem/wsman/1/cimbinding.xsd
|
26
|
+
NS_ENUM ='n' # http://schemas.xmlsoap.org/ws/2004/09/enumeration
|
27
|
+
NS_TRANSFER ='x' # http://schemas.xmlsoap.org/ws/2004/09/transfer
|
28
|
+
NS_WSMAN_DMTF ='w' # http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd
|
29
|
+
NS_WSMAN_MSFT ='p' # http://schemas.microsoft.com/wbem/wsman/1/wsman.xsd
|
30
|
+
NS_SCHEMA_INST ='xsi' # http://www.w3.org/2001/XMLSchema-instance
|
31
|
+
NS_WIN_SHELL ='rsp' # http://schemas.microsoft.com/wbem/wsman/1/windows/shell
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
require 'soap/exceptions/exceptions'
|
36
|
+
require 'soap/winrm_service'
|
@@ -0,0 +1,345 @@
|
|
1
|
+
#############################################################################
|
2
|
+
# Copyright © 2010 Dan Wanek <dan.wanek@gmail.com>
|
3
|
+
#
|
4
|
+
#
|
5
|
+
# This file is part of WinRM.
|
6
|
+
#
|
7
|
+
# WinRM is free software: you can redistribute it and/or
|
8
|
+
# modify it under the terms of the GNU General Public License as published
|
9
|
+
# by the Free Software Foundation, either version 3 of the License, or (at
|
10
|
+
# your option) any later version.
|
11
|
+
#
|
12
|
+
# WinRM is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
15
|
+
# Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU General Public License along
|
18
|
+
# with WinRM. If not, see <http://www.gnu.org/licenses/>.
|
19
|
+
#############################################################################
|
20
|
+
Handsoap.http_driver = :http_client
|
21
|
+
|
22
|
+
module WinRM
|
23
|
+
module SOAP
|
24
|
+
class WinRMWebService < Handsoap::Service
|
25
|
+
include SOAP
|
26
|
+
|
27
|
+
@@raw_soap = false
|
28
|
+
|
29
|
+
def initialize()
|
30
|
+
if $DEBUG
|
31
|
+
@debug = File.new('winrm_debug.out', 'w')
|
32
|
+
@debug.sync = true
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.set_auth(user,pass)
|
38
|
+
@@user = user
|
39
|
+
@@pass = pass
|
40
|
+
end
|
41
|
+
|
42
|
+
# Turn off parsing and just return the soap response
|
43
|
+
def self.raw_soap!
|
44
|
+
@@raw_soap = true
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
# ********* Begin Hooks *********
|
49
|
+
|
50
|
+
def on_create_document(doc)
|
51
|
+
doc.alias NS_ADDRESSING, 'http://schemas.xmlsoap.org/ws/2004/08/addressing'
|
52
|
+
doc.alias NS_ENUM, 'http://schemas.xmlsoap.org/ws/2004/09/enumeration'
|
53
|
+
doc.alias NS_WSMAN_DMTF, 'http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd'
|
54
|
+
doc.alias NS_WSMAN_MSFT, 'http://schemas.microsoft.com/wbem/wsman/1/wsman.xsd'
|
55
|
+
doc.alias NS_SCHEMA_INST,'http://www.w3.org/2001/XMLSchema-instance'
|
56
|
+
doc.alias NS_WIN_SHELL, 'http://schemas.microsoft.com/wbem/wsman/1/windows/shell'
|
57
|
+
doc.alias NS_CIMBINDING, 'http://schemas.dmtf.org/wbem/wsman/1/cimbinding.xsd'
|
58
|
+
|
59
|
+
header = doc.find('Header')
|
60
|
+
header.add("#{NS_ADDRESSING}:To", WinRMWebService.uri)
|
61
|
+
header.add("#{NS_ADDRESSING}:ReplyTo") {|rto|
|
62
|
+
rto.add("#{NS_ADDRESSING}:Address",'http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous') {|addr|
|
63
|
+
addr.set_attr('mustUnderstand','true')
|
64
|
+
}
|
65
|
+
}
|
66
|
+
header.add("#{NS_WSMAN_DMTF}:MaxEnvelopeSize",'153600') {|mes|
|
67
|
+
mes.set_attr('mustUnderstand','true')
|
68
|
+
}
|
69
|
+
header.add("#{NS_ADDRESSING}:MessageID", "uuid:#{UUID.generate.upcase}")
|
70
|
+
header.add("#{NS_WSMAN_DMTF}:Locale") {|loc|
|
71
|
+
loc.set_attr('xml:lang','en-US')
|
72
|
+
loc.set_attr('mustUnderstand','false')
|
73
|
+
}
|
74
|
+
header.add("#{NS_WSMAN_MSFT}:DataLocale") {|loc|
|
75
|
+
loc.set_attr('xml:lang','en-US')
|
76
|
+
loc.set_attr('mustUnderstand','false')
|
77
|
+
}
|
78
|
+
header.add("#{NS_WSMAN_DMTF}:OperationTimeout",'PT60.000S')
|
79
|
+
end
|
80
|
+
|
81
|
+
# Adds knowledge of namespaces to the response object. These have to be identical to the
|
82
|
+
# URIs returned in the XML response. For example, I had some issues with the 'soap'
|
83
|
+
# namespace because my original URI did not end in a '/'
|
84
|
+
# @example
|
85
|
+
# Won't work: http://schemas.xmlsoap.org/soap/envelope
|
86
|
+
# Works: http://schemas.xmlsoap.org/soap/envelope/
|
87
|
+
def on_response_document(doc)
|
88
|
+
doc.add_namespace NS_ADDRESSING, 'http://schemas.xmlsoap.org/ws/2004/08/addressing'
|
89
|
+
doc.add_namespace NS_ENUM, 'http://schemas.xmlsoap.org/ws/2004/09/enumeration'
|
90
|
+
doc.add_namespace NS_TRANSFER, 'http://schemas.xmlsoap.org/ws/2004/09/transfer'
|
91
|
+
doc.add_namespace NS_WSMAN_DMTF, 'http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd'
|
92
|
+
doc.add_namespace NS_WSMAN_MSFT, 'http://schemas.microsoft.com/wbem/wsman/1/wsman.xsd'
|
93
|
+
doc.add_namespace NS_WIN_SHELL, 'http://schemas.microsoft.com/wbem/wsman/1/windows/shell'
|
94
|
+
doc.add_namespace NS_CIMBINDING, 'http://schemas.dmtf.org/wbem/wsman/1/cimbinding.xsd'
|
95
|
+
end
|
96
|
+
|
97
|
+
def on_after_create_http_request(req)
|
98
|
+
req.set_auth @@user, @@pass
|
99
|
+
req.set_header('Content-Type','application/soap+xml;charset=UTF-8')
|
100
|
+
#puts "SOAP DOCUMENT=\n#{req.body}"
|
101
|
+
end
|
102
|
+
|
103
|
+
def on_http_error(resp)
|
104
|
+
puts "HTTP ERROR: #{resp.status}"
|
105
|
+
puts "HEADERS=\n#{resp.headers}"
|
106
|
+
puts "BODY=\n#{resp.body}"
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
# ********** End Hooks **********
|
111
|
+
|
112
|
+
|
113
|
+
# Create a Shell on the destination host
|
114
|
+
# @param [String<optional>] i_stream Which input stream to open. Leave this alone unless you know what you're doing
|
115
|
+
# @param [String<optional>] o_stream Which output stream to open. Leave this alone unless you know what you're doing
|
116
|
+
# @return [String] The ShellId from the SOAP response. This is our open shell instance on the remote machine.
|
117
|
+
def open_shell(i_stream = 'stdin', o_stream = 'stdout stderr')
|
118
|
+
header = {
|
119
|
+
"#{NS_WSMAN_DMTF}:OptionSet" => [
|
120
|
+
{"#{NS_WSMAN_DMTF}:Option" => {:name => 'WINRS_NOPROFILE', :text =>"FALSE"}},
|
121
|
+
{"#{NS_WSMAN_DMTF}:Option" => {:name => 'WINRS_CODEPAGE', :text =>"437"}}
|
122
|
+
]
|
123
|
+
}.merge(resource_uri_cmd).merge(action_create)
|
124
|
+
|
125
|
+
resp = invoke("#{NS_WIN_SHELL}:Shell", {:soap_action => :auto, :http_options => nil, :soap_header => header}) do |shell|
|
126
|
+
shell.add("#{NS_WIN_SHELL}:InputStreams", i_stream)
|
127
|
+
shell.add("#{NS_WIN_SHELL}:OutputStreams",o_stream)
|
128
|
+
end
|
129
|
+
|
130
|
+
# Get the Shell ID from the response
|
131
|
+
(resp/"//*[@Name='ShellId']").to_s
|
132
|
+
end
|
133
|
+
|
134
|
+
# Run a command on a machine with an open shell
|
135
|
+
# @param [String] shell_id The shell id on the remote machine. See #open_shell
|
136
|
+
# @param [String] command The command to run on the remote machine
|
137
|
+
# @return [String] The CommandId from the SOAP response. This is the ID we need to query in order to get output.
|
138
|
+
def run_command(shell_id, command)
|
139
|
+
header = {
|
140
|
+
"#{NS_WSMAN_DMTF}:OptionSet" => {
|
141
|
+
"#{NS_WSMAN_DMTF}:Option" => {:name => 'WINRS_CONSOLEMODE_STDIN', :text =>"TRUE"},
|
142
|
+
}
|
143
|
+
}.merge(resource_uri_cmd).merge(action_command).merge(selector_shell_id(shell_id))
|
144
|
+
|
145
|
+
# Issue the Command
|
146
|
+
resp = invoke("#{NS_WIN_SHELL}:CommandLine", {:soap_action => :auto, :http_options => nil, :soap_header => header}) do |cli|
|
147
|
+
cli.add("#{NS_WIN_SHELL}:Command","\"#{command}\"")
|
148
|
+
end
|
149
|
+
|
150
|
+
(resp/"//#{NS_WIN_SHELL}:CommandId").to_s
|
151
|
+
end
|
152
|
+
|
153
|
+
# Get the Output of the given shell and command
|
154
|
+
# @param [String] shell_id The shell id on the remote machine. See #open_shell
|
155
|
+
# @param [String] command_id The command id on the remote machine. See #run_command
|
156
|
+
# @return [Hash] :stdout and :stderr
|
157
|
+
def get_command_output(shell_id, command_id)
|
158
|
+
header = {}.merge(resource_uri_cmd).merge(action_receive).merge(selector_shell_id(shell_id))
|
159
|
+
|
160
|
+
# Get Command Output
|
161
|
+
resp = invoke("#{NS_WIN_SHELL}:Receive", {:soap_action => :auto, :http_options => nil, :soap_header => header}) do |rec|
|
162
|
+
rec.add("#{NS_WIN_SHELL}:DesiredStream",'stdout stderr') do |ds|
|
163
|
+
ds.set_attr('CommandId', command_id)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
cmd_stdout = ''
|
168
|
+
cmd_stderr = ''
|
169
|
+
(resp/"//*[@Name='stdout']").each do |n|
|
170
|
+
next if n.to_s.nil?
|
171
|
+
cmd_stdout << Base64.decode64(n.to_s)
|
172
|
+
end
|
173
|
+
(resp/"//*[@Name='stderr']").each do |n|
|
174
|
+
next if n.to_s.nil?
|
175
|
+
cmd_stderr << Base64.decode64(n.to_s)
|
176
|
+
end
|
177
|
+
{:stdout => cmd_stdout, :stderr => cmd_stderr}
|
178
|
+
end
|
179
|
+
|
180
|
+
# Clean-up after a command.
|
181
|
+
# @see #run_command
|
182
|
+
# @param [String] shell_id The shell id on the remote machine. See #open_shell
|
183
|
+
# @param [String] command_id The command id on the remote machine. See #run_command
|
184
|
+
# @return [true] This should have more error checking but it just returns true for now.
|
185
|
+
def cleanup_command(shell_id, command_id)
|
186
|
+
header = {}.merge(resource_uri_cmd).merge(action_signal).merge(selector_shell_id(shell_id))
|
187
|
+
# Signal the Command references to terminate (close stdout/stderr)
|
188
|
+
resp = invoke("#{NS_WIN_SHELL}:Signal", {:soap_action => :auto, :http_options => nil, :soap_header => header}) do |sig|
|
189
|
+
sig.set_attr('CommandId', command_id)
|
190
|
+
sig.add("#{NS_WIN_SHELL}:Code",'http://schemas.microsoft.com/wbem/wsman/1/windows/shell/signal/terminate')
|
191
|
+
end
|
192
|
+
true
|
193
|
+
end
|
194
|
+
|
195
|
+
# Close the shell
|
196
|
+
# @param [String] shell_id The shell id on the remote machine. See #open_shell
|
197
|
+
# @return [true] This should have more error checking but it just returns true for now.
|
198
|
+
def close_shell(shell_id)
|
199
|
+
header = {}.merge(resource_uri_cmd).merge(action_delete).merge(selector_shell_id(shell_id))
|
200
|
+
# Delete the Shell reference
|
201
|
+
resp = invoke(:nil_body, {:soap_action => nil, :soap_body => true, :http_options => nil, :soap_header => header})
|
202
|
+
true
|
203
|
+
end
|
204
|
+
|
205
|
+
# Run a Powershell script that resides on the local box.
|
206
|
+
# @param [String] script_file The string representing the path to a Powershell script
|
207
|
+
# @return [Hash] :stdout and :stderr
|
208
|
+
def run_powershell_script(script_file)
|
209
|
+
script = File.read(script_file)
|
210
|
+
script = script.chars.to_a.join("\x00").chomp.encode('ASCII-8BIT')
|
211
|
+
script = Base64.strict_encode64(script)
|
212
|
+
|
213
|
+
shell_id = open_shell
|
214
|
+
command_id = run_command(shell_id, "powershell -encodedCommand #{script}")
|
215
|
+
command_output = get_command_output(shell_id, command_id)
|
216
|
+
cleanup_command(shell_id, command_id)
|
217
|
+
close_shell(shell_id)
|
218
|
+
command_output
|
219
|
+
end
|
220
|
+
|
221
|
+
|
222
|
+
# Run a WQL Query
|
223
|
+
# @see http://msdn.microsoft.com/en-us/library/aa394606(VS.85).aspx
|
224
|
+
# @param [String] wql The WQL query
|
225
|
+
# @return [Array<Hash>] Returns an array of Hashes that contain the key/value pairs returned from the query.
|
226
|
+
def run_wql(wql)
|
227
|
+
header = {}.merge(resource_uri_wmi).merge(action_enumerate)
|
228
|
+
|
229
|
+
begin
|
230
|
+
resp = invoke("#{NS_ENUM}:Enumerate", {:soap_action => :auto, :http_options => nil, :soap_header => header}) do |enum|
|
231
|
+
enum.add("#{NS_WSMAN_DMTF}:OptimizeEnumeration")
|
232
|
+
enum.add("#{NS_WSMAN_DMTF}:MaxElements",'32000')
|
233
|
+
mattr = nil
|
234
|
+
enum.add("#{NS_WSMAN_DMTF}:Filter", wql) do |filt|
|
235
|
+
filt.set_attr('Dialect','http://schemas.microsoft.com/wbem/wsman/1/WQL')
|
236
|
+
end
|
237
|
+
end
|
238
|
+
rescue Handsoap::Fault => e
|
239
|
+
raise WinRMWebServiceError, e.reason
|
240
|
+
end
|
241
|
+
|
242
|
+
query_response = []
|
243
|
+
(resp/"//#{NS_ENUM}:EnumerateResponse//#{NS_WSMAN_DMTF}:Items/*").each do |i|
|
244
|
+
qitem = {}
|
245
|
+
(i/'*').each do |si|
|
246
|
+
qitem[si.node_name] = si.to_s
|
247
|
+
end
|
248
|
+
query_response << qitem
|
249
|
+
end
|
250
|
+
query_response
|
251
|
+
end
|
252
|
+
|
253
|
+
|
254
|
+
# To create an empty body set :soap_body => true in the invoke options and set the action to :nil_body
|
255
|
+
def iterate_hash_array(element, hash_array)
|
256
|
+
add_hierarchy!(element, hash_array, nil) unless hash_array.key?(:nil_body)
|
257
|
+
end
|
258
|
+
|
259
|
+
|
260
|
+
protected
|
261
|
+
|
262
|
+
# Add a hierarchy of elements from hash data
|
263
|
+
# @example Hash to XML
|
264
|
+
# {:this => {:text =>'that'},'top' => {:id => '32fss', :text => 'TestText', {'middle' => 'bottom'}}}
|
265
|
+
# becomes...
|
266
|
+
# <this>that</this>
|
267
|
+
# <top Id='32fss'>
|
268
|
+
# TestText
|
269
|
+
# <middle>bottom</middle>
|
270
|
+
# </top>
|
271
|
+
def add_hierarchy!(node, e_hash, prefix = NS_ADDRESSING)
|
272
|
+
prefix << ":" unless prefix.nil?
|
273
|
+
e_hash.each_pair do |k,v|
|
274
|
+
name = (k.is_a?(Symbol) && k != :text) ? k.to_s.camel_case : k
|
275
|
+
if v.is_a? Hash
|
276
|
+
node.add("#{prefix}#{name}", v[:text]) do |n|
|
277
|
+
add_hierarchy!(n, v, prefix)
|
278
|
+
end
|
279
|
+
elsif v.is_a? Array
|
280
|
+
node.add("#{prefix}#{name}") do |n|
|
281
|
+
v.each do |i|
|
282
|
+
add_hierarchy!(n, i, prefix)
|
283
|
+
end
|
284
|
+
end
|
285
|
+
else
|
286
|
+
node.set_attr(name, v) unless k == :text
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
|
292
|
+
# Private Methods (Builders and Parsers)
|
293
|
+
private
|
294
|
+
|
295
|
+
def build!(node, opts = {}, &block)
|
296
|
+
#EwsBuilder.new(node, opts, &block)
|
297
|
+
end
|
298
|
+
|
299
|
+
def parse!(response, opts = {})
|
300
|
+
return response if @@raw_soap
|
301
|
+
#EwsParser.new(response).parse(opts)
|
302
|
+
end
|
303
|
+
|
304
|
+
|
305
|
+
# Helper methods for SOAP Headers
|
306
|
+
|
307
|
+
def resource_uri_cmd
|
308
|
+
{"#{NS_WSMAN_DMTF}:ResourceURI" => {'mustUnderstand' => 'true', :text => 'http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd'}}
|
309
|
+
end
|
310
|
+
|
311
|
+
def resource_uri_wmi(namespace = 'root/cimv2/*')
|
312
|
+
{"#{NS_WSMAN_DMTF}:ResourceURI" => {'mustUnderstand' => 'true', :text => "http://schemas.microsoft.com/wbem/wsman/1/wmi/#{namespace}"}}
|
313
|
+
end
|
314
|
+
|
315
|
+
def action_create
|
316
|
+
{"#{NS_ADDRESSING}:Action" => {'mustUnderstand' => 'true', :text => 'http://schemas.xmlsoap.org/ws/2004/09/transfer/Create'}}
|
317
|
+
end
|
318
|
+
|
319
|
+
def action_delete
|
320
|
+
{"#{NS_ADDRESSING}:Action" => {'mustUnderstand' => 'true', :text => 'http://schemas.xmlsoap.org/ws/2004/09/transfer/Delete'}}
|
321
|
+
end
|
322
|
+
|
323
|
+
def action_command
|
324
|
+
{"#{NS_ADDRESSING}:Action" => {'mustUnderstand' => 'true', :text => 'http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Command'}}
|
325
|
+
end
|
326
|
+
|
327
|
+
def action_receive
|
328
|
+
{"#{NS_ADDRESSING}:Action" => {'mustUnderstand' => 'true', :text => 'http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Receive'}}
|
329
|
+
end
|
330
|
+
|
331
|
+
def action_signal
|
332
|
+
{"#{NS_ADDRESSING}:Action" => {'mustUnderstand' => 'true', :text => 'http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Signal'}}
|
333
|
+
end
|
334
|
+
|
335
|
+
def action_enumerate
|
336
|
+
{"#{NS_ADDRESSING}:Action" => {'mustUnderstand' => 'true', :text => 'http://schemas.xmlsoap.org/ws/2004/09/enumeration/Enumerate'}}
|
337
|
+
end
|
338
|
+
|
339
|
+
def selector_shell_id(shell_id)
|
340
|
+
{"#{NS_WSMAN_DMTF}:SelectorSet" => {"#{NS_WSMAN_DMTF}:Selector" => {:name => 'ShellId', :text => shell_id}}}
|
341
|
+
end
|
342
|
+
|
343
|
+
end # class WinRMWebService
|
344
|
+
end # module SOAP
|
345
|
+
end # WinRM
|
data/lib/winrm.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
#############################################################################
|
2
|
+
# Copyright © 2010 Dan Wanek <dan.wanek@gmail.com>
|
3
|
+
#
|
4
|
+
#
|
5
|
+
# This file is part of WinRM.
|
6
|
+
#
|
7
|
+
# WinRM is free software: you can redistribute it and/or
|
8
|
+
# modify it under the terms of the GNU General Public License as published
|
9
|
+
# by the Free Software Foundation, either version 3 of the License, or (at
|
10
|
+
# your option) any later version.
|
11
|
+
#
|
12
|
+
# WinRM is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
15
|
+
# Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU General Public License along
|
18
|
+
# with WinRM. If not, see <http://www.gnu.org/licenses/>.
|
19
|
+
#############################################################################
|
20
|
+
|
21
|
+
# We only what one instance of this class so include Singleton
|
22
|
+
require 'singleton'
|
23
|
+
require 'date'
|
24
|
+
require 'base64'
|
25
|
+
require 'uuid'
|
26
|
+
require 'kconv'
|
27
|
+
|
28
|
+
# Class Extensions
|
29
|
+
require 'extensions/string'
|
30
|
+
|
31
|
+
# Load the backend SOAP infrastructure. Today this is Handsoap.
|
32
|
+
require 'soap/soap_provider'
|
33
|
+
|
34
|
+
|
35
|
+
module WinRM
|
36
|
+
class WinRM
|
37
|
+
include Singleton
|
38
|
+
|
39
|
+
attr_reader :winrm
|
40
|
+
|
41
|
+
# Set the endpoint for WinRM Web Services.
|
42
|
+
# @param [String] endpoint The URL of the endpoint.
|
43
|
+
# https://myserver:5986/wsman
|
44
|
+
# @param [Integer] version The SOAP version to use. This defaults to 1
|
45
|
+
# and you should not need to pass this parameter.
|
46
|
+
def self.endpoint=(endpoint, version = 2)
|
47
|
+
@@endpoint = endpoint
|
48
|
+
SOAP::WinRMWebService.endpoint(:uri => endpoint, :version => version)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Fetch the current endpoint
|
52
|
+
def self.endpoint
|
53
|
+
@@endpoint
|
54
|
+
end
|
55
|
+
|
56
|
+
# Set the SOAP username and password.
|
57
|
+
# @param [String] user The user name
|
58
|
+
# @param [String] pass The password
|
59
|
+
def self.set_auth(user,pass)
|
60
|
+
@@user = user
|
61
|
+
SOAP::WinRMWebService.set_auth(user,pass)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Set the http driver that the SOAP back-end will use.
|
65
|
+
# @param [Symbol] driver The HTTP driver. Available drivers:
|
66
|
+
# :curb, :net_http, :http_client(Default)
|
67
|
+
def self.set_http_driver(driver)
|
68
|
+
Handsoap.http_driver = driver
|
69
|
+
end
|
70
|
+
|
71
|
+
def initialize
|
72
|
+
@winrm = SOAP::WinRMWebService.new
|
73
|
+
end
|
74
|
+
|
75
|
+
# Run a Powershell script
|
76
|
+
# @see WinRM::SOAP::WinRMWebService#run_powershell_script
|
77
|
+
def powershell(script_file)
|
78
|
+
@winrm.run_powershell_script(script_file)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Run a WQL Query
|
82
|
+
# @see WinRM::SOAP::WinRMWebService#run_wql
|
83
|
+
# @see http://msdn.microsoft.com/en-us/library/aa394606(VS.85).aspx
|
84
|
+
def wql(wql)
|
85
|
+
@winrm.run_wql(wql)
|
86
|
+
end
|
87
|
+
|
88
|
+
end # class WinRM
|
89
|
+
end
|
data/preamble
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
#############################################################################
|
2
|
+
# Copyright © 2010 Dan Wanek <dan.wanek@gmail.com>
|
3
|
+
#
|
4
|
+
#
|
5
|
+
# This file is part of WinRM.
|
6
|
+
#
|
7
|
+
# WinRM is free software: you can redistribute it and/or
|
8
|
+
# modify it under the terms of the GNU General Public License as published
|
9
|
+
# by the Free Software Foundation, either version 3 of the License, or (at
|
10
|
+
# your option) any later version.
|
11
|
+
#
|
12
|
+
# WinRM is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
15
|
+
# Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU General Public License along
|
18
|
+
# with WinRM. If not, see <http://www.gnu.org/licenses/>.
|
19
|
+
#############################################################################
|