My first DSC class resource - Some questions

This topic contains 10 replies, has 2 voices, and was last updated by  Zuldan 1 year, 4 months ago.

  • Author
  • #51399


    So I've just written my first DSC class resource (skipped cmdlet based resources). I'm hoping someone can give me some guidance on a couple of questions.

    Question 1: When testing to see if something has been set correctly in Test() and using an optional property I'm checking to see if the property was defined before making the test. Is there a better way to implement this?

    if ($this.VendorClass) {
        if ($OptionDefinition.VendorClass -ne $this.VendorClass)
            $return = $False

    Question 2: I'm using the DHCPServer module in the resource, when Test() runs is spams out a TON of verbose from DHCPServer (basically it's doing a Import-Module DHCPServer -verbose in the background). How do I stop all this verbose traffic? When I see other DSC resource use modules I don't see verbose from the modules they use.

    Question 3: Get() is supposed to return an instance of the class. Which of the following methods for doing that is correct?

    Method 1

    $MyInstance = [MyInstance]::new()
    $MyInstance.Name = 'blah'
    return $MyInstance

    Method 2 (MSFT use this method

    return $this

    Question 4: When the LCM runs the config 'Start Set' runs first with no information. Is that correct?

    VERBOSE: Perform operation 'Invoke CimMethod' with following parameters, ''methodName' = SendConfigurationApply,'className' = MSFT_DSCLocalConfigurationManager,'namespaceName' = root/Microsoft/Windows/DesiredStateConfiguration'.
    VERBOSE: An LCM method call arrived from computer LABSERVER01 with user sid S-1-5-21-2199847848-3872042683-2133798733-1134.
    VERBOSE: [S12AUMDC01TST05]: LCM:  [ Start  Set      ]
    VERBOSE: [S12AUMDC01TST05]:                            [DSCEngine] Importing the module C:\Program Files\WindowsPowerShell\Modules\MyInstance\MyInstance.psd1 in force mode.
    VERBOSE: [S12AUMDC01TST05]: LCM:  [ Start  Resource ]  [[MyInstance]Test1]
    VERBOSE: [S12AUMDC01TST05]: LCM:  [ Start  Test     ] 
    • This topic was modified 1 year, 4 months ago by  Zuldan.
    • This topic was modified 1 year, 4 months ago by  Zuldan.
    • This topic was modified 1 year, 4 months ago by  Zuldan.
    • This topic was modified 1 year, 4 months ago by  Zuldan.
  • #51424

    Luke Griffith

    Question 1:
    From what I understand, the general design principals around class based resources are to use them as an interface to a module that would do the heavy lifting, keeping the method code light and the implementation abstracted (This also aids unit testing of your implementation). You might want to think about moving some of the logic to cmdlets opposed to method code, potentially building up a pipeline that either start with $this, or a cmdlet that actually obtains the Vendor Class


    Test() {
    	$TestResult = Get-VendorClass | Test-VendorClass
    	return $TestResult

    Question 2:
    Is the module a listed as a RequiredModule in the ModuleManifest? Potentially when your running the cmdlets in the resource its doing the import, as its not already been imported at a higher scope.

    Question 3:
    Option 2 definitely, the Get method is already working on an instantiated object (its self) – option 1 would mean creating an entire new object.

    Question 4:
    I'd need to do some testing, but I believe that Start Set is the start of a configuration run, the resource is starting at LCM [ Start Resource ], then the test executes as you would expect first.

    • This reply was modified 1 year, 4 months ago by  Luke Griffith.
  • #51599


    Hi Luke, thank you so much for your response. Here are my comments...

    Question 1: Thanks for the heads up.

    Question 2: I managed to resolve this by making the DHCPServer module load before the class with -Verbose:$False. I'll try RequiredModule as you suggested, tomorrow.

    Question 3: I actually found method 2 breaks DSC because $this holds what your desired state should be. For instance if you wanted to see if the desired state is Ensure, you would check $This.Ensure. If you're modifying $this in Test() then your changing what the intended desired state should be and so when you check $this in Set() and Test() the logic goes out the door because the intended desired state has changed during runtime. Ideally you want to leave $this alone because it's the only place that holds what your desired state should be, unless I'm mistaken? $this acts as a global variable and it should be treated like one. Would love to hear your thoughts...

    • #51660

      Luke Griffith

      Hi Zuldan,

      The write properties on $this are indeed what you expect the desired state to be so changing this could definitely cause problems if there is some odd code going on, but personally I don't believe this situation would ever occur and if it does, personally I believe the framework is being bent into a way it shouldn't because it wouldn't make sense.

      From what I've observed no information in the Test that is set on $this is ever passed to the next stage because the Set (and similarly the Get) execute in their own runspace entirely separate. No $global scope variable would be able to get to the next stage and no information can be passed within powershell without deserialising it to disk or queue. There are technical reasons for this – The LCM allow Set / Test to decrypt PSCredential objects where as the Get cant to avoid any credential objects leaking out by accident.

      Personally, i'd be interested in knowing why you're changing properties on $this in the test, as from my experience the write properties on $this are used to just check the state of the machine and determine if its compliant then return true or false to tell the engine if set should run – These methods are intended to have a very small chunk of responsibility so it can be kept simple.

  • #51710


    Hi Luke,

    Question 2: Your RequiredModule solution worked. Thank you!

    Question 4: My apologies. Setting $this in Test() was a typo. It was supposed to be Get(). When I set $this.Ensure in Get(), it changed $this.Ensure in Test(). I'll have to do some more testing and replicate it again.

    • #51720

      Luke Griffith

      Would be great if you can replicate it, I've been testing myself and have been unable to get the same behaviour using ps 5.1 on 2016tp.

      I'll try and get some traces up to show my examples.

  • #51726


    Could you also tell me if you can enter in debug session with a DSC class and 5.1. It appears to be broken.

    So if I run the following (when PS tells you too)...

    Enter-PSHostProcess -Id xxx -AppDomainName DscPsPluginWkr_AppDomain
    Debug-Runspace -Id x bombs out on Debug-Runspace (can't remember the error message). It works perfectly in 5.0 though.

    If you are arent able to debug DSC classes either then I'll create a UserVoice.

    • This reply was modified 1 year, 4 months ago by  Zuldan.
  • #51736

    Luke Griffith

    Have no problem debugging on 5.1, see the below gist file list that has me debugging on the same version – I have a number of the transcripts I've worked with for my examples. Part to take away is the Caller, Get and Test debugging transcripts. In the GET i changed the Ensure property to Absent, and its returned to the Caller showing Absent as it was changed in that runspace, yet when I ran and debugged the test with Test-DscConfiguration following that the property is back to Present. This is due to the way the DSC engine uses MOF's, and will only use the stored serialized data from the mof as parameters to start the Get/Set/Test.

    Unsure why your seeing an error entering, whats the exception you're seeing?

  • #51737

    Luke Griffith

    Accidental double post 🙁

    • This reply was modified 1 year, 4 months ago by  Luke Griffith. Reason: double post
  • #51741


    Luke that's making sense now. Thanks for the transcript. Going to update my code tomorrow. Maybe I was doing something weird and thought Ensure had changed.

    As for the debugging. I'm using 5.1 (upgraded from 5.0) on 2012 R2 so maybe it's only broken on that OS? Unless it's just broken on my environment (which is what I'm leaning towards). Going to give it another crack tomorrow.

  • #52188


    Hi Luke, I updated my code so that Get() returns a modified $this and the class completely broke (including the Pester tests) so I've reverted back to creating my own instance of the class and returning that instead.

    I tried to find some other real world examples of someones code returning $this in Get() but only found people creating their own instance as well. See below; Now I'm totally confused.

    As for debugging on 5.1, it works for the 'most' part. However, after 10 to 15 debugging sessions (on the same class) and with WmiPrvSE.exe reaching over 110,000K in memory (which isn't very much but I guess it starts at around 10,000K), I get the following error and cannot debug any further. Once I kill the WmiPrvSE.exe process I can then start debugging again. I think I might report it on UserVoice.

    Value cannot be null.
    Parameter name: No Runspace was found.
        + CategoryInfo          : InvalidOperation: (Microsoft.Power...RunspaceCommand:DebugRunspaceCommand) [Debug-Runspace], PSArgumentNullException
        + FullyQualifiedErrorId : DebugRunspaceNoRunspaceFound,Microsoft.PowerShell.Commands.DebugRunspaceCommand

You must be logged in to reply to this topic.