Skip to content

Commit 08b69bf

Browse files
committed
powershell adjustment
1 parent ad35820 commit 08b69bf

File tree

1 file changed

+83
-9
lines changed

1 file changed

+83
-9
lines changed

lib/puppet/provider/base_dsc_lite/powershell.rb

Lines changed: 83 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
require 'pathname'
44
require 'json'
5+
require 'erb' # ensure ERB is available
6+
require 'puppet'
7+
require 'puppet/node'
8+
require 'puppet/parser/script_compiler'
59
require 'puppet/pops/evaluator/deferred_resolver'
610
require_relative '../../../puppet_x/puppetlabs/dsc_lite/powershell_hash_formatter'
711

@@ -41,9 +45,13 @@ def self.vendored_modules_path
4145
File.expand_path(Pathname.new(__FILE__).dirname + '../../../' + 'puppet_x/dsc_resources')
4246
end
4347

48+
# ---------- Resolution helpers (Option 2) ----------
49+
50+
# 1) Catalog-wide resolve: replace all Deferreds/futures in the catalog
4451
def force_resolve_catalog_deferred!
4552
cat = resource&.catalog
4653
return unless cat
54+
4755
facts = Puppet.lookup(:facts) { nil }
4856
env = if cat.respond_to?(:environment_instance)
4957
cat.environment_instance
@@ -58,6 +66,59 @@ def force_resolve_catalog_deferred!
5866
end
5967
end
6068

69+
# Build a compiler on the agent for local resolution (ScriptCompiler on Puppet 8)
70+
def build_agent_compiler(env)
71+
node_name = Puppet[:node_name_value]
72+
node = Puppet::Node.new(node_name, environment: env)
73+
74+
# If you can attach facts, do so—it can influence function behavior during resolve
75+
begin
76+
facts = Puppet.lookup(:facts) { nil }
77+
node.add_facts(facts) if facts
78+
rescue => e
79+
Puppet.debug("DSC_lite: could not attach facts to node for local resolve: #{e.class}: #{e.message}")
80+
end
81+
82+
if defined?(Puppet::Parser::ScriptCompiler)
83+
Puppet::Parser::ScriptCompiler.new(node, env)
84+
else
85+
Puppet::Parser::Compiler.new(node)
86+
end
87+
end
88+
89+
# 2) Targeted resolve: explicitly resolve the :properties value using a resolver that
90+
# can handle both Deferred and evaluator futures, then write it back to the resource.
91+
def force_resolve_properties!
92+
return unless resource.parameters.key?(:properties)
93+
94+
cat = resource&.catalog
95+
env = if cat&.respond_to?(:environment_instance)
96+
cat.environment_instance
97+
else
98+
Puppet.lookup(:current_environment) { nil }
99+
end
100+
101+
begin
102+
compiler = build_agent_compiler(env) if env
103+
return unless compiler # without a compiler, local resolve can't proceed
104+
105+
facts = Puppet.lookup(:facts) { nil }
106+
107+
resolver = Puppet::Pops::Evaluator::DeferredResolver.new(compiler, true)
108+
resolver.set_facts_variable(facts) if facts
109+
110+
props = resource.parameters[:properties].value
111+
resolved = resolver.resolve(props) # handles nested Deferred and futures
112+
resource.parameters[:properties].value = resolved
113+
114+
Puppet.debug('DSC_lite: explicitly resolved resource[:properties] on agent')
115+
rescue => e
116+
Puppet.debug("DSC_lite: explicit properties resolve failed: #{e.class}: #{e.message}")
117+
end
118+
end
119+
120+
# ---------- Existing provider helpers ----------
121+
61122
def dsc_parameters
62123
resource.parameters_with_value.select do |p|
63124
p.name.to_s.include? 'dsc_'
@@ -83,20 +144,31 @@ def ps_manager
83144
Pwsh::Manager.instance(command(:powershell), Pwsh::Manager.powershell_args, debug: debug_output)
84145
end
85146

147+
# If your ERBs call provider.format_for_ps(...), keep this minimal helper
148+
def format_for_ps(value)
149+
self.class.format_dsc_lite(value)
150+
end
151+
152+
# ---------- Provider operations ----------
153+
86154
def exists?
87155
Puppet.notice("DSC PROVIDER SENTINEL → #{__FILE__}")
156+
157+
# Two-stage resolve before we render ERB
88158
force_resolve_catalog_deferred!
159+
force_resolve_properties!
160+
89161
timeout = set_timeout
90162
Puppet.debug "Dsc Timeout: #{timeout} milliseconds"
91163
version = Facter.value(:powershell_version)
92164
Puppet.debug "PowerShell Version: #{version}"
165+
93166
script_content = ps_script_content('test')
94167
Puppet.debug "\n" + self.class.redact_content(script_content)
95168

96169
if Pwsh::Manager.windows_powershell_supported?
97170
output = ps_manager.execute(script_content, timeout)
98171
raise Puppet::Error, output[:errormessage] if output[:errormessage]&.match?(%r{PowerShell module timeout \(\d+ ms\) exceeded while executing})
99-
100172
output = output[:stdout]
101173
else
102174
self.class.upgrade_message
@@ -116,16 +188,20 @@ def exists?
116188

117189
def create
118190
Puppet.notice("DSC PROVIDER SENTINEL → #{__FILE__}")
191+
192+
# Two-stage resolve before we render ERB
119193
force_resolve_catalog_deferred!
194+
force_resolve_properties!
195+
120196
timeout = set_timeout
121197
Puppet.debug "Dsc Timeout: #{timeout} milliseconds"
198+
122199
script_content = ps_script_content('set')
123200
Puppet.debug "\n" + self.class.redact_content(script_content)
124201

125202
if Pwsh::Manager.windows_powershell_supported?
126203
output = ps_manager.execute(script_content, timeout)
127204
raise Puppet::Error, output[:errormessage] if output[:errormessage]&.match?(%r{PowerShell module timeout \(\d+ ms\) exceeded while executing})
128-
129205
output = output[:stdout]
130206
else
131207
self.class.upgrade_message
@@ -136,7 +212,6 @@ def create
136212
data = JSON.parse(output)
137213

138214
raise(data['errormessage']) unless data['errormessage'].empty?
139-
140215
notify_reboot_pending if data['rebootrequired'] == true
141216

142217
data
@@ -169,12 +244,7 @@ def self.escape_quotes(text)
169244
end
170245

171246
def self.redact_content(content)
172-
# Note that here we match after an equals to ensure we redact the value being passed, but not the key.
173-
# This means a redaction of a string not including '= ' before the string value will not redact.
174-
# Every secret unwrapped in this module will unwrap as "'secret' # PuppetSensitive" and, currently,
175-
# always inside a hash table to be passed along. This means we can (currently) expect the value to
176-
# always come after an equals sign.
177-
# Note that the line may include a semi-colon and/or a newline character after the sensitive unwrap.
247+
# NOTE: match after '=' so we redact the value being passed, but not the key
178248
content.gsub(%r{= '.+' # PuppetSensitive;?(\\n)?$}, "= '[REDACTED]'")
179249
end
180250

@@ -187,6 +257,10 @@ def self.ps_script_content(mode, resource, provider)
187257
@param_hash = resource
188258
template_name = resource.generic_dsc ? '/invoke_generic_dsc_resource.ps1.erb' : '/invoke_dsc_resource.ps1.erb'
189259
file = File.new(template_path + template_name, encoding: Encoding::UTF_8)
260+
261+
# Make vendored_modules_path visible in ERB if the template uses it
262+
vendored_modules_path = self.vendored_modules_path
263+
190264
template = ERB.new(file.read, trim_mode: '-')
191265
template.result(binding)
192266
end

0 commit comments

Comments
 (0)