1+
12# frozen_string_literal: true
23
34require 'pathname'
45require 'json'
5- require_relative '../../../puppet_x/puppetlabs/dsc_lite/powershell_hash_formatter'
66require 'puppet/pops/evaluator/deferred_resolver'
77require 'puppet/node/facts'
8+ require_relative '../../../puppet_x/puppetlabs/dsc_lite/powershell_hash_formatter'
89
910Puppet ::Type . type ( :base_dsc_lite ) . provide ( :powershell ) do
1011 confine feature : :pwshlib
@@ -67,7 +68,9 @@ def ps_manager
6768 Pwsh ::Manager . instance ( command ( :powershell ) , Pwsh ::Manager . powershell_args , debug : debug_output )
6869 end
6970
70- # Returns true if obj (or any nested value) is a DeferredValue
71+ # ===== NEW: Deferred helpers (instance methods) =====
72+
73+ # Detects if a value (or nested) contains a DeferredValue
7174 def deep_contains_deferred? ( obj )
7275 case obj
7376 when Puppet ::Pops ::Evaluator ::DeferredValue
@@ -87,47 +90,57 @@ def deep_contains_deferred?(obj)
8790 end
8891 end
8992
90- # Resolve any lingering Deferred values in the `properties` parameter
91- def resolve_properties_deferreds!
93+ # Force resolution of Deferreds across the catalog before script generation.
94+ # This mirrors Puppet's own preprocessing when preprocess_deferred=true.
95+ def ensure_deferreds_resolved!
96+ # (A) Prefer compiler-based resolution for properties when available
9297 props_param = resource . parameters [ :properties ]
93- return unless props_param
94-
95- props_val = props_param . value
96- return unless deep_contains_deferred? ( props_val )
97-
98- # Prefer compiler-based resolution when available
99- compiler = Puppet . lookup ( :compiler ) { nil }
100- if compiler
101- resolved = Puppet :: Pops :: Evaluator :: DeferredResolver . resolve ( props_val , compiler )
102- resource [ :properties ] = resolved
103- return
98+ compiler = Puppet . lookup ( :compiler ) { nil }
99+
100+ if props_param
101+ pv = props_param . value
102+ if deep_contains_deferred? ( pv )
103+ Puppet . debug ( 'DSC_lite: Deferred detected in properties; resolving via compiler' ) if compiler
104+ if compiler
105+ resolved = Puppet :: Pops :: Evaluator :: DeferredResolver . resolve ( pv , compiler )
106+ resource [ :properties ] = resolved
107+ end
108+ end
104109 end
105110
106- # Fallback: resolve across the whole catalog in place (official API)
111+ # (B) Always run a full catalog resolution pass as a safety net.
107112 facts = Puppet ::Node ::Facts . new (
108113 ( resource . catalog . respond_to? ( :host ) && resource . catalog . host ) ||
109114 Facter . value ( :fqdn ) || Facter . value ( :hostname ) || 'localhost' ,
110115 Facter . to_hash ,
111116 )
112117 env = resource . catalog . respond_to? ( :environment_instance ) ? resource . catalog . environment_instance : nil
113118
119+ Puppet . debug ( 'DSC_lite: invoking DeferredResolver.resolve_and_replace on catalog' )
114120 Puppet ::Pops ::Evaluator ::DeferredResolver . resolve_and_replace ( facts , resource . catalog , env , true )
115121
116- # Refresh our local value after in-place resolution
117- updated = resource . parameters [ :properties ]
118- resource [ :properties ] = updated . value if updated
122+ # Post-check for diagnostics
123+ pp = resource . parameters [ :properties ]
124+ if pp && deep_contains_deferred? ( pp . value )
125+ Puppet . debug ( 'DSC_lite: WARNING — Deferred still present in properties after resolution pass' )
126+ else
127+ Puppet . debug ( 'DSC_lite: no Deferred values remain in properties after resolution' )
128+ end
119129 rescue => e
120- raise Puppet ::Error , "DSC_lite: failed to resolve Deferred values in `properties`: #{ e . class } : #{ e . message } "
130+ # Raise a targeted error; this is better than failing later with "unsupported type DeferredValue"
131+ raise Puppet ::Error , "DSC_lite: failed while resolving Deferred values: #{ e . class } : #{ e . message } "
121132 end
122133
134+ # ===== END NEW helpers =====
135+
123136 def exists?
124137 timeout = set_timeout
125138 Puppet . debug "Dsc Timeout: #{ timeout } milliseconds"
126139 version = Facter . value ( :powershell_version )
127140 Puppet . debug "PowerShell Version: #{ version } "
128141
129- # NEW: resolve any lingering DeferredValue in properties
130- resolve_properties_deferreds !
142+ # NEW: ensure any lingering Deferreds are resolved before building the script
143+ ensure_deferreds_resolved !
131144
132145 script_content = ps_script_content ( 'test' )
133146 Puppet . debug "\n " + self . class . redact_content ( script_content )
@@ -157,8 +170,8 @@ def create
157170 timeout = set_timeout
158171 Puppet . debug "Dsc Timeout: #{ timeout } milliseconds"
159172
160- # NEW: resolve any lingering DeferredValue in properties
161- resolve_properties_deferreds !
173+ # NEW: ensure any lingering Deferreds are resolved before building the script
174+ ensure_deferreds_resolved !
162175
163176 script_content = ps_script_content ( 'set' )
164177 Puppet . debug "\n " + self . class . redact_content ( script_content )
@@ -179,7 +192,6 @@ def create
179192 raise ( data [ 'errormessage' ] ) unless data [ 'errormessage' ] . empty?
180193
181194 notify_reboot_pending if data [ 'rebootrequired' ] == true
182-
183195 data
184196 end
185197
@@ -216,7 +228,7 @@ def self.redact_content(content)
216228 # always inside a hash table to be passed along. This means we can (currently) expect the value to
217229 # always come after an equals sign.
218230 # Note that the line may include a semi-colon and/or a newline character after the sensitive unwrap.
219- content . gsub ( %r{= '.+' # PuppetSensitive;?(\\ n)?$} , "= '[REDACTED]'" )
231+ content . gsub ( %r{= '.+' # PuppetSensitive;?(\n )?$} , "= '[REDACTED]'" )
220232 end
221233
222234 def ps_script_content ( mode )
0 commit comments