Wish I can author DSC Resource in C#!!

In previous blog, we learned how one can use their PowerShell skills to author DSC resources very easily. Still there are folks (we met some at TechEd NA) who want to author their DSC resources using C# because they are more productive with it than PowerShell language. Well, you can fully leverage the power of DSC by writing your resources in C#. In this blog, we will explore how you can write a C# based DSC resource and later seamlessly consume it from your DSC Configurations.

Authoring DSC resources in C#

For the purpose of this blog, we will write a DSC resource named “xDemoFile”. This resource will be used to assert the existence of a file and its contents. It is similar to a File resource but with limited functionalities.

I)             Project Setup:-

a)      Open visual studio

b)      Create a C# project and provide the name (such as  “cSharpDSCResourceExample”)

c)       Select “Class Library” from the available project templates

d)      Hit “Ok”

e)    Add assembly reference to system.automation.management.dll preferably from PowerShell SDK [but you can add assembly reference to your project from the windows assembly cache, GAC (<systemDrive>\ Windows\Microsoft.NET\assembly\GAC_MSIL\System.Management.Automation\v4.0_3.0.0.0__31bf3856ad364e35\System.Management.Automation.dll)].

f)      *Update the assembly name to match the DSC resource name. (write right click on the project then hit properties and change the Assembly Name to MSFT_xDemoFile)

 

II)           Resource Definition

Similar to script based DSC resource. You will need to define the input and output parameters of your resources in <ResourceName>.schema.mof.  You can generate the schema of your resource using the Resource Designer Tool.

Save the following in to a file named MSFT_xDemoFile.Schema.mof

[ClassVersion(“1.0.0″), FriendlyName(“xDemoFile”)]

class MSFT_XDemoFile : OMI_BaseResource

{

                [Key, Description(“path”)] String Path;

                [Write, Description(“Should the file be present”), ValueMap{“Present”,”Absent”}, Values{“Present”,”Absent”}] String Ensure;

                [Write, Description(“Contentof file.”)] String Content;                  

};

 

 

III)         Resource Implementation

In order to write DSC Resources in C#, you need to implement three PowerShell cmdlets. PowerShell cmdlets are written by inheriting from PSCmdlet or Cmdlet. Detail on how to write PowerShell Cmdlet in C# can be found in this MSDN documentation.

See below for the signature of the cmdlets:-

Get-TargetResource

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

       [Cmdlet(VerbsCommon.Get, “TargetResource”)]

       public class GetTargetResource : PSCmdlet

       {

              [Parameter(Mandatory = true)]

              public string Path { get; set; }

 

///<summary>

/// Implement the logic to write the current state of the resource as a

/// Hash table with keys being the resource properties

/// and the Values are the corresponding current values on the target machine.

 

///</summary>

              protected override void ProcessRecord()

              {

// Download the zip file at the end of this blog to see sample implementation.

 }

 

Set-TargetResouce

         [OutputType(typeof(void))]

    [Cmdlet(VerbsCommon.Set, “TargetResource”)]

    public class SetTargetResource : PSCmdlet

    {

        privatestring _ensure;

        privatestring _content;

       

[Parameter(Mandatory = true)]

        public string Path { get; set; }

       

[Parameter(Mandatory = false)]     

       [ValidateSet(“Present”, “Absent”, IgnoreCase = true)]

       public string Ensure {

            get

            {

                // set the default to present.

               return (this._ensure ?? “Present”);

            }

            set

            {

                this._ensure = value;

            }

           } 

            public string Content {

            get { return (string.IsNullOrEmpty(this._content) ? “” : this._content); }

            set { this._content = value; }

        }

 

///<summary>

        /// Implement the logic to set the state of the machine to the desired state.

        ///</summary>

        protected override void ProcessRecord()

        {

//Implement the set method of the resource

/* Uncomment this section if your resource needs a machine reboot.

PSVariable DscMachineStatus = new PSVariable(“DSCMachineStatus”, 1, ScopedItemOptions.AllScope);

                this.SessionState.PSVariable.Set(DscMachineStatus);

*/    

  }

    }

Test-TargetResource    

       [Cmdlet(“Test”, “TargetResource”)]

    [OutputType(typeof(Boolean))]

    public class TestTargetResource : PSCmdlet

    {  

       

        private string _ensure;

        private string _content;

 

        [Parameter(Mandatory = true)]

        public string Path { get; set; }

 

        [Parameter(Mandatory = false)]

        [ValidateSet(“Present”, “Absent”, IgnoreCase = true)]

        public string Ensure

        {

            get

            {

                // set the default to present.

                return (this._ensure ?? “Present”);

            }

            set

            {

                this._ensure = value;

            }

        }

 

        [Parameter(Mandatory = false)]

        public string Content

        {

            get { return (string.IsNullOrEmpty(this._content) ? ““:this._content);}

            set { this._content = value; }

        }

 

///<summary>

/// Write a Boolean value which indicates whether the current machine is in   

/// desired state or not.

        ///</summary>

        protected override void ProcessRecord()

        {

                // Implement the test method of the resource.

        }

}

 

IV)        How to handle Machine reboot in C# based DSC Resources.

If your resource needs a machine reboot. The way to indicate that in script-based DSC resource is setting the global variable $global:DSCMachineStatus  to 1 in the Set-TargetResource function of the resource.  To do similar in C#-based DSC resource, you will need to set the same variable in the runspace where the Set Cmdlet of the resource will be executed.

Adding the following two lines will signal a machine reboot to the DSC engine.

PSVariable DSCMachineStatus = new PSVariable(“DSCMachineStatus”, 1, ScopedItemOptions.AllScope);

this.SessionState.PSVariable.Set(DSCMachineStatus);

 

 

Consume C# based resources

I)        How to deploy C# based DSC Resource

The folder structure of C# based DSC resource is the same as the script based resource. Please refer to this blog to see how DSC resources should be deployed in your machine.

The output binaries from your project and the schema mof of the resource should be deployed to the correct path before you can use it to author or apply configurations.

Example: – if you deploy the resource under a module “CSharpDSCResource” inside $env:programfiles, the folder structure would look like:-

             $env:ProgramFiles\WindowsPowerShell\Modules\CSharpDSCResource\DSCResources\MSFT_XDemoFile\MSFT_XDemoFile.dll

             $env:ProgramFiles\WindowsPowerShell\Modules\CSharpDSCResource\DSCResources\MSFT_XDemoFile\MSFT_XDemoFile.Shema.mof

II)        Create Configuration:-

 

Configuration CsharpExample

{

    Import-DSCResource -Module CSharpDSCResource

    Node(“localhost”)

    {

        xDemoFile fileLog

        {

            Path = “c:\foo.txt”

            Content = “content example”

            Ensure =Present”         

           

        }

    }

}

 

III)        Run the configuration:-

 

Start-DSCConfiguration -ComputerName localhost -Path .\CsharpExample\ -Verbose -Wait

           

 

Berhe Abrha

Windows PowerShell Team

 

 

 

 

 

 

Complet_Sample_DSCResource_CSharp.zip