ruby-pwsh 0.1.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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).
|