ValidateSet for a Parameter in a DSC Class Based Resource Fails to Throw Error

While working on a Custom DSC Resource that I started Monday night at the PowerShell Summit I came across some interesting behavior that turned out to be a bug in the WMF 5.0 February Preview. I have logged this issue in Connect, but I wanted to write a blog post to demonstrate what exactly is going on for when someone else runs into this issue. I am just going to the use Custom DSC Resource for creating a Primary DNS Zone that I was working on as the example to demonstrate the behavior.

Here are the properties for the resource. I figured I could just do ValidateSet like I always had done for an advanced function or non-class based DSC Resource.

    [DscProperty(Key)]
    [string]$ZoneName
    
    [DscProperty(Mandatory)]
    [Ensure] $Ensure

    [DscProperty()]
    [ValidateSet("Forest","Domain","Current","Legacy")]
    [string] $ReplicationScope

    [DscProperty(Mandatory)]
    [bool] $Aging

My Configuration for testing the Resource looks like this:

Configuration TestDNS{

    Import-DSCResource -ModuleName cDNSPrimaryZone
   
    node localhost{

        cDNSPrimaryZone TestZone{

            ZoneName = "Test"
            Ensure = "Present"
            ReplicationScope = "Domain"
            Aging = $True
        }

    }

}

TestDNS -OutputPath C:\Scripts\TestDNS
Start-DSCConfiguration -Wait -Force -Verbose -Path C:\Scripts\TestDNS

If I run this Configuration, with one of the appropriate values for ReplicationScope, it works exactly like you would expect it to.

Directory: C:\Scripts\TestDNS


Mode                LastWriteTime         Length Name                                                                                     
----                -------------         ------ ----                                                                                     
-a----        4/22/2015   4:08 AM           1746 localhost.mof                                                                            
VERBOSE: Perform operation 'Invoke CimMethod' with following parameters, ''methodName' = SendConfigurationApply,'className' = MSFT_DSCLocal
ConfigurationManager,'namespaceName' = root/Microsoft/Windows/DesiredStateConfiguration'.
VERBOSE: An LCM method call arrived from computer DC01 with user sid S-1-5-21-3991266330-1997624308-690668430-500.
VERBOSE: [DC01]: LCM:  [ Start  Set      ]
VERBOSE: [DC01]:                            [DSCEngine] Importing the module C:\Program Files\WindowsPowerShell\Modules\cDNSPrimaryZone\
cDNSPrimaryZone.psd1 in force mode.
VERBOSE: [DC01]: LCM:  [ Start  Resource ]  [[cDNSPrimaryZone]TestZone]
VERBOSE: [DC01]: LCM:  [ Start  Test     ]  [[cDNSPrimaryZone]TestZone]
VERBOSE: [DC01]:                            [[cDNSPrimaryZone]TestZone] Importing the module cDNSPrimaryZone in force mode.
VERBOSE: [DC01]:                            [[cDNSPrimaryZone]TestZone] Checking to see if the Zone Test exists
VERBOSE: [DC01]:                            [[cDNSPrimaryZone]TestZone] Zone Test does not exist
VERBOSE: [DC01]: LCM:  [ End    Test     ]  [[cDNSPrimaryZone]TestZone]  in 1.5350 seconds.
VERBOSE: [DC01]: LCM:  [ Start  Set      ]  [[cDNSPrimaryZone]TestZone]
VERBOSE: [DC01]:                            [[cDNSPrimaryZone]TestZone] Importing the module cDNSPrimaryZone in force mode.
VERBOSE: [DC01]:                            [[cDNSPrimaryZone]TestZone] Checking to see if the Zone Test exists
VERBOSE: [DC01]:                            [[cDNSPrimaryZone]TestZone] Adding DNS Server Zone Test
VERBOSE: [DC01]:                            [[cDNSPrimaryZone]TestZone] AllowUpdate successfully set on server DC01.
VERBOSE: [DC01]:                            [[cDNSPrimaryZone]TestZone] Aging successfully set on server DC01.
VERBOSE: [DC01]: LCM:  [ End    Set      ]  [[cDNSPrimaryZone]TestZone]  in 0.7220 seconds.
VERBOSE: [DC01]: LCM:  [ End    Resource ]  [[cDNSPrimaryZone]TestZone]
VERBOSE: [DC01]: LCM:  [ End    Set      ]    in  3.8620 seconds.
VERBOSE: Operation 'Invoke CimMethod' complete.
VERBOSE: Time taken for configuration job to complete is 3.932 seconds

That’s great. But, what happens if I put in a value that isn’t part of Validate Set?

Configuration TestDNS{

    Import-DSCResource -ModuleName cDNSPrimaryZone
   
    node localhost{

        cDNSPrimaryZone TestZone{

            ZoneName = "Test"
            Ensure = "Present"
            ReplicationScope = "HokeyPokey"
            Aging = $True
        }

    }

}

TestDNS -OutputPath C:\Scripts\TestDNS
Start-DSCConfiguration -Wait -Force -Verbose -Path C:\Scripts\TestDNS

Directory: C:\Scripts\TestDNS


Mode                LastWriteTime         Length Name                                                                                     
----                -------------         ------ ----                                                                                     
-a----        4/22/2015   4:13 AM           1754 localhost.mof                                                                            
VERBOSE: Perform operation 'Invoke CimMethod' with following parameters, ''methodName' = SendConfigurationApply,'className' = MSFT_DSCLocal
ConfigurationManager,'namespaceName' = root/Microsoft/Windows/DesiredStateConfiguration'.
VERBOSE: An LCM method call arrived from computer DC01 with user sid S-1-5-21-3991266330-1997624308-690668430-500.
VERBOSE: [DC01]: LCM:  [ Start  Set      ]
VERBOSE: [DC01]:                            [DSCEngine] Importing the module C:\Program Files\WindowsPowerShell\Modules\cDNSPrimaryZone\
cDNSPrimaryZone.psd1 in force mode.
VERBOSE: [DC01]: LCM:  [ Start  Resource ]  [[cDNSPrimaryZone]TestZone]
VERBOSE: [DC01]: LCM:  [ Start  Test     ]  [[cDNSPrimaryZone]TestZone]
VERBOSE: [DC01]:                            [[cDNSPrimaryZone]TestZone] Importing the module cDNSPrimaryZone in force mode.
VERBOSE: [DC01]: LCM:  [ End    Test     ]  [[cDNSPrimaryZone]TestZone]  in 3.2480 seconds.
VERBOSE: [DC01]: LCM:  [ Start  Set      ]  [[cDNSPrimaryZone]TestZone]
VERBOSE: [DC01]:                            [[cDNSPrimaryZone]TestZone] Importing the module cDNSPrimaryZone in force mode.
VERBOSE: [DC01]: LCM:  [ End    Set      ]  [[cDNSPrimaryZone]TestZone]  in 3.0840 seconds.
VERBOSE: [DC01]: LCM:  [ End    Resource ]  [[cDNSPrimaryZone]TestZone]
VERBOSE: [DC01]: LCM:  [ End    Set      ]    in  14.0760 seconds.
VERBOSE: Operation 'Invoke CimMethod' complete.
VERBOSE: Time taken for configuration job to complete is 19.845 seconds

That is clearly not what should happen. You would expect to see an error saying something to effect of “HokeyPokey does not belong to the set “Domain”,”Forest”,”Current”,”Legacy”, it needs to be one of those values”.

If we look at the .MOF file that gets created, this incorrect value also makes it into the .MOF file:

instance of cDNSPrimaryZone as $cDNSPrimaryZone1ref
{
ResourceID = "[cDNSPrimaryZone]TestZone";
 Aging = True;
 Ensure = "Present";
 ZoneName = "Test";
 SourceInfo = "C:\\Program Files\\WindowsPowerShell\\Modules\\cDNSPrimaryZone\\Examples\\DNSTest.ps1::7::9::cDNSPrimaryZone";
 ModuleName = "cDNSPrimaryZone";
 ReplicationScope = "HokeyPokey";
 ModuleVersion = "1.0";

 ConfigurationName = "TestDNS";

};

You can tell that it knows something is wrong, because when it runs through Test-TargetResource and Set-TargetResource it doesn’t actually do anything (notice all the Verbose messages that are missing from the previous example), but it also doesn’t error.

So how do we get around this? By using an Enum!

enum ReplicationScope
{
    Domain
    Forest
    Current
    Legacy
}

enum Ensure 
{ 
    Absent 
    Present 
}

[DscResource()]
class cDNSPrimaryZone{

    [DscProperty(Key)]
    [string]$ZoneName
    
    [DscProperty(Mandatory)]
    [Ensure] $Ensure

    [DscProperty(Mandatory)]    
    [ReplicationScope] $ReplicationScope

    [DscProperty(Mandatory)]
    [bool] $Aging

Now, if I try to set the ReplicationScope to HokeyPokey, we get the behavior we would expect.

cDNSPrimaryZone\cDNSPrimaryZone : At least one of the values 'HokeyPokey' is not supported or valid for property 'ReplicationScope' on 
class 'cDNSPrimaryZone'. Please specify only supported values: 
Domain, Forest, Current, Legacy.
At C:\Program Files\WindowsPowerShell\Modules\cDNSPrimaryZone\Examples\DNSTest.ps1:7 char:9
+         cDNSPrimaryZone TestZone{
+         ~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [Write-Error], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : UnsupportedValueForProperty,cDNSPrimaryZone\cDNSPrimaryZone
 
Errors occurred while processing configuration 'TestDNS'.
At C:\Windows\system32\WindowsPowerShell\v1.0\Modules\PSDesiredStateConfiguration\PSDesiredStateConfiguration.psm1:3189 char:5
+     throw $errorRecord
+     ~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (TestDNS:String) [], InvalidOperationException
    + FullyQualifiedErrorId : FailToProcessConfiguration

Leave a Reply