Resource Designer Tool – A walkthrough writing a DSC resource

Resource Designer Tool – A walkthrough writing a DSC resource

Rate This
  • Comments 8

At the heart of Windows PowerShell Desired State Configuration are the resources. It is the resources which act behind the scenes for DSC to achieve its “make it so” philosophy. DSC ships with a number of resources in-box and you can take a look here for the complete list. However, once you get started with DSC, you may want to write your own resources (which are PowerShell modules) for your particular use case. This post will walk you through creating a custom DSC resource. You can take a look here to get an understanding of the schema/packaging of DSC resources. Attached with this post is the Resource Designer Tool which makes writing resources a breeze.

There are certain rules which must be complied with when writing a DSC resource. I will state the rules towards the end of the post for reference and completeness. The Resource Designer Tool takes care of the nitty-gritty so that you can concentrate on implementing the features in your resource without having to get caught up in figuring out the correct MOF syntax or worrying about missing some important requirements in the resources (which is a PowerShell module) file.

Let us take an example. Say you want to create a resource for modelling an Active Directory User. (The User resource shipped in-box is for local users only.) In this example, we will have the following configuration properties in our schema MOF:

  • UserName -> This will be the key property for our resource to uniquely identify a user. This is a required property
  • Ensure -> This can only take two values: ‘Present’ and ‘Absent’ for stating whether we want the user account to be present or absent.
  • DomainAdminCredential -> This will hold the domain account password for the user.
  • Password -> This will hold the new password in case we wish to change an existing password.

Once we have the properties all set out, we can begin using the Resource Designer Tool. For this, create a folder named ‘DSCPack_ResourceDesigner’ anywhere inside your $env:PSModulePath, download the attachments and place them inside the folder you just created. After this, import the module (using ‘Import-Module xDSCResourceDesigner’). We are all set. Let’s dive in now!

Let us see all the commands which we have as part of the designer tool:

PS C:\> (Get-Module xDSCResourceDesigner).ExportedCommands

 

Key                                                      Value

---                                                      -----

Import-xDscSchema                                        Import-xDscSchema

New-xDscResource                                         New-xDscResource

New-xDscResourceProperty                                 New-xDscResourceProperty

Test-xDscResource                                        Test-xDscResource

Test-xDscSchema                                          Test-xDscSchema

Update-xDscResource                                      Update-xDscResource

 

The first cmdlet we will be using is New-xDscResourceProperty. Let us explore it.

PS C:\> help New-xDscResourceProperty

 

NAME

    New-xDscResourceProperty

   

SYNOPSIS

    Creates a DscResourceProperty to be used by New-xDscResource.

   

   

SYNTAX

    New-xDscResourceProperty [-Name] <String> [-Type] <String> [-Attribute] {Key | Required | Read

    | Write} [-ValidateSet <String[]>] [-Description <String>] [<CommonParameters>]

   

   

DESCRIPTION

    Takes all of the given arguments and constructs a DscResourceProperty object to be used by New-xDscResource.

   

 

RELATED LINKS

 

REMARKS

    To see the examples, type: "get-help New-xDscResourceProperty -examples".

    For more information, type: "get-help New-xDscResourceProperty -detailed".

    For technical information, type: "get-help New-xDscResourceProperty -full".

 

For every configuration property we want, we need to define it using this cmdlet. Let us see how we would define the various properties we stated earlier:

UserName:

PS C:\> $UserName = New-xDscResourceProperty -Name UserName -Type String -Attribute Key

 

Ensure:

PS C:\> $Ensure = New-xDscResourceProperty -Name Ensure -Type String -Attribute Write -ValidateSet "Present", "Absent" 

 

DomainCredential:

PS C:\> $DomainCredential = New-xDscResourceProperty -Name DomainAdminCredential -Type PSCredential -Attribute Write

 

Password:

PS C:\> $Password = New-xDscResourceProperty -Name Password -Type PSCredential -Attribute Write

 

Now that the properties are defined, let us see how to generate the PowerShell module that will contain the implementation logic of the resource and the schema MOF file which will be consumed by DSC. The cmdlet we are looking for is New-xDscResource.

PS C:\> help New-xDscResource

 

NAME

    New-xDscResource

   

SYNOPSIS

    Creates a DscResource based on the given arguments.

   

   

SYNTAX

    New-xDscResource [-Name] <String> [-Property] <DscResourceProperty[]> [[-Path] <String>]

    [[-ModuleName] <String>] [-ClassVersion <Version>] [-FriendlyName <String>] [-Force]

    [-WhatIf] [-Confirm] [<CommonParameters>]

   

   

DESCRIPTION

    Creates a .psd1, .psm1, and .schema.mof file representing a Dsc Resource based on the properties and values passed in.

   

 

RELATED LINKS

 

REMARKS

    To see the examples, type: "get-help New-xDscResource -examples".

    For more information, type: "get-help New-xDscResource -detailed".

    For technical information, type: "get-help New-xDscResource -full".

 

 

Let us use the properties which we just defined to create the DSC resource.

PS C:\> New-xDscResource -Name Demo_ADUser -Property $UserName, $Ensure, $DomainCredential, $Password -Path 'C:\Program Files\WindowsPowerShell\Modules' -ModuleName Demo_DSCModule 

 

 

Here is the output we get:

   Directory: C:\Program Files\WindowsPowerShell\Modules

 

 

Mode                LastWriteTime     Length Name

----                -------------     ------ ----

d----        11/13/2013   3:42 PM            Demo_DSCModule

 

 

    Directory: C:\Program Files\WindowsPowerShell\Modules\Demo_DSCModule

 

 

Mode                LastWriteTime     Length Name

----                -------------     ------ ----

d----        11/13/2013   3:42 PM            DSCResources

 

 

    Directory: C:\Program Files\WindowsPowerShell\Modules\Demo_DSCModule\DSCResources

 

 

Mode                LastWriteTime     Length Name

----                -------------     ------ ----

d----         11/13/2013   3:42 PM           Demo_ADUser                                    

 

 

 

Looks like something exciting is going on here. A directory for our custom DSC resource has been created. Let us peek into what is in there.

 

PS C:\> cd 'C:\Program Files\WindowsPowerShell\Modules\Demo_DSCModule\DSCResources\Demo_ADUser'

 

PS C:\Program Files\WindowsPowerShell\Modules\Demo_DSCModule\DSCResources\Demo_ADUser> dir

 

 

    Directory: C:\Program Files\WindowsPowerShell\Modules\Demo_DSCModule\DSCResources\Demo_ADUser

 

 

Mode                LastWriteTime     Length Name

----                -------------     ------ ----

-a---        11/13/2013   3:42 PM       3770 Demo_ADUser.psm1

-a---        11/13/2013   3:42 PM        700 Demo_ADUser.schema.mof

 

 

Let us take a look at the contents of the schema MOF:

 

[ClassVersion("1.0.0.0"), FriendlyName("Demo_ADUser")]

class Demo_ADUser : OMI_BaseResource

{

       [Key] String UserName;

       [Write, ValueMap{"Present","Absent"}, Values{"Present","Absent"}] String Ensure;

       [Write, EmbeddedInstance("MSFT_Credential")] String DomainAdminCredential;

       [Write, EmbeddedInstance("MSFT_Credential")] String Password;

};

 

The designer tool took care of the syntax of the MOF file for us. One of the nice things I liked about the tool when I first started using it was that it took care of deriving the class from ‘OMI_BaseResource’. I would always forget about this. Also take note of how it converted ‘ValidateSet’ – a term well known to PowerShell scripters into ‘ValueMap’ – a term specific to CIM schema language specification. Similarly it knows to convert ‘PSCredential’ into the type ‘MSFT_Credential’ which is understood by the DSC Engine.

Next, let us see what the psm1 file looks like (As explained here, this file contains the three ‘*-Target-Resource’ functions):

function Get-TargetResource

{

       [CmdletBinding()]

       [OutputType([System.Collections.Hashtable])]

       param

       (

              [parameter(Mandatory = $true)]

              [System.String]

              $UserName

       )

 

       #Write-Verbose "Use this cmdlet to deliver information about command processing."

 

       #Write-Debug "Use this cmdlet to write debug information while troubleshooting."

 

 

       <#

       $returnValue = @{

              UserName = [System.String]

              Ensure = [System.String]

              DomainAdminCredential = [System.Management.Automation.PSCredential]

              Password = [System.Management.Automation.PSCredential]

       }

 

       $returnValue

       #>

}

 

 

function Set-TargetResource

{

       [CmdletBinding()]

       param

       (

              [parameter(Mandatory = $true)]

              [System.String]

              $UserName,

 

              [ValidateSet("Present","Absent")]

              [System.String]

              $Ensure,

 

              [System.Management.Automation.PSCredential]

              $DomainAdminCredential,

 

              [System.Management.Automation.PSCredential]

              $Password

       )

 

       #Write-Verbose "Use this cmdlet to deliver information about command processing."

 

       #Write-Debug "Use this cmdlet to write debug information while troubleshooting."

 

       #Include this line if the resource requires a system reboot.

       #$global:DSCMachineStatus = 1

 

 

}

 

 

function Test-TargetResource

{

       [CmdletBinding()]

       [OutputType([System.Boolean])]

       param

       (

              [parameter(Mandatory = $true)]

              [System.String]

              $UserName,

 

              [ValidateSet("Present","Absent")]

              [System.String]

              $Ensure,

 

              [System.Management.Automation.PSCredential]

              $DomainAdminCredential,

 

              [System.Management.Automation.PSCredential]

              $Password

       )

 

       #Write-Verbose "Use this cmdlet to deliver information about command processing."

 

       #Write-Debug "Use this cmdlet to write debug information while troubleshooting."

 

 

       <#

       $result = [System.Boolean]

      

       $result

       #>

}

 

 

Export-ModuleMember -Function *-TargetResource

 

 

This is the skeleton of the PowerShell Module generated by the tool. It contains the three functions that all DSC resources must define: Get-TargetResource, Test-TargetResource and Set-TargetResource. It also adds in some comments to help us with writing the resource. For example, take a look at the Test-TargetResource function. Since DSC is idempotent, we can apply the same configuration multiple times and if the current state is the same as the desired state, no action is taken. This is achieved by the DSC engine calling Set-TargetResource only if Test-TargetResource returns false. The tool hints at this by pointing out that Test-TargetResource should return a Boolean value. Also take note that the Get-TargetResource function contains the key property as its only parameter.

Given the skeleton of the generated module, we can proceed with adding our logic. After we are done writing our resource, we need to import our new DSC module. Then we can see that it is one of the DSC resources added to the existing set:

PS C:\> Import-Module Demo_DSCModule

PS C:\> Get-DscResource 'Demo_ADUser'

 

ImplementedAs   Name                      Module                         Properties

-------------   ----                      ------                         ----------

PowerShell      Demo_ADUser               Demo_DSCModule                 {UserName, …

 

 

At this point we can start writing our DSC configuration scripts using our new resource.

All this done, you realize you also need to add one more property which would be a hash table mapping a particular user to his last log in time.  Oh, no do you need to re write the entire resource again? No! We got you covered:

S C:\> help Update-xDscResource

 

NAME

    Update-xDscResource

   

SYNOPSIS

    Update an existing DscResource based on the given arguments.

   

   

SYNTAX

    Update-DscResource [-Name] <String> [-Property] <DscResourceProperty[]> [-ClassVersion

    <Version>] [-Force] [-WhatIf] [-Confirm] [<CommonParameters>]

    Update-DscResource [-Path] <String> [-Property] <DscResourceProperty[]> [-ClassVersion

    <Version>] [-Force] [-WhatIf] [-Confirm] [<CommonParameters>]

   

 

DESCRIPTION

    Update the .psm1 and .schema.mof file representing a Dsc Resource based on the properties and values

    passed in.

   

 

RELATED LINKS

 

REMARKS

    To see the examples, type: "get-help Update-xDscResource -examples".

    For more information, type: "get-help Update-xDscResource -detailed".

    For technical information, type: "get-help Update-xDscResource -full".

 

So cool. Let us add the new property:

PS C:\> $lastLogOn = New-xDscResourceProperty -Name LastLogOn -Type Hashtable -Attribute Write -Description "For mapping users to their last log on time"

 

Let us now update our resource:

PS C:\> Update-xDscResource -Name 'Demo_ADUser' -Property $UserName, $Ensure, $DomainCredential, $Password, $lastLogOn -Force

 

The contents of ADUser.schema.mof have been updated:

[ClassVersion("1.0.0.0"), FriendlyName("")]

class ADUser : OMI_BaseResource

{

       [Key] String UserName;

       [Write, ValueMap{"Present","Absent"}, Values{"Present","Absent"}] String Ensure;

       [Write, EmbeddedInstance("MSFT_Credential")] String DomainAdminCredential;

       [Write, EmbeddedInstance("MSFT_Credential")] String Password;

       [Write, EmbeddedInstance("MSFT_KeyValuePair"), Description("For mapping users to their last log on time")] String LastLogOn;

};

 

 

If you take a look at the resource module, you will find that it has been updated too with the new parameter while all the logic you added are intact.

Let us cover one more cmdlet exposed by the designer tool:

PS C:\> help Test-DscSchema

 

NAME

    Test-xDscSchema

   

SYNTAX

    Test-DscSchema [-Path] <string> [-IncludeError] [<CommonParameters>] 

   

 

ALIASES

    None

   

 

REMARKS

    None

 

You may have written a MOF schema by hand and want to test whether it will satisfy the conditions required by DSC. You could use the Test-xDscSchema. Suppose we had the following schema:

[ClassVersion("1.0.0.0"), FriendlyName("")]

class Demo_ADUser : OMI_BaseResource

{

       [Key] String UserName;

       [Write, ValueMap{"Present","Absent"}, Values{"Present","Absent"}] String Ensure;

};

 

 

Say we save it as buggy.schema.mof.

PS C:\> Test-xDscSchema -Path .\buggy.schema.mof

Test-MockSchema : The Class name Demo_ADUser does not match the Schema name buggy.

At C:\Program

Files\WindowsPowerShell\Modules\DSCPack_ResourceDesigner\DSCPack_ResourceDesigner.psm1:2454 char:21

+             return (Test-MockSchema $Schema $SchemaCimClass)

+                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    + CategoryInfo         : NotSpecified: (:) [Write-Error], WriteErrorException

    + FullyQualifiedErrorId : ClassNameSchemaNameDifferentError,Test-MockSchema

 

False

 

There you go, it complains that the name of the class and the schema should be the same.

In this post we explored the Resource Designer Tool and saw how easy it is to create our own custom DSC resources. Hope you find it useful!

Given below are the rules which a DSC resource must conform to (this is just for reference and you do not need to worry about them if you use the designer tool):

1)      At least one parameter in the schema is marked as [key]

2)      All parameters MUST be marked with either key, required, write or read property. Additional rules as follows:

a.       [Read] and ([key] or [required] or [write]) can’t coexist.

b.      If multiple qualifiers are specified except [Read], then [key] takes precedence.

c.       If [write] and [required] are specified, then [required] takes precedence.

3)      Class from EmbeddedInstance MUST be either DSC system class or a class defined in the same scope. EmbeddedInstance class should not be DSC resource class i.e. derived from OMI_BaseResource class.

4)      All classes MUST have [ClassVersion] qualifier.

5)      Existence of Get-TargetResource, Set-TargetResource, and Test-TargetResource methods

6)      Set/Test-TargetResource take all [key] and [write] parameters only.

7)      Get-TargetResource takes all [key] parameters and optionally can have [required] as well as [write] parameters

8)      Get/Set/Test-TargetResource do not have any parameters not defined in the schema

9)      Get/Set/Test-TargetResource should have key/required parameter marked as mandatory

 

Updated On 1/13/2014: Removed old version of Resource Designer Tool.  An newer version of this resource can be found here.

Updated On 8/20/2014: Updated some syntax to reflect new version of Resource Designer Tool.

 

Abhik Chatterjee

Windows PowerShell Developer

Microsoft

 

Leave a Comment
  • Please add 5 and 1 and type the answer here:
  • Post
  • Thank you for providing this walk through. Hope the resource designer will be shipped with Windows Server vNext.

  • This is a great article, but I feel I'm missing an important element here. I created a simple filecopy resource that allows you to provide a filter, something the built-in file resource is missing in my opinion. At any rate, I apply a configuration that copies a handful of .txt files to the C:\temp folder. When I remove a single .txt file I should be out of compliance. the code inside the test-dscconfiguration of my .psm1 does in fact work when I manually execute the code inside. but when I run the cmdlet against my configuration it returns true when it should be false.

    github.com/.../FileIO_Copy

    I'll post a question over on the forums shortly as well.

  • With the new version of the Resource Designer Tool the name also changed to xDscResourceDesigner.

    /Stefan

    MSFT

  • I am having a problem creating a resource that has a property that is of type "hashtable".  The resource designer generates the schema.mof file fine, and the hashtabl shows up as a EmbeddedInstance("MSFT_KeyValuePair").  I am able to generate the configuration MOF file.  When I attempt to "Start-DscConfiguration", I get the following output:

    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 DEV-MJOHNSON with user sid S-1-5-21-630933059-147641830-1634843525-6260.

    VERBOSE: [DEV-MJOHNSON]: LCM:  [ Start  Set      ]

    VERBOSE: [DEV-MJOHNSON]: LCM:  [ Start  Resource ]  [[Tokens]WebConfigs]

    VERBOSE: [DEV-MJOHNSON]: LCM:  [ Start  Test     ]  [[Tokens]WebConfigs]

    VERBOSE: [DEV-MJOHNSON]: LCM:  [ End    Test     ]  [[Tokens]WebConfigs]  in 0.0560 seconds.

    PowerShell provider TokenReplace  failed to execute Test-TargetResource functionality with error message: Cannot process argument transformation on parameter 'TokenMap'. Cannot convert the "MSFT_KeyValuePair (Key = "sitename")" value of type

    "Microsoft.Management.Infrastructure.CimInstance#MSFT_KeyValuePair" to type "System.Collections.Hashtable".

       + CategoryInfo          : InvalidOperation: (:) [], CimException

       + FullyQualifiedErrorId : ProviderOperationExecutionFailure

       + PSComputerName        : localhost

    VERBOSE: [DEV-MJOHNSON]: LCM:  [ End    Set      ]

    The SendConfigurationApply function did not succeed.

       + CategoryInfo          : NotSpecified: (root/Microsoft/...gurationManager:String) [], CimException

       + FullyQualifiedErrorId : MI RESULT 1

       + PSComputerName        : localhost

    After search through all DSC resources out there, I found that none have properties of type "hashtable".  After scouring for any resource that passes some sort of key value pairs, I found that the "MSFT_xWebsite" resource (in the xWebAdministration DSC module) has the $BindingInfo propery which is defined as "[Microsoft.Management.Infrastructure.CimInstance[]]$BindingInfo".  When looking at the "MSFT_xWebSite.schema.mof, I notice that there is a class definition for "MSFT_xWebBindingInformation".

  • “ Please try specifying the type of the hashtable  parameter to your *-TargetResource functions as Microsoft.Management.Infrastructure.CimInstance, like so:

    [Microsoft.Management.Infrastructure.CimInstance]

    TokenMap

  • Hi, I've been trying to get DSC to work with a Windows 2008 R2 SP1 server. I have used someone else's sample for setting up a pull server. When running this command:

    Invoke-CimMethod -ComputerName servername -Namespace root/microsoft/windows/desiredstateconfiguration `

    -Class MSFT_DscLocalConfigurationManager -MethodName PerformRequiredConfigurationChecks `

    -Arguments @{Flags = [uint32]1} -Verbose on Windows 2008, I get this error:

    Invoke-CimMethod : The SendConfigurationApply function did not succeed

    + CategoryInfo          : NotSpecified: (root/microsoft/...gurationManager:String) [Invoke-CimMethod], CimException

       + FullyQualifiedErrorId : MI RESULT 1,Microsoft.Management.Infrastructure.CimCmdlets.InvokeCimMethodCommand

    I am very unfamiliar with WIM classes. I have tried to find that namespace on the client but cannot. I can, however, PUSH installations just fine to this client. The pull is the problem.

    Thanks for any help!!

  • Jen,

    We're working on improving the error messaging. The error you're seeing is usually the last one but if you look in the Operational event log for DSC, you'll usually see that the errors come in a set and going back one or two gives you the actual error that you can use to correct your scenario.

    Ray

  • Think I am hitting a size limit on the MOF file generated by New-xDscResourceProperty.  My custom resource has 236 properties.  The MOF and PSM1 file get generated as expected but the config fails with:

    PSDesiredStateConfiguration\Node : The term 'cFimService_Object' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling

    of the name, or if a path was included, verify that the path is correct and try again.

    If I remove most of the properties from the MOF file then it works as expected.

Page 1 of 1 (8 items)