I am excited for the potential of PowerShell Desired State Configuration. The ideal of pushing configurations with one click, making settings consistent across multiple machines is a sysadmins utopia. But getting there can be a challenge and somewhat of a steep learning curve.
This week’s obstacle was with the PowerShell DSC script resource. There is a lot of information available. This Microsoft document is a good place to start.
A little background before we go any further. I deploy servers using Azure Resource Manager Templates. These templates will create a server with any number of data drives, depending on what is available for the server size. I want to use DSC to pool all available data drives using Storage Spaces and create a usable disk drive from the pool after the deployment. The first step in the script was to create the storage pool. The Script Block I used to create the pool looked like this:
Script StoragePool { SetScript = { New-StoragePool -FriendlyName StoragePool1 -StorageSubSystemFriendlyName '*storage*' -PhysicalDisks (Get-PhysicalDisk –CanPool $True) } TestScript = { (Get-StoragePool -FriendlyName StoragePool1).OperationalStatus -eq 'OK' } GetScript = { @{Ensure = if ((Get-StoragePool -FriendlyName StoragePool1).OperationalStatus -eq 'OK') {'Present'} Else {'Absent'}} } }
Completed DSC Storage Spaces configuration post here
This script block has the three required sections, the TestScript that tests and returns a $true of $false, depending on if the setting exists. The SetScript sets the configuration if the TestScript returns $false, and the GetScript that returns the required Array. However, the DSC deployment kept giving me the errors below when it ran.
From the Portal:
The PowerShell DSC resource '[Script]StoragePool' with SourceInfo '::36::13::Script' threw one or more non-terminating errors while running the Test-TargetResource functionality. These errors are logged to the ETW channel called Microsoft-Windows-DSC/Operational. Refer to this channel for more details.
From the Desired State Configuration Operation log:
Event ID: 4252
MIResult: 1 Error Message: No MSFT_StoragePool objects found with property 'FriendlyName' equal to 'StoragePool1'. Verify the value of the property and retry. Message ID: CmdletizationQuery_NotFound_FriendlyName,Get-StoragePool Error Category: 13 Error Code: 13 Error Type: MI
Event ID: 4097
This event indicates that failure happens when LCM is processing the configuration. Error Id is 0x1. Error Detail is The SendConfigurationApply function did not succeed.. Resource Id is [Script]StoragePool and Source Info is ::36::13::Script. Error Message is The PowerShell DSC resource '[Script]StoragePool' with SourceInfo '::36::13::Script' threw one or more non-terminating errors while running the Test-TargetResource functionality. These errors are logged to the ETW channel called Microsoft-Windows-DSC/Operational. Refer to this channel for more details..
Event ID: 4103
This event indicates that a non-terminating error was thrown when DSCEngine was executing Test-TargetResource on MSFT_ScriptResource DSC resource. FullyQualifiedErrorId is CmdletizationQuery_NotFound_FriendlyName,Get-StoragePool. Error Message is No MSFT_StoragePool objects found with property 'FriendlyName' equal to 'StoragePool1'. Verify the value of the property and retry..
Initially, I thought this was an issue with the SetScript command setting up the storage pool. But looking at Event ID 4252, the Message ID indicates an issue with the Get-StoragePool command. This is the command the TestScript section runs, not the SetScript. So next, I ran the business part of the TestScirpt command using PowerShell on the test server to see what happens.
First, with the storage pool in place, the command returns True as I would expect
Okay, that works as expected. Now what happens if there is no storage pool
As you can see, it returns False but it also gives an error because there is no pool to check. Turns out that this error was causing the issue. The simple fix was to tell PowerShell to silently continue on error. Now the command just returns $false without the added error.
The updated script block below removes the errors and now functions as intended. Now back to finishing the configuration.
Script StoragePool { SetScript = { New-StoragePool -FriendlyName StoragePool1 -StorageSubSystemFriendlyName '*storage*' -PhysicalDisks (Get-PhysicalDisk –CanPool $True) } TestScript = { (Get-StoragePool -ErrorAction SilentlyContinue -FriendlyName StoragePool1).OperationalStatus -eq 'OK' } GetScript = { @{Ensure = if ((Get-StoragePool -FriendlyName StoragePool1).OperationalStatus -eq 'OK') {'Present'} Else {'Absent'}} } }