Resource Designer Tool – A walkthrough writing a DSC resource

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-DscResourceDemo_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-xDscResourceNameDemo_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