bolt 2.5.0 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
|