PowerShell How-To

How To Create PowerShell DSC Class-Based Resources

Desired State Configuration (DSC) is an excellent tool to get control of your infrastructure. A core component of DSC is the DSC resource. When DSC was introduced, administrators had to create a schema MOF file and build these resources a not-so-intuitive way. With the introduction of PowerShell v5, however, classes were added. As part of that rollout, we now have the ability to build DSC resources via classes instead of the old MOF-based method.

Creating a class-based DSC resources is simpler via its file structure, and you also don't have to worry about including that schema.mof file. To create a class-based resource, we'll first need to create a module folder. The module will act as the grouping of all like-minded resources.

I'm going to be creating a DSC resource called MyFolder. This is a resource that will simply check to see if a folder exists and, if not, create it. Technically, I could use the built-in File resource for this, but it's a great example to show how to build any class-based resource. I'll also need a module to place this resource in. Since the module is a grouping of similar resources, I'll call my module MyFileSystem.

Creating a DSC class-based resource involves a few different steps:

  1. Creating the module folder with the same name as the module.
  2. Creating the module manifest.
  3. Create the DSC resource inside of the module.
  4. Creating three methods; Test, Get and Set inside of the resource.
  5. Create the DSC configuration to execute the resource.
  6. Call the configuration with an optional node name to create the MOF.
  7. Start the DSC configuration process.

I'll first create the module folder in any folder path in $env:PSModulePath.

$moduleFolder =  "$env:ProgramFiles\WindowsPowerShell\Modules\MyFileSystem"
mkdir $moduleFolder

Next, I need a manifest. I can expedite creating one with the New-ModuleManifest command. Here, I need to specify the resource name that will be in my module along with specifying the module file name as the RootModule.

New-ModuleManifest -DscResourcesToExport 'MyFolder' -RootModule  'MyFileSystem.psm1' -Path "$moduleFolder\MyFileSystem.psd1"

Next, I'll open up the MyFileSystem.psm1 module and first create an enum. Enums are easy to create in PowerShell v5. Just specify the enum keyword, the name and each enum inside of the set.  A class-based resource requires a property called Ensure with an Absent and Present value.

enum Ensure
{
Present
Absent
}

I'll now define the resource itself.  Notice that I have couple properties much like function parameters with a Get(), Set() and Test() methods.

[DscResource()]
class MyFolder ## MyFolder is the name of the resource
{
[DscProperty(Key)] ## The Key property is required to uniquely identify the resource
[string]$Path

[DscProperty(Mandatory)] ## At least one mandatory property is required
[Ensure]$Ensure

## What to do if it's not in the right state. This returns nothing, indicated by [void].
[void] Set()
{

}
## Test to ensure it's in the right state. This returns a Boolean value, indicated by [bool].
[bool] Test()
{

}
## Get the state. This returns an instance of the class itself, indicated by [MyFolder]
[MyFolder] Get()
{

}
}

When the resource is first run, it will execute the Get() method. In this instance, I'm testing to see if the value of the Path property represented as the property on the current object ($this) exist. If it does, it will set the Ensure property to Present else it will set it to Absent.

[MyFolder] Get()
{
if (Test-Path -Path $this.Path)
{
$this.Ensure = [Ensure]::Present
}
else
{
$this.Ensure = [Ensure]::Absent
}
return $this
}

Next, it will run the Test() method. The test method will return a [bool] $true or false depending on the value of Ensure.

[bool] Test()
{
$There = Test-Path -Path $this.Path
if ($this.Ensure -eq [Ensure]::Present)
{
return $There
}
else
{
return -not $There
}
}

Now, we have the Set() method that will only be called if Test() returns $false (meaning the folder does not exist). If I intended for the folder to be there then it will attempt to create it otherwise if I set Ensure to Absent meaning I do not want the folder there, it will try to remove it.

[void] Set()
{
if ($this.Ensure -eq [Ensure]::Present)
{
$null = mkdir $this.Path
}
elseif ($this.Ensure -eq [Ensure]::Absent)
{
Remove-Item -Path $this.Path -Force
}
}

Finally, I'll create a configuration to execute this resource on a computer.

Configuration MyFolderConfig
{
Import-DSCResource -module MyFileSystem ## import the module the resource is a part of
MyFolder DoIt ## Call this resource block 'DoIt'
{
## Ensure that the C:\SomeDirectory exists
Path = 'C:\SomeDirectory'
Ensure = 'Present'
}
}

I'll now call the MyFolderConfig just like a function to create the MOF file my target node will consume.

MyFolderConfig

This will create a configuration for the localhost.

I can now apply this configuration by calling Start-DscConfiguration.

[Click on image for larger view.] Figure 1.

You can see that C:\SomeDirectory now exists!  The resource did its job.

About the Author

Adam Bertram is a 20-year veteran of IT. He's an automation engineer, blogger, consultant, freelance writer, Pluralsight course author and content marketing advisor to multiple technology companies. Adam also founded the popular TechSnips e-learning platform. He mainly focuses on DevOps, system management and automation technologies, as well as various cloud platforms mostly in the Microsoft space. He is a Microsoft Cloud and Datacenter Management MVP who absorbs knowledge from the IT field and explains it in an easy-to-understand fashion. Catch up on Adam's articles at adamtheautomator.com, connect on LinkedIn or follow him on Twitter at @adbertram or the TechSnips Twitter account @techsnips_io.


comments powered by Disqus
Most   Popular