bolt 2.5.0 → 2.6.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of bolt might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Puppetfile +2 -1
- data/lib/bolt/executor.rb +1 -2
- data/lib/bolt/pal/yaml_plan/evaluator.rb +22 -14
- data/lib/bolt/pal/yaml_plan/step.rb +11 -4
- data/lib/bolt/pal/yaml_plan/step/command.rb +2 -2
- data/lib/bolt/pal/yaml_plan/step/resources.rb +3 -3
- data/lib/bolt/pal/yaml_plan/step/script.rb +2 -2
- data/lib/bolt/pal/yaml_plan/step/task.rb +2 -2
- data/lib/bolt/pal/yaml_plan/step/upload.rb +2 -2
- data/lib/bolt/plugin.rb +2 -1
- data/lib/bolt/shell.rb +1 -0
- data/lib/bolt/shell/powershell.rb +263 -0
- data/lib/bolt/shell/powershell/snippets.rb +273 -0
- data/lib/bolt/transport/local/connection.rb +11 -1
- data/lib/bolt/transport/winrm.rb +1 -118
- data/lib/bolt/transport/winrm/connection.rb +27 -78
- data/lib/bolt/version.rb +1 -1
- metadata +18 -4
- data/lib/bolt/transport/local_windows.rb +0 -189
- data/lib/bolt/transport/powershell.rb +0 -337
data/lib/bolt/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bolt
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Puppet
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-04-
|
11
|
+
date: 2020-04-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: addressable
|
@@ -80,6 +80,20 @@ dependencies:
|
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '3'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: jwt
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '2.2'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '2.2'
|
83
97
|
- !ruby/object:Gem::Dependency
|
84
98
|
name: logging
|
85
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -471,6 +485,8 @@ files:
|
|
471
485
|
- lib/bolt/shell.rb
|
472
486
|
- lib/bolt/shell/bash.rb
|
473
487
|
- lib/bolt/shell/bash/tmpdir.rb
|
488
|
+
- lib/bolt/shell/powershell.rb
|
489
|
+
- lib/bolt/shell/powershell/snippets.rb
|
474
490
|
- lib/bolt/target.rb
|
475
491
|
- lib/bolt/task.rb
|
476
492
|
- lib/bolt/task/puppet_server.rb
|
@@ -480,10 +496,8 @@ files:
|
|
480
496
|
- lib/bolt/transport/docker/connection.rb
|
481
497
|
- lib/bolt/transport/local.rb
|
482
498
|
- lib/bolt/transport/local/connection.rb
|
483
|
-
- lib/bolt/transport/local_windows.rb
|
484
499
|
- lib/bolt/transport/orch.rb
|
485
500
|
- lib/bolt/transport/orch/connection.rb
|
486
|
-
- lib/bolt/transport/powershell.rb
|
487
501
|
- lib/bolt/transport/remote.rb
|
488
502
|
- lib/bolt/transport/simple.rb
|
489
503
|
- lib/bolt/transport/ssh.rb
|
@@ -1,189 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'json'
|
4
|
-
require 'fileutils'
|
5
|
-
require 'open3'
|
6
|
-
require 'tmpdir'
|
7
|
-
require 'bolt/node/output'
|
8
|
-
require 'bolt/transport/base'
|
9
|
-
require 'bolt/transport/powershell'
|
10
|
-
require 'bolt/util'
|
11
|
-
|
12
|
-
module Bolt
|
13
|
-
module Transport
|
14
|
-
class LocalWindows < Base
|
15
|
-
def provided_features
|
16
|
-
['powershell']
|
17
|
-
end
|
18
|
-
|
19
|
-
def default_input_method(executable)
|
20
|
-
input_method ||= Powershell.powershell_file?(executable) ? 'powershell' : 'both'
|
21
|
-
input_method
|
22
|
-
end
|
23
|
-
|
24
|
-
def in_tmpdir(base)
|
25
|
-
args = base ? [nil, base] : []
|
26
|
-
dir = begin
|
27
|
-
Dir.mktmpdir(*args)
|
28
|
-
rescue StandardError => e
|
29
|
-
raise Bolt::Node::FileError.new("Could not make tempdir: #{e.message}", 'TEMPDIR_ERROR')
|
30
|
-
end
|
31
|
-
|
32
|
-
yield dir
|
33
|
-
ensure
|
34
|
-
FileUtils.remove_entry dir if dir
|
35
|
-
end
|
36
|
-
private :in_tmpdir
|
37
|
-
|
38
|
-
def copy_file(source, destination)
|
39
|
-
logger.debug { "Uploading #{source}, to #{destination}" }
|
40
|
-
FileUtils.cp_r(source, destination, remove_destination: true)
|
41
|
-
rescue StandardError => e
|
42
|
-
raise Bolt::Node::FileError.new(e.message, 'WRITE_ERROR')
|
43
|
-
end
|
44
|
-
|
45
|
-
def with_tmpscript(script, base)
|
46
|
-
in_tmpdir(base) do |dir|
|
47
|
-
dest = File.join(dir, File.basename(script))
|
48
|
-
copy_file(script, dest)
|
49
|
-
File.chmod(0o750, dest)
|
50
|
-
yield dest, dir
|
51
|
-
end
|
52
|
-
end
|
53
|
-
private :with_tmpscript
|
54
|
-
|
55
|
-
def execute(*command, options)
|
56
|
-
# Interpreter can be an array or string. It will be appended to the command array.
|
57
|
-
command.unshift(options[:interpreter]).flatten! if options[:interpreter]
|
58
|
-
command = [options[:env]] + command if options[:env]
|
59
|
-
|
60
|
-
if options[:stdin]
|
61
|
-
stdout, stderr, rc = Open3.capture3(*command, stdin_data: options[:stdin], chdir: options[:dir])
|
62
|
-
else
|
63
|
-
stdout, stderr, rc = Open3.capture3(*command, chdir: options[:dir])
|
64
|
-
end
|
65
|
-
|
66
|
-
result_output = Bolt::Node::Output.new
|
67
|
-
result_output.stdout << stdout unless stdout.nil?
|
68
|
-
result_output.stderr << stderr unless stderr.nil?
|
69
|
-
result_output.exit_code = rc.exitstatus
|
70
|
-
result_output
|
71
|
-
end
|
72
|
-
|
73
|
-
def upload(target, source, destination, _options = {})
|
74
|
-
copy_file(source, destination)
|
75
|
-
Bolt::Result.for_upload(target, source, destination)
|
76
|
-
end
|
77
|
-
|
78
|
-
def run_command(target, command, _options = {})
|
79
|
-
in_tmpdir(target.options['tmpdir']) do |dir|
|
80
|
-
output = execute(command, dir: dir)
|
81
|
-
Bolt::Result.for_command(target,
|
82
|
-
output.stdout.string,
|
83
|
-
output.stderr.string,
|
84
|
-
output.exit_code,
|
85
|
-
'command', command)
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
def run_script(target, script, arguments, _options = {})
|
90
|
-
with_tmpscript(File.absolute_path(script), target.options['tmpdir']) do |file, dir|
|
91
|
-
logger.debug "Running '#{file}' with #{arguments.to_json}"
|
92
|
-
|
93
|
-
# unpack any Sensitive data AFTER we log
|
94
|
-
arguments = unwrap_sensitive_args(arguments)
|
95
|
-
if Powershell.powershell_file?(file)
|
96
|
-
command = Powershell.run_script(arguments, file)
|
97
|
-
interpreter = ['powershell.exe', *Powershell.ps_args]
|
98
|
-
output = execute(command, dir: dir, interpreter: interpreter)
|
99
|
-
else
|
100
|
-
path, args = *Powershell.process_from_extension(file)
|
101
|
-
args += Powershell.escape_arguments(arguments)
|
102
|
-
command = args.unshift(path).join(' ')
|
103
|
-
output = execute(command, dir: dir)
|
104
|
-
end
|
105
|
-
Bolt::Result.for_command(target,
|
106
|
-
output.stdout.string,
|
107
|
-
output.stderr.string,
|
108
|
-
output.exit_code,
|
109
|
-
'script', script)
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
def run_task(target, task, arguments, _options = {})
|
114
|
-
implementation = select_implementation(target, task)
|
115
|
-
executable = implementation['path']
|
116
|
-
input_method = implementation['input_method']
|
117
|
-
extra_files = implementation['files']
|
118
|
-
|
119
|
-
in_tmpdir(target.options['tmpdir']) do |dir|
|
120
|
-
if extra_files.empty?
|
121
|
-
script = File.join(dir, File.basename(executable))
|
122
|
-
else
|
123
|
-
arguments['_installdir'] = dir
|
124
|
-
script_dest = File.join(dir, task.tasks_dir)
|
125
|
-
FileUtils.mkdir_p([script_dest] + extra_files.map { |file| File.join(dir, File.dirname(file['name'])) })
|
126
|
-
|
127
|
-
script = File.join(script_dest, File.basename(executable))
|
128
|
-
extra_files.each do |file|
|
129
|
-
dest = File.join(dir, file['name'])
|
130
|
-
copy_file(file['path'], dest)
|
131
|
-
File.chmod(0o750, dest)
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
copy_file(executable, script)
|
136
|
-
File.chmod(0o750, script)
|
137
|
-
|
138
|
-
interpreter = select_interpreter(script, target.options['interpreters'])
|
139
|
-
interpreter_debug = interpreter ? " using '#{interpreter}' interpreter" : nil
|
140
|
-
# log the arguments with sensitive data redacted, do NOT log unwrapped_arguments
|
141
|
-
logger.debug("Running '#{script}' with #{arguments.to_json}#{interpreter_debug}")
|
142
|
-
unwrapped_arguments = unwrap_sensitive_args(arguments)
|
143
|
-
|
144
|
-
stdin = Bolt::Task::STDIN_METHODS.include?(input_method) ? JSON.dump(unwrapped_arguments) : nil
|
145
|
-
if Bolt::Task::ENVIRONMENT_METHODS.include?(input_method)
|
146
|
-
environment_params = envify_params(unwrapped_arguments).each_with_object([]) do |(arg, val), list|
|
147
|
-
list << Powershell.set_env(arg, val)
|
148
|
-
end
|
149
|
-
environment_params = environment_params.join("\n") + "\n"
|
150
|
-
else
|
151
|
-
environment_params = ""
|
152
|
-
end
|
153
|
-
|
154
|
-
if Powershell.powershell_file?(script) && stdin.nil?
|
155
|
-
command = Powershell.run_ps_task(arguments, script, input_method)
|
156
|
-
command = environment_params + Powershell.shell_init + command
|
157
|
-
interpreter ||= ['powershell.exe', *Powershell.ps_args]
|
158
|
-
output =
|
159
|
-
if input_method == 'powershell'
|
160
|
-
execute(command, dir: dir, interpreter: interpreter)
|
161
|
-
else
|
162
|
-
execute(command, dir: dir, stdin: stdin, interpreter: interpreter)
|
163
|
-
end
|
164
|
-
end
|
165
|
-
unless output
|
166
|
-
if interpreter
|
167
|
-
env = Bolt::Task::ENVIRONMENT_METHODS.include?(input_method) ? envify_params(unwrapped_arguments) : nil
|
168
|
-
output = execute(script, stdin: stdin, env: env, dir: dir, interpreter: interpreter)
|
169
|
-
else
|
170
|
-
path, args = *Powershell.process_from_extension(script)
|
171
|
-
command = args.unshift(path).join(' ')
|
172
|
-
command = environment_params + Powershell.shell_init + command
|
173
|
-
output = execute(command, dir: dir, stdin: stdin, interpreter: 'powershell.exe')
|
174
|
-
end
|
175
|
-
end
|
176
|
-
Bolt::Result.for_task(target,
|
177
|
-
output.stdout.string,
|
178
|
-
output.stderr.string,
|
179
|
-
output.exit_code,
|
180
|
-
task.name)
|
181
|
-
end
|
182
|
-
end
|
183
|
-
|
184
|
-
def connected?(_targets)
|
185
|
-
true
|
186
|
-
end
|
187
|
-
end
|
188
|
-
end
|
189
|
-
end
|
@@ -1,337 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Bolt
|
4
|
-
module Transport
|
5
|
-
module Powershell
|
6
|
-
class << self
|
7
|
-
def ps_args
|
8
|
-
%w[-NoProfile -NonInteractive -NoLogo -ExecutionPolicy Bypass]
|
9
|
-
end
|
10
|
-
|
11
|
-
def powershell_file?(path)
|
12
|
-
Pathname(path).extname.casecmp('.ps1').zero?
|
13
|
-
end
|
14
|
-
|
15
|
-
def process_from_extension(path)
|
16
|
-
case Pathname(path).extname.downcase
|
17
|
-
when '.rb'
|
18
|
-
[
|
19
|
-
'ruby.exe',
|
20
|
-
['-S', "\"#{path}\""]
|
21
|
-
]
|
22
|
-
when '.ps1'
|
23
|
-
[
|
24
|
-
'powershell.exe',
|
25
|
-
[*ps_args, '-File', "\"#{path}\""]
|
26
|
-
]
|
27
|
-
when '.pp'
|
28
|
-
[
|
29
|
-
'puppet.bat',
|
30
|
-
['apply', "\"#{path}\""]
|
31
|
-
]
|
32
|
-
else
|
33
|
-
# Run the script via cmd, letting Windows extension handling determine how
|
34
|
-
[
|
35
|
-
'cmd.exe',
|
36
|
-
['/c', "\"#{path}\""]
|
37
|
-
]
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
def escape_arguments(arguments)
|
42
|
-
arguments.map do |arg|
|
43
|
-
if arg =~ / /
|
44
|
-
"\"#{arg}\""
|
45
|
-
else
|
46
|
-
arg
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
def set_env(arg, val)
|
52
|
-
"[Environment]::SetEnvironmentVariable('#{arg}', @'\n#{val}\n'@)"
|
53
|
-
end
|
54
|
-
|
55
|
-
def quote_string(string)
|
56
|
-
"'" + string.gsub("'", "''") + "'"
|
57
|
-
end
|
58
|
-
|
59
|
-
def execute_process(path, arguments, stdin = nil)
|
60
|
-
quoted_args = arguments.map { |arg| quote_string(arg) }.join(' ')
|
61
|
-
|
62
|
-
quoted_path = if path =~ /^'.*'$/ || path =~ /^".*"$/
|
63
|
-
path
|
64
|
-
else
|
65
|
-
quote_string(path)
|
66
|
-
end
|
67
|
-
exec_cmd =
|
68
|
-
if stdin.nil?
|
69
|
-
"& #{quoted_path} #{quoted_args}"
|
70
|
-
else
|
71
|
-
"@'\n#{stdin}\n'@ | & #{quoted_path} #{quoted_args}"
|
72
|
-
end
|
73
|
-
<<-PS
|
74
|
-
$OutputEncoding = [Console]::OutputEncoding
|
75
|
-
#{exec_cmd}
|
76
|
-
if (-not $? -and ($LASTEXITCODE -eq $null)) { exit 1 }
|
77
|
-
exit $LASTEXITCODE
|
78
|
-
PS
|
79
|
-
end
|
80
|
-
|
81
|
-
def mkdirs(dirs)
|
82
|
-
"mkdir -Force #{dirs.uniq.sort.join(',')}"
|
83
|
-
end
|
84
|
-
|
85
|
-
def make_tempdir(parent)
|
86
|
-
<<-PS
|
87
|
-
$parent = #{parent}
|
88
|
-
$name = [System.IO.Path]::GetRandomFileName()
|
89
|
-
$path = Join-Path $parent $name
|
90
|
-
New-Item -ItemType Directory -Path $path | Out-Null
|
91
|
-
$path
|
92
|
-
PS
|
93
|
-
end
|
94
|
-
|
95
|
-
def rmdir(dir)
|
96
|
-
<<-PS
|
97
|
-
Remove-Item -Force -Recurse -Path "#{dir}"
|
98
|
-
PS
|
99
|
-
end
|
100
|
-
|
101
|
-
def run_script(arguments, script_path)
|
102
|
-
mapped_args = arguments.map do |a|
|
103
|
-
"$invokeArgs.ArgumentList += @'\n#{a}\n'@"
|
104
|
-
end.join("\n")
|
105
|
-
<<-PS
|
106
|
-
$invokeArgs = @{
|
107
|
-
ScriptBlock = (Get-Command "#{script_path}").ScriptBlock
|
108
|
-
ArgumentList = @()
|
109
|
-
}
|
110
|
-
#{mapped_args}
|
111
|
-
|
112
|
-
try
|
113
|
-
{
|
114
|
-
Invoke-Command @invokeArgs
|
115
|
-
}
|
116
|
-
catch
|
117
|
-
{
|
118
|
-
Write-Error $_.Exception
|
119
|
-
exit 1
|
120
|
-
}
|
121
|
-
PS
|
122
|
-
end
|
123
|
-
|
124
|
-
def run_ps_task(arguments, task_path, input_method)
|
125
|
-
# NOTE: cannot redirect STDIN to a .ps1 script inside of PowerShell
|
126
|
-
# must create new powershell.exe process like other interpreters
|
127
|
-
# fortunately, using PS with stdin input_method should never happen
|
128
|
-
if input_method == 'powershell'
|
129
|
-
<<-PS
|
130
|
-
$private:tempArgs = Get-ContentAsJson (
|
131
|
-
$utf8.GetString([System.Convert]::FromBase64String('#{Base64.encode64(JSON.dump(arguments))}'))
|
132
|
-
)
|
133
|
-
$allowedArgs = (Get-Command "#{task_path}").Parameters.Keys
|
134
|
-
$private:taskArgs = @{}
|
135
|
-
$private:tempArgs.Keys | ? { $allowedArgs -contains $_ } | % { $private:taskArgs[$_] = $private:tempArgs[$_] }
|
136
|
-
try { & "#{task_path}" @taskArgs } catch { Write-Error $_.Exception; exit 1 }
|
137
|
-
PS
|
138
|
-
else
|
139
|
-
%(try { & "#{task_path}" } catch { Write-Error $_.Exception; exit 1 })
|
140
|
-
end
|
141
|
-
end
|
142
|
-
|
143
|
-
def shell_init
|
144
|
-
<<-PS
|
145
|
-
$ENV:PATH += ";${ENV:ProgramFiles}\\Puppet Labs\\Puppet\\bin\\;" +
|
146
|
-
"${ENV:ProgramFiles}\\Puppet Labs\\Puppet\\puppet\\bin;" +
|
147
|
-
"${ENV:ProgramFiles}\\Puppet Labs\\Puppet\\sys\\ruby\\bin\\"
|
148
|
-
$ENV:RUBYLIB = "${ENV:ProgramFiles}\\Puppet Labs\\Puppet\\puppet\\lib;" +
|
149
|
-
"${ENV:ProgramFiles}\\Puppet Labs\\Puppet\\facter\\lib;" +
|
150
|
-
"${ENV:ProgramFiles}\\Puppet Labs\\Puppet\\hiera\\lib;" +
|
151
|
-
$ENV:RUBYLIB
|
152
|
-
|
153
|
-
Add-Type -AssemblyName System.ServiceModel.Web, System.Runtime.Serialization
|
154
|
-
$utf8 = [System.Text.Encoding]::UTF8
|
155
|
-
|
156
|
-
function Write-Stream {
|
157
|
-
PARAM(
|
158
|
-
[Parameter(Position=0)] $stream,
|
159
|
-
[Parameter(ValueFromPipeline=$true)] $string
|
160
|
-
)
|
161
|
-
PROCESS {
|
162
|
-
$bytes = $utf8.GetBytes($string)
|
163
|
-
$stream.Write( $bytes, 0, $bytes.Length )
|
164
|
-
}
|
165
|
-
}
|
166
|
-
|
167
|
-
function Convert-JsonToXml {
|
168
|
-
PARAM([Parameter(ValueFromPipeline=$true)] [string[]] $json)
|
169
|
-
BEGIN {
|
170
|
-
$mStream = New-Object System.IO.MemoryStream
|
171
|
-
}
|
172
|
-
PROCESS {
|
173
|
-
$json | Write-Stream -Stream $mStream
|
174
|
-
}
|
175
|
-
END {
|
176
|
-
$mStream.Position = 0
|
177
|
-
try {
|
178
|
-
$jsonReader = [System.Runtime.Serialization.Json.JsonReaderWriterFactory]::CreateJsonReader($mStream,[System.Xml.XmlDictionaryReaderQuotas]::Max)
|
179
|
-
$xml = New-Object Xml.XmlDocument
|
180
|
-
$xml.Load($jsonReader)
|
181
|
-
$xml
|
182
|
-
} finally {
|
183
|
-
$jsonReader.Close()
|
184
|
-
$mStream.Dispose()
|
185
|
-
}
|
186
|
-
}
|
187
|
-
}
|
188
|
-
|
189
|
-
Function ConvertFrom-Xml {
|
190
|
-
[CmdletBinding(DefaultParameterSetName="AutoType")]
|
191
|
-
PARAM(
|
192
|
-
[Parameter(ValueFromPipeline=$true,Mandatory=$true,Position=1)] [Xml.XmlNode] $xml,
|
193
|
-
[Parameter(Mandatory=$true,ParameterSetName="ManualType")] [Type] $Type,
|
194
|
-
[Switch] $ForceType
|
195
|
-
)
|
196
|
-
PROCESS{
|
197
|
-
if (Get-Member -InputObject $xml -Name root) {
|
198
|
-
return $xml.root.Objects | ConvertFrom-Xml
|
199
|
-
} elseif (Get-Member -InputObject $xml -Name Objects) {
|
200
|
-
return $xml.Objects | ConvertFrom-Xml
|
201
|
-
}
|
202
|
-
$propbag = @{}
|
203
|
-
foreach ($name in Get-Member -InputObject $xml -MemberType Properties | Where-Object{$_.Name -notmatch "^(__.*|type)$"} | Select-Object -ExpandProperty name) {
|
204
|
-
Write-Debug "$Name Type: $($xml.$Name.type)" -Debug:$false
|
205
|
-
$propbag."$Name" = Convert-Properties $xml."$name"
|
206
|
-
}
|
207
|
-
if (!$Type -and $xml.HasAttribute("__type")) { $Type = $xml.__Type }
|
208
|
-
if ($ForceType -and $Type) {
|
209
|
-
try {
|
210
|
-
$output = New-Object $Type -Property $propbag
|
211
|
-
} catch {
|
212
|
-
$output = New-Object PSObject -Property $propbag
|
213
|
-
$output.PsTypeNames.Insert(0, $xml.__type)
|
214
|
-
}
|
215
|
-
} elseif ($propbag.Count -ne 0) {
|
216
|
-
$output = New-Object PSObject -Property $propbag
|
217
|
-
if ($Type) {
|
218
|
-
$output.PsTypeNames.Insert(0, $Type)
|
219
|
-
}
|
220
|
-
}
|
221
|
-
return $output
|
222
|
-
}
|
223
|
-
}
|
224
|
-
|
225
|
-
Function Convert-Properties {
|
226
|
-
PARAM($InputObject)
|
227
|
-
switch ($InputObject.type) {
|
228
|
-
"object" {
|
229
|
-
return (ConvertFrom-Xml -Xml $InputObject)
|
230
|
-
}
|
231
|
-
"string" {
|
232
|
-
$MightBeADate = $InputObject.get_InnerText() -as [DateTime]
|
233
|
-
## Strings that are actually dates (*grumble* JSON is crap)
|
234
|
-
if ($MightBeADate -and $propbag."$Name" -eq $MightBeADate.ToString("G")) {
|
235
|
-
return $MightBeADate
|
236
|
-
} else {
|
237
|
-
return $InputObject.get_InnerText()
|
238
|
-
}
|
239
|
-
}
|
240
|
-
"number" {
|
241
|
-
$number = $InputObject.get_InnerText()
|
242
|
-
if ($number -eq ($number -as [int])) {
|
243
|
-
return $number -as [int]
|
244
|
-
} elseif ($number -eq ($number -as [double])) {
|
245
|
-
return $number -as [double]
|
246
|
-
} else {
|
247
|
-
return $number -as [decimal]
|
248
|
-
}
|
249
|
-
}
|
250
|
-
"boolean" {
|
251
|
-
return [bool]::parse($InputObject.get_InnerText())
|
252
|
-
}
|
253
|
-
"null" {
|
254
|
-
return $null
|
255
|
-
}
|
256
|
-
"array" {
|
257
|
-
[object[]]$Items = $(foreach( $item in $InputObject.GetEnumerator() ) {
|
258
|
-
Convert-Properties $item
|
259
|
-
})
|
260
|
-
return $Items
|
261
|
-
}
|
262
|
-
default {
|
263
|
-
return $InputObject
|
264
|
-
}
|
265
|
-
}
|
266
|
-
}
|
267
|
-
|
268
|
-
Function ConvertFrom-Json2 {
|
269
|
-
[CmdletBinding()]
|
270
|
-
PARAM(
|
271
|
-
[Parameter(ValueFromPipeline=$true,Mandatory=$true,Position=1)] [string] $InputObject,
|
272
|
-
[Parameter(Mandatory=$true)] [Type] $Type,
|
273
|
-
[Switch] $ForceType
|
274
|
-
)
|
275
|
-
PROCESS {
|
276
|
-
$null = $PSBoundParameters.Remove("InputObject")
|
277
|
-
[Xml.XmlElement]$xml = (Convert-JsonToXml $InputObject).Root
|
278
|
-
if ($xml) {
|
279
|
-
if ($xml.Objects) {
|
280
|
-
$xml.Objects.Item.GetEnumerator() | ConvertFrom-Xml @PSBoundParameters
|
281
|
-
} elseif ($xml.Item -and $xml.Item -isnot [System.Management.Automation.PSParameterizedProperty]) {
|
282
|
-
$xml.Item | ConvertFrom-Xml @PSBoundParameters
|
283
|
-
} else {
|
284
|
-
$xml | ConvertFrom-Xml @PSBoundParameters
|
285
|
-
}
|
286
|
-
} else {
|
287
|
-
Write-Error "Failed to parse JSON with JsonReader" -Debug:$false
|
288
|
-
}
|
289
|
-
}
|
290
|
-
}
|
291
|
-
|
292
|
-
function ConvertFrom-PSCustomObject
|
293
|
-
{
|
294
|
-
PARAM([Parameter(ValueFromPipeline = $true)] $InputObject)
|
295
|
-
PROCESS {
|
296
|
-
if ($null -eq $InputObject) { return $null }
|
297
|
-
|
298
|
-
if ($InputObject -is [System.Collections.IEnumerable] -and $InputObject -isnot [string]) {
|
299
|
-
$collection = @(
|
300
|
-
foreach ($object in $InputObject) { ConvertFrom-PSCustomObject $object }
|
301
|
-
)
|
302
|
-
|
303
|
-
$collection
|
304
|
-
} elseif ($InputObject -is [System.Management.Automation.PSCustomObject]) {
|
305
|
-
$hash = @{}
|
306
|
-
foreach ($property in $InputObject.PSObject.Properties) {
|
307
|
-
$hash[$property.Name] = ConvertFrom-PSCustomObject $property.Value
|
308
|
-
}
|
309
|
-
|
310
|
-
$hash
|
311
|
-
} else {
|
312
|
-
$InputObject
|
313
|
-
}
|
314
|
-
}
|
315
|
-
}
|
316
|
-
|
317
|
-
function Get-ContentAsJson
|
318
|
-
{
|
319
|
-
[CmdletBinding()]
|
320
|
-
PARAM(
|
321
|
-
[Parameter(Mandatory = $true)] $Text,
|
322
|
-
[Parameter(Mandatory = $false)] [Text.Encoding] $Encoding = [Text.Encoding]::UTF8
|
323
|
-
)
|
324
|
-
|
325
|
-
# using polyfill cmdlet on PS2, so pass type info
|
326
|
-
if ($PSVersionTable.PSVersion -lt [Version]'3.0') {
|
327
|
-
$Text | ConvertFrom-Json2 -Type PSObject | ConvertFrom-PSCustomObject
|
328
|
-
} else {
|
329
|
-
$Text | ConvertFrom-Json | ConvertFrom-PSCustomObject
|
330
|
-
}
|
331
|
-
}
|
332
|
-
PS
|
333
|
-
end
|
334
|
-
end
|
335
|
-
end
|
336
|
-
end
|
337
|
-
end
|