ruby-pwsh 0.1.0 → 0.5.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.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/.pmtignore +21 -0
- data/.rubocop.yml +12 -2
- data/.travis.yml +1 -0
- data/CHANGELOG.md +52 -0
- data/CONTRIBUTING.md +155 -0
- data/Gemfile +8 -9
- data/README.md +50 -0
- data/Rakefile +58 -2
- data/appveyor.yml +38 -0
- data/lib/puppet/feature/pwshlib.rb +5 -0
- data/lib/puppet/provider/dsc_base_provider/dsc_base_provider.rb +666 -0
- data/lib/puppet/provider/dsc_base_provider/invoke_dsc_resource_functions.ps1 +120 -0
- data/lib/puppet/provider/dsc_base_provider/invoke_dsc_resource_postscript.ps1 +23 -0
- data/lib/puppet/provider/dsc_base_provider/invoke_dsc_resource_preamble.ps1 +8 -0
- data/lib/pwsh.rb +4 -3
- data/lib/pwsh/util.rb +112 -1
- data/lib/pwsh/version.rb +1 -1
- data/metadata.json +85 -0
- data/pwshlib.md +92 -0
- metadata +13 -2
@@ -0,0 +1,120 @@
|
|
1
|
+
function new-pscredential {
|
2
|
+
[CmdletBinding()]
|
3
|
+
param (
|
4
|
+
[parameter(Mandatory = $true,
|
5
|
+
ValueFromPipelineByPropertyName = $true)]
|
6
|
+
[string]
|
7
|
+
$user,
|
8
|
+
|
9
|
+
[parameter(Mandatory = $true,
|
10
|
+
ValueFromPipelineByPropertyName = $true)]
|
11
|
+
[string]
|
12
|
+
$password
|
13
|
+
)
|
14
|
+
|
15
|
+
$secpasswd = ConvertTo-SecureString $password -AsPlainText -Force
|
16
|
+
$credentials = New-Object System.Management.Automation.PSCredential ($user, $secpasswd)
|
17
|
+
return $credentials
|
18
|
+
}
|
19
|
+
|
20
|
+
Function ConvertTo-CanonicalResult {
|
21
|
+
[CmdletBinding()]
|
22
|
+
param(
|
23
|
+
[Parameter(Mandatory, Position = 1)]
|
24
|
+
[psobject]
|
25
|
+
$Result,
|
26
|
+
|
27
|
+
[Parameter(DontShow)]
|
28
|
+
[string]
|
29
|
+
$PropertyPath,
|
30
|
+
|
31
|
+
[Parameter(DontShow)]
|
32
|
+
[int]
|
33
|
+
$RecursionLevel = 0
|
34
|
+
)
|
35
|
+
|
36
|
+
$MaxDepth = 5
|
37
|
+
$CimInstancePropertyFilter = { $_.Definition -match 'CimInstance' -and $_.Name -ne 'PSDscRunAsCredential' }
|
38
|
+
|
39
|
+
# Get the properties which are/aren't Cim instances
|
40
|
+
$ResultObject = @{ }
|
41
|
+
$ResultPropertyList = $Result | Get-Member -MemberType Property | Where-Object { $_.Name -ne 'PSComputerName' }
|
42
|
+
$CimInstanceProperties = $ResultPropertyList | Where-Object -FilterScript $CimInstancePropertyFilter
|
43
|
+
|
44
|
+
foreach ($Property in $ResultPropertyList) {
|
45
|
+
$PropertyName = $Property.Name
|
46
|
+
if ($Property -notin $CimInstanceProperties) {
|
47
|
+
$Value = $Result.$PropertyName
|
48
|
+
if ($PropertyName -eq 'Ensure' -and [string]::IsNullOrEmpty($Result.$PropertyName)) {
|
49
|
+
# Just set 'Present' since it was found /shrug
|
50
|
+
# If the value IS listed as absent, don't update it unless you want flapping
|
51
|
+
$Value = 'Present'
|
52
|
+
}
|
53
|
+
else {
|
54
|
+
if ($Value -is [string] -or $value -is [string[]]) {
|
55
|
+
$Value = $Value
|
56
|
+
}
|
57
|
+
|
58
|
+
if ($Value.Count -eq 1 -and $Property.Definition -match '\\[\\]') {
|
59
|
+
$Value = @($Value)
|
60
|
+
}
|
61
|
+
}
|
62
|
+
}
|
63
|
+
elseif ($null -eq $Result.$PropertyName) {
|
64
|
+
if ($Property -match 'InstanceArray') {
|
65
|
+
$Value = @()
|
66
|
+
}
|
67
|
+
else {
|
68
|
+
$Value = $null
|
69
|
+
}
|
70
|
+
}
|
71
|
+
else {
|
72
|
+
# Looks like a nested CIM instance, recurse if we're not too deep in already.
|
73
|
+
$RecursionLevel++
|
74
|
+
|
75
|
+
if ($PropertyPath -eq [string]::Empty) {
|
76
|
+
$PropertyPath = $PropertyName
|
77
|
+
}
|
78
|
+
else {
|
79
|
+
$PropertyPath = "$PropertyPath.$PropertyName"
|
80
|
+
}
|
81
|
+
|
82
|
+
if ($RecursionLevel -gt $MaxDepth) {
|
83
|
+
# Give up recursing more than this
|
84
|
+
return $Result.ToString()
|
85
|
+
}
|
86
|
+
|
87
|
+
$Value = foreach ($item in $Result.$PropertyName) {
|
88
|
+
ConvertTo-CanonicalResult -Result $item -PropertyPath $PropertyPath -RecursionLevel ($RecursionLevel + 1) -WarningAction Continue
|
89
|
+
}
|
90
|
+
|
91
|
+
# The cim instance type is the last component of the type Name
|
92
|
+
# We need to return this for ruby to compare the result hashes
|
93
|
+
# We do NOT need it for the top-level properties as those are defined in the type
|
94
|
+
If ($RecursionLevel -gt 1 -and ![string]::IsNullOrEmpty($Value) ) {
|
95
|
+
# If there's multiple instances, you need to add the type to each one, but you
|
96
|
+
# need to specify only *one* name, otherwise things end up *very* broken.
|
97
|
+
if ($Value.GetType().Name -match '\[\]') {
|
98
|
+
$Value | ForEach-Object -Process {
|
99
|
+
$_.cim_instance_type = $Result.$PropertyName.CimClass.CimClassName[0]
|
100
|
+
}
|
101
|
+
} else {
|
102
|
+
$Value.cim_instance_type = $Result.$PropertyName.CimClass.CimClassName
|
103
|
+
# Ensure that, if it should be an array, it is
|
104
|
+
if ($Result.$PropertyName.GetType().Name -match '\[\]') {
|
105
|
+
$Value = @($Value)
|
106
|
+
}
|
107
|
+
}
|
108
|
+
}
|
109
|
+
}
|
110
|
+
|
111
|
+
if ($Property.Definition -match 'InstanceArray') {
|
112
|
+
if ($Value.Count -lt 2) { $Value = @($Value) }
|
113
|
+
}
|
114
|
+
|
115
|
+
$ResultObject.$PropertyName = $Value
|
116
|
+
}
|
117
|
+
|
118
|
+
# Output the final result
|
119
|
+
$ResultObject
|
120
|
+
}
|
@@ -0,0 +1,23 @@
|
|
1
|
+
Try {
|
2
|
+
$Result = Invoke-DscResource @InvokeParams
|
3
|
+
} catch {
|
4
|
+
$Response.errormessage = $_.Exception.Message
|
5
|
+
return ($Response | ConvertTo-Json -Compress)
|
6
|
+
}
|
7
|
+
|
8
|
+
# keep the switch for when Test passes back changed properties
|
9
|
+
Switch ($invokeParams.Method) {
|
10
|
+
'Test' {
|
11
|
+
$Response.indesiredstate = $Result.InDesiredState
|
12
|
+
return ($Response | ConvertTo-Json -Compress)
|
13
|
+
}
|
14
|
+
'Set' {
|
15
|
+
$Response.indesiredstate = $true
|
16
|
+
$Response.rebootrequired = $Result.RebootRequired
|
17
|
+
return ($Response | ConvertTo-Json -Compress)
|
18
|
+
}
|
19
|
+
'Get' {
|
20
|
+
$CanonicalizedResult = ConvertTo-CanonicalResult -Result $Result
|
21
|
+
return ($CanonicalizedResult | ConvertTo-Json -Compress -Depth 10)
|
22
|
+
}
|
23
|
+
}
|
data/lib/pwsh.rb
CHANGED
@@ -54,7 +54,7 @@ module Pwsh
|
|
54
54
|
if manager.nil? || !manager.alive?
|
55
55
|
# ignore any errors trying to tear down this unusable instance
|
56
56
|
begin
|
57
|
-
manager
|
57
|
+
manager.exit unless manager.nil? # rubocop:disable Style/SafeNavigation
|
58
58
|
rescue
|
59
59
|
nil
|
60
60
|
end
|
@@ -117,6 +117,7 @@ module Pwsh
|
|
117
117
|
# This named pipe path is Windows specific.
|
118
118
|
pipe_path = "\\\\.\\pipe\\#{named_pipe_name}"
|
119
119
|
else
|
120
|
+
require 'tmpdir'
|
120
121
|
# .Net implements named pipes under Linux etc. as Unix Sockets in the filesystem
|
121
122
|
# Paths that are rooted are not munged within C# Core.
|
122
123
|
# https://github.com/dotnet/corefx/blob/94e9d02ad70b2224d012ac4a66eaa1f913ae4f29/src/System.IO.Pipes/src/System/IO/Pipes/PipeStream.Unix.cs#L49-L60
|
@@ -470,7 +471,7 @@ Invoke-PowerShellUserCode @params
|
|
470
471
|
# @return [String] The UTF-8 encoded string containing the payload
|
471
472
|
def self.read_length_prefixed_string!(bytes)
|
472
473
|
# 32 bit integer in Little Endian format
|
473
|
-
length = bytes.slice!(0, 4).
|
474
|
+
length = bytes.slice!(0, 4).unpack1('V')
|
474
475
|
return nil if length.zero?
|
475
476
|
|
476
477
|
bytes.slice!(0, length).force_encoding(Encoding::UTF_8)
|
@@ -585,7 +586,7 @@ Invoke-PowerShellUserCode @params
|
|
585
586
|
|
586
587
|
pipe_reader = Thread.new(@pipe) do |pipe|
|
587
588
|
# Read a Little Endian 32-bit integer for length of response
|
588
|
-
expected_response_length = pipe.sysread(4).
|
589
|
+
expected_response_length = pipe.sysread(4).unpack1('V')
|
589
590
|
|
590
591
|
next nil if expected_response_length.zero?
|
591
592
|
|
data/lib/pwsh/util.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# Manage PowerShell and Windows PowerShell via ruby
|
3
4
|
module Pwsh
|
4
5
|
# Various helper methods
|
5
6
|
module Util
|
@@ -11,7 +12,7 @@ module Pwsh
|
|
11
12
|
def on_windows?
|
12
13
|
# Ruby only sets File::ALT_SEPARATOR on Windows and the Ruby standard
|
13
14
|
# library uses that to test what platform it's on.
|
14
|
-
!!File::ALT_SEPARATOR
|
15
|
+
!!File::ALT_SEPARATOR
|
15
16
|
end
|
16
17
|
|
17
18
|
# Verify paths specified are valid directories which exist.
|
@@ -29,6 +30,116 @@ module Pwsh
|
|
29
30
|
|
30
31
|
invalid_paths
|
31
32
|
end
|
33
|
+
|
34
|
+
# Return a string or symbol converted to snake_case
|
35
|
+
#
|
36
|
+
# @return [String] snake_cased string
|
37
|
+
def snake_case(object)
|
38
|
+
# Implementation copied from: https://github.com/rubyworks/facets/blob/master/lib/core/facets/string/snakecase.rb
|
39
|
+
# gsub(/::/, '/').
|
40
|
+
should_symbolize = object.is_a?(Symbol)
|
41
|
+
raise "snake_case method only handles strings and symbols, passed a #{object.class}: #{object}" unless should_symbolize || object.is_a?(String)
|
42
|
+
|
43
|
+
text = object.to_s
|
44
|
+
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
45
|
+
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
46
|
+
.tr('-', '_')
|
47
|
+
.gsub(/\s/, '_')
|
48
|
+
.gsub(/__+/, '_')
|
49
|
+
.downcase
|
50
|
+
should_symbolize ? text.to_sym : text
|
51
|
+
end
|
52
|
+
|
53
|
+
# Iterate through a hashes keys, snake_casing them
|
54
|
+
#
|
55
|
+
# @return [Hash] Hash with all keys snake_cased
|
56
|
+
def snake_case_hash_keys(object)
|
57
|
+
snake_case_proc = proc { |key| snake_case(key) }
|
58
|
+
apply_key_mutator(object, snake_case_proc)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Return a string or symbol converted to PascalCase
|
62
|
+
#
|
63
|
+
# @return [String] PascalCased string
|
64
|
+
def pascal_case(object)
|
65
|
+
should_symbolize = object.is_a?(Symbol)
|
66
|
+
raise "snake_case method only handles strings and symbols, passed a #{object.class}: #{object}" unless should_symbolize || object.is_a?(String)
|
67
|
+
|
68
|
+
# Break word boundaries to snake case first
|
69
|
+
text = snake_case(object.to_s).split('_').collect(&:capitalize).join
|
70
|
+
should_symbolize ? text.to_sym : text
|
71
|
+
end
|
72
|
+
|
73
|
+
# Iterate through a hashes keys, PascalCasing them
|
74
|
+
#
|
75
|
+
# @return [Hash] Hash with all keys PascalCased
|
76
|
+
def pascal_case_hash_keys(object)
|
77
|
+
pascal_case_proc = proc { |key| pascal_case(key) }
|
78
|
+
apply_key_mutator(object, pascal_case_proc)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Ensure that quotes inside a passed string will continue to be passed
|
82
|
+
#
|
83
|
+
# @return [String] the string with quotes escaped
|
84
|
+
def escape_quotes(text)
|
85
|
+
text.gsub("'", "''")
|
86
|
+
end
|
87
|
+
|
88
|
+
# Ensure that all keys in a hash are symbols, not strings.
|
89
|
+
#
|
90
|
+
# @return [Hash] a hash whose keys have been converted to symbols.
|
91
|
+
def symbolize_hash_keys(object)
|
92
|
+
symbolize_proc = proc(&:to_sym)
|
93
|
+
apply_key_mutator(object, symbolize_proc)
|
94
|
+
end
|
95
|
+
|
96
|
+
def apply_key_mutator(object, proc)
|
97
|
+
return object.map { |item| apply_key_mutator(item, proc) } if object.is_a?(Array)
|
98
|
+
return object unless object.is_a?(Hash)
|
99
|
+
|
100
|
+
modified_hash = {}
|
101
|
+
object.each do |key, value|
|
102
|
+
modified_hash[proc.call(key)] = apply_key_mutator(value, proc)
|
103
|
+
end
|
104
|
+
modified_hash
|
105
|
+
end
|
106
|
+
|
107
|
+
private_class_method :apply_key_mutator
|
108
|
+
|
109
|
+
# Convert a ruby value into a string to be passed along to PowerShell for interpolation in a command
|
110
|
+
# Handles:
|
111
|
+
# - Strings
|
112
|
+
# - Numbers
|
113
|
+
# - Booleans
|
114
|
+
# - Symbols
|
115
|
+
# - Arrays
|
116
|
+
# - Hashes
|
117
|
+
#
|
118
|
+
# @return [String] representation of the value for interpolation
|
119
|
+
def format_powershell_value(object)
|
120
|
+
if %i[true false].include?(object) || %w[trueclass falseclass].include?(object.class.name.downcase) # rubocop:disable Lint/BooleanSymbol
|
121
|
+
"$#{object}"
|
122
|
+
elsif object.class.name == 'Symbol' || object.class.ancestors.include?(Numeric)
|
123
|
+
object.to_s
|
124
|
+
elsif object.class.name == 'String'
|
125
|
+
"'#{escape_quotes(object)}'"
|
126
|
+
elsif object.class.name == 'Array'
|
127
|
+
'@(' + object.collect { |item| format_powershell_value(item) }.join(', ') + ')'
|
128
|
+
elsif object.class.name == 'Hash'
|
129
|
+
'@{' + object.collect { |k, v| format_powershell_value(k) + ' = ' + format_powershell_value(v) }.join('; ') + '}'
|
130
|
+
else
|
131
|
+
raise "unsupported type #{object.class} of value '#{object}'"
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# Return the representative string of a PowerShell hash for a custom object property to be used in selecting or filtering.
|
136
|
+
# The script block for the expression must be passed as the string you want interpolated into the hash; this method does
|
137
|
+
# not do any of the additional work of interpolation for you as the type sits inside a code block inside a hash.
|
138
|
+
#
|
139
|
+
# @return [String] representation of a PowerShell hash with the keys 'Name' and 'Expression'
|
140
|
+
def custom_powershell_property(name, expression)
|
141
|
+
"@{Name = '#{name}'; Expression = {#{expression}}}"
|
142
|
+
end
|
32
143
|
end
|
33
144
|
end
|
34
145
|
|
data/lib/pwsh/version.rb
CHANGED
data/metadata.json
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
{
|
2
|
+
"name": "puppetlabs-pwshlib",
|
3
|
+
"version": "0.4.1",
|
4
|
+
"author": "puppetlabs",
|
5
|
+
"summary": "Provide library code for interoperating with PowerShell.",
|
6
|
+
"license": "MIT",
|
7
|
+
"source": "https://github.com/puppetlabs/ruby-pwsh",
|
8
|
+
"project_page": "https://github.com/puppetlabs/ruby-pwsh/blob/master/pwshlib.md",
|
9
|
+
"issues_url": "https://github.com/puppetlabs/ruby-pwsh/issues",
|
10
|
+
"dependencies": [
|
11
|
+
|
12
|
+
],
|
13
|
+
"operatingsystem_support": [
|
14
|
+
{
|
15
|
+
"operatingsystem": "CentOS",
|
16
|
+
"operatingsystemrelease": [
|
17
|
+
"7"
|
18
|
+
]
|
19
|
+
},
|
20
|
+
{
|
21
|
+
"operatingsystem": "OracleLinux",
|
22
|
+
"operatingsystemrelease": [
|
23
|
+
"7"
|
24
|
+
]
|
25
|
+
},
|
26
|
+
{
|
27
|
+
"operatingsystem": "RedHat",
|
28
|
+
"operatingsystemrelease": [
|
29
|
+
"8"
|
30
|
+
]
|
31
|
+
},
|
32
|
+
{
|
33
|
+
"operatingsystem": "Scientific",
|
34
|
+
"operatingsystemrelease": [
|
35
|
+
"7"
|
36
|
+
]
|
37
|
+
},
|
38
|
+
{
|
39
|
+
"operatingsystem": "Debian",
|
40
|
+
"operatingsystemrelease": [
|
41
|
+
"9"
|
42
|
+
]
|
43
|
+
},
|
44
|
+
{
|
45
|
+
"operatingsystem": "Ubuntu",
|
46
|
+
"operatingsystemrelease": [
|
47
|
+
"18.04"
|
48
|
+
]
|
49
|
+
},
|
50
|
+
{
|
51
|
+
"operatingsystem": "windows",
|
52
|
+
"operatingsystemrelease": [
|
53
|
+
"2019",
|
54
|
+
"10"
|
55
|
+
]
|
56
|
+
},
|
57
|
+
{
|
58
|
+
"operatingsystem": "SLES",
|
59
|
+
"operatingsystemrelease": [
|
60
|
+
"15"
|
61
|
+
]
|
62
|
+
},
|
63
|
+
{
|
64
|
+
"operatingsystem": "Darwin",
|
65
|
+
"operatingsystemrelease": [
|
66
|
+
"16"
|
67
|
+
]
|
68
|
+
},
|
69
|
+
{
|
70
|
+
"operatingsystem": "Fedora",
|
71
|
+
"operatingsystemrelease": [
|
72
|
+
"29"
|
73
|
+
]
|
74
|
+
}
|
75
|
+
],
|
76
|
+
"requirements": [
|
77
|
+
{
|
78
|
+
"name": "puppet",
|
79
|
+
"version_requirement": ">= 5.5.0 < 7.0.0"
|
80
|
+
}
|
81
|
+
],
|
82
|
+
"pdk-version": "1.13.0",
|
83
|
+
"template-url": "pdk-default#1.13.0",
|
84
|
+
"template-ref": "1.13.0-0-g66e1443"
|
85
|
+
}
|
data/pwshlib.md
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
# pwshlib
|
2
|
+
|
3
|
+
This module enables you to leverage the `ruby-pwsh` gem to execute PowerShell from within your Puppet providers without having to instantiate and tear down a PowerShell process for each command called.
|
4
|
+
It supports Windows PowerShell as well as PowerShell Core - if you're running **PowerShell v3+**, this gem supports you.
|
5
|
+
|
6
|
+
The `Manager` class enables you to execute and interoperate with PowerShell from within ruby, leveraging the strengths of both languages as needed.
|
7
|
+
|
8
|
+
## Prerequisites
|
9
|
+
|
10
|
+
Include `puppetlabs-pwshlib` as a dependency in your module and you can leverage it in your providers by using a requires statement, such as in this example:
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
require 'puppet/resource_api/simple_provider'
|
14
|
+
begin
|
15
|
+
require 'ruby-pwsh'
|
16
|
+
rescue LoadError
|
17
|
+
raise 'Could not load the "ruby-pwsh" library; is the dependency module puppetlabs-pwshlib installed in this environment?'
|
18
|
+
end
|
19
|
+
|
20
|
+
# Implementation for the foo type using the Resource API.
|
21
|
+
class Puppet::Provider::Foo::Foo < Puppet::ResourceApi::SimpleProvider
|
22
|
+
def get(context)
|
23
|
+
context.debug("PowerShell Path: #{Pwsh::Manager.powershell_path}")
|
24
|
+
context.debug('Returning pre-canned example data')
|
25
|
+
[
|
26
|
+
{
|
27
|
+
name: 'foo',
|
28
|
+
ensure: 'present',
|
29
|
+
},
|
30
|
+
{
|
31
|
+
name: 'bar',
|
32
|
+
ensure: 'present',
|
33
|
+
},
|
34
|
+
]
|
35
|
+
end
|
36
|
+
|
37
|
+
def create(context, name, should)
|
38
|
+
context.notice("Creating '#{name}' with #{should.inspect}")
|
39
|
+
end
|
40
|
+
|
41
|
+
def update(context, name, should)
|
42
|
+
context.notice("Updating '#{name}' with #{should.inspect}")
|
43
|
+
end
|
44
|
+
|
45
|
+
def delete(context, name)
|
46
|
+
context.notice("Deleting '#{name}'")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
```
|
50
|
+
|
51
|
+
Aside from adding it as a dependency to your module metadata, you will probably also want to include it in your `.fixtures.yml` file:
|
52
|
+
|
53
|
+
```yaml
|
54
|
+
fixtures:
|
55
|
+
forge_modules:
|
56
|
+
pwshlib: "puppetlabs/pwshlib"
|
57
|
+
```
|
58
|
+
|
59
|
+
## Using the Library
|
60
|
+
|
61
|
+
Instantiating the manager can be done using some defaults:
|
62
|
+
|
63
|
+
```ruby
|
64
|
+
# Instantiate the manager for Windows PowerShell, using the default path and arguments
|
65
|
+
# Note that this takes a few seconds to instantiate.
|
66
|
+
posh = Pwsh::Manager.instance(Pwsh::Manager.powershell_path, Pwsh::Manager.powershell_args)
|
67
|
+
# If you try to create another manager with the same arguments it will reuse the existing one.
|
68
|
+
ps = Pwsh::Manager.instance(Pwsh::Manager.powershell_path, Pwsh::Manager.powershell_args)
|
69
|
+
# Note that this time the return is very fast.
|
70
|
+
# We can also use the defaults for PowerShell Core, though these only work if PowerShell is
|
71
|
+
# installed to the default paths - if it is installed anywhere else, you'll need to specify
|
72
|
+
# the full path to the pwsh executable.
|
73
|
+
pwsh = Pwsh::Manager.instance(Pwsh::Manager.pwsh_path, Pwsh::Manager.pwsh_args)
|
74
|
+
```
|
75
|
+
|
76
|
+
Execution can be done with relatively little additional work - pass the command string you want executed:
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
# Instantiate the Manager:
|
80
|
+
posh = Pwsh::Manager.instance(Pwsh::Manager.powershell_path, Pwsh::Manager.powershell_args)
|
81
|
+
# Pretty print the output of `$PSVersionTable` to validate the version of PowerShell running
|
82
|
+
# Note that the output is a hash with a few different keys, including stdout.
|
83
|
+
Puppet.debug(posh.execute('$PSVersionTable'))
|
84
|
+
# Lets reduce the noise a little and retrieve just the version number:
|
85
|
+
# Note: We cast to a string because PSVersion is actually a Version object.
|
86
|
+
Puppet.debug(posh.execute('[String]$PSVersionTable.PSVersion'))
|
87
|
+
# We could store this output to a ruby variable if we wanted, for further use:
|
88
|
+
ps_version = posh.execute('[String]$PSVersionTable.PSVersion')[:stdout].strip
|
89
|
+
Puppet.debug("The PowerShell version of the currently running Manager is #{ps_version}")
|
90
|
+
```
|
91
|
+
|
92
|
+
For more information, please review the [online reference documentation for the gem](https://rubydoc.info/gems/ruby-pwsh).
|