Authoring Puppet manifests using the new puppetized DSC Resources is, like using any other Puppet modules, a few steps and a deploy away for most use cases. Sometimes you will run into a problem trying to apply a manifest containing one or more Puppetized DSC resources. What could be causing the problem?
Basic troubleshooting
For basic troubleshooting, start with our basic troubleshooting guide for Puppet.Dsc
. The guide covers finding problems like type errors or incorrect enums in the module builder, invocation errors during a Puppet run, and problems with the translation layer in pwslib
.
The guide also covers running Puppet in debug mode, which you will need to know about for the next couple sections. In short, if you append the --debug
flag to the end of your Puppet invocation, you’ll get a ton of additional information out of the run.
Advanced troubleshooting
So, you have a Puppet manifest with one or more puppetized DSC Resource declarations in it. Things are not working and the basic troubleshooting guide didn’t solve the problem.
The first place to start is to validate that what you declared in the manifest was translated correctly to the PowerShell DSC code to execute.
Inspect the debug logs
When you specify a DSC Resource in your Puppet manifest, behind the scenes Puppet converts that into PowerShell code to pass to Invoke-DscResource
. In debug mode, you can actually see what Puppet thinks you passed and that generated code.
Lets walk through an example manifest:
dsc_psrepository { 'PowerShell Gallery':
dsc_name => 'psgAllery',
dsc_installationpolicy => 'Trusted',
}
If we execute Puppet in debug mode, we’ll get the following (abbreviated) output:
Debug: dsc_psrepository[{:name=>"PowerShell Gallery", :dsc_name=>"psgAllery"}]: Updating: Invoking Set Method for '{:name=>"PowerShell Gallery", :dsc_name=>"psgAllery"}' with {:name=>"PowerShell Gallery", :dsc_name=>"psgAllery", :dsc_installationpolicy=>"Trusted"}
... (snipped for brevity)
dsc_psrepository[{:name=>"PowerShell Gallery", :dsc_name=>"psgAllery"}]: Updating: Script:
... (snipped for brevity)
$InvokeParams = @{Name = 'PSRepository'; Method = 'set'; Property = @{name = 'psgAllery'; installationpolicy = 'Trusted'}; ModuleName = @{ModuleName = 'C:/pathToPuppetModules/powershellget/lib/puppet_x/powershellget/dsc_resources/PowerShellGet/PowerShellGet.psd1'; RequiredVersion = '2.2.4.1'}} Try {
$Result = Invoke-DscResource @InvokeParams
} catch {
$Response.errormessage = $_.Exception.Message
return ($Response | ConvertTo-Json -Compress)
}
installationpolicy
to Trusted
. Debug: dsc_psrepository[{:name=>"PowerShell Gallery", :dsc_name=>"psgAllery"}]: Updating: Invoking Set Method for '{:name=>"PowerShell Gallery", :dsc_name=>"psgAllery"}' with {:name=>"PowerShell Gallery", :dsc_name=>"psgAllery", :dsc_installationpolicy=>"Trusted"}
This can be more easily read as as Ruby hash:{
:name=>"PowerShell Gallery",
:dsc_name=>"psgAllery",
:dsc_installationpolicy=>"Trusted"
}
The next part in the log to look at is the invocation parameters. This is the PowerShell code generated from the section we just examined:$InvokeParams = @{Name = 'PSRepository'; Method = 'set'; Property = @{name = 'psgAllery'; installationpolicy = 'Trusted'}; ModuleName = @{ModuleName = 'C:/pathToPuppetModules/powershellget/lib/puppet_x/powershellget/dsc_resources/PowerShellGet/PowerShellGet.psd1'; RequiredVersion = '2.2.4.1'}}
Translating that to a PowerShell hash, we can see a similar representation of the Ruby hash we just looked at. This is the exact code that will be passed to Invoke-DscResource
, expanded from one line for clarity. Note that your ModuleName
path will be system-specific:
$InvokeParams = @{
Name = 'PSRepository';
Method = 'get';
Property = @{ name = 'psgAllery' };
ModuleName = @{
ModuleName = 'C:/pathToPuppetModules/powershellget/lib/puppet_x/powershellget/dsc_resources/PowerShellGet/PowerShellGet.psd1';
RequiredVersion = '2.2.4.1'
}
}
In summary, the debug logs from a Puppet execution run provide us detailed information about how your Puppet manifest was translated into PowerShell code to invoke the DSC Resource.
Now we can use this information to invoke the DSC Resource outside of Puppet to debug it.
Invoke without Puppet
After inspecting the debug Puppet logs, we now have the PowerShell code Puppet was executing. We can try that code out on a test machine and walk through the execution ourselves interactively.
In a PowerShell console with administrator permissions, you’ll copy and paste the $InvokeParams
line from the debug log we pulled out above. Then you’ll invoke it using Invoke-DscRsource
. For example:
PS C:\> $InvokeParams = @{
>> Name = 'PSRepository';
>> Method = 'get';
>> Property = @{ name = 'psgAllery' };
>> ModuleName = @{
>> ModuleName = 'C:/pathToPuppetModules/powershellget/lib/puppet_x/powershellget/dsc_resources/PowerShellGet/PowerShellGet.psd1';
>> RequiredVersion = '2.2.4.1'
>> }
>> }
PS C:\> Invoke-DscResource @InvokeParams -Verbose
Now you can evaluate the success or failure of the invocation without involving Puppet at all. This way you can determine whether the values being passed were incorrect or if the DSC Resource itself caused the issue.
If the code fails after pasting in the $InvokeParams
block, then there is something wrong with the values or the hash statement. If something has gone wrong converting the values in your Puppet manifest to the InvokeParams
hash being passed to Invoke-DscResource
, please do file an issue with us and we’ll be sure to take a look.
If the code fails after invoking Invoke-DscResource
, then there is likely a problem inside the DSC Resource itself. If it’s an issue with the DSC Resource, hopefully you’ll have enough information from the execution to put together a bug report for the upstream PowerShell module and work with that DSC Resource’s authors for a resolution.
Invoke DSC Resources with PSCredentials
When your manifest specifies a PSCredential, you’ll see one or more lines above the InvokeParams
declaration, like this (expanded for readability):
$febabd4f_19e8_4ed6_a218_188e275ecc05 = New-PSCredential -User apple -Password '#<Sensitive [value redacted]>'
$InvokeParams = @{
Name = 'PSRepository';
Method = 'set';
Property = @{
name = 'psgAllery';
installationpolicy = 'Trusted';
psdscrunascredential = $febabd4f_19e8_4ed6_a218_188e275ecc05
};
ModuleName = @{
ModuleName = 'C:/pathToPuppetModules/powershellget/lib/puppet_x/powershellget/dsc_resources/PowerShellGet/PowerShellGet.psd1';
RequiredVersion = '2.2.5'
}
}
Note that it is building a credential object from plain text before passing that object to DSC; the debug logs will strip the password from being returned. In this case, you will need some of the custom helper code that we snipped for brevity earlier, namely this function:
function new-pscredential {
[CmdletBinding()]
param (
[parameter(Mandatory = $true,
ValueFromPipelineByPropertyName = $true)]
[string]
$user,
[parameter(Mandatory = $true,
ValueFromPipelineByPropertyName = $true)]
[string]
$password
)
$secpasswd = ConvertTo-SecureString $password -AsPlainText -Force
$credentials = New-Object System.Management.Automation.PSCredential ($user, $secpasswd)
return $credentials
}
Make sure to copy that code into your terminal before you copy in the invocation variables and remember to replace #<Sensitive [value redacted]>
with the appropriate password string. Alternatively, you could use Get-Credential
and capture the output of that command to the same variable name from your debug log.
Invoke DSC Resources with CIM instances
Some DSC Resources use nested CIM instances to pass hashes as property values. In these cases, there’s one or more lines above the definition of the InvokeParams
variable which creates these CIM instances.
$b615d113_bb6e_49e4_9776_7e895dfe20c7 = New-CimInstance -ClientOnly -ClassName 'MSFT_KeyValuePair' -Property @{'key' = 'Accept-Language' ; 'value' = 'en-US'}
$InvokeParams = @{
Name = 'xRemoteFile';
Method = 'set';
Property = @{
destinationpath = 'C:\dsc-xpsdesiredstateconfiguration-9.1.0-0-1.tar.gz';
headers = [CimInstance[]]@($b615d113_bb6e_49e4_9776_7e895dfe20c7);
uri = 'https://forge.puppet.com/v3/files/dsc-xpsdesiredstateconfiguration-9.1.0-0-1.tar.gz'
};
ModuleName = @{
ModuleName = 'C:/pathToPuppetModules/xpsdesiredstateconfiguration/lib/puppet_x/xpsdc/dsc_resources/xPSDesiredStateConfiguration/xPSDesiredStateConfiguration.psd1';
RequiredVersion = '9.1.0'
}
}
This is necessary for DSC to correctly handle the objects, especially if they have a custom CIM instance class specific to the DSC Resource being called.
Need to continue debugging? Dig deeper with another advanced troubleshooting tool: Pry.
Comments
0 comments
Please sign in to leave a comment.