In this chapter, you will learn the definition and syntax of a DSC resource. This entails both the code/syntactic elements as well as the folder structure and placement. We will spend significant time covering the purpose of a DSC resource and best practices to develop custom DSC resources; we'll do this by showing examples of badly authored DSC resources and working on them until they are well-defined DSC resources. We will then explain class-based DSC resource differences and how you should handle them compared to their V4 counterparts.
DSC resources are PowerShell modules that contain both the schema and implementation to determine and set the target node state. The schema is defined in MOF files, and the implementation code is defined in PowerShell script modules. Beginning with PowerShell V5, DSC resources can also be implemented using only PowerShell script files.
This is all well and good for a definition, but we are getting ahead of ourselves. At its simplest definition, a DSC resource is a PowerShell script that can be run against a target node that only changes the system state when it differs from the desired state. Schema MOFs and module folders aside, the core of a DSC resource is the idempotent code that determines the state of the target node and adjusts it until it is in line with what is expected. This code is implemented in PowerShell, so you're already familiar with the language used, which is a huge boost in starting to customize your own DSC resources.
Since DSC resources are authored using PowerShell code, the power is in your hands. It's the same PowerShell code you know and love. This boosts productivity out of the gate, leaving you with the freedom to focus on the actual code that performs the changes, instead of worrying about learning a new way of doing things.
DSC resources are responsible for both testing the current state of a target node and bringing the target node to the expected state. They follow the concepts of idempotency by only changing the system state if it is not in compliance, and they can be run as many times as desired. Even if they are run many times, DSC resources will only change the state if it is not in compliance with the desired state.
DSC resources are the smallest units of change in PowerShell DSC, so, by their very nature, they are much more focused and narrow in the subject matter they address. You will not find a single all-encompassing DSC resource that installs and configures the entirety of a Microsoft SQL Server four-node cluster with alwayson availability, but, instead, you'll find several focused DSC resources that each cover unique and specific task to accomplish. One DSC resource handles installation and basic configuration, while another handles configuration of the cluster and addition of the four nodes, and yet another handles creating or removing databases.
This specification of DSC resources allows you to perform complicated orchestration of multi-node deployments. Rather than a resource that installs a whole SQL cluster across nodes, several individual DSC resources install each component of a SQL cluster, which can be chained across nodes and reboots to accomplish the task. The point is that DSC resources, such as PowerShell Cmdlets, do one thing and in a repeatable, idempotent fashion.
We will delve into more about how DSC resources accomplish this as we progress through this chapter.
At this point, we've covered what DSC resources are. Even with this knowledge, it is good to step back and consider what makes a good DSC resource. Remember when we were covering DSC configuration scripts in the previous chapter and we spent some time on best practices to create them? The same approach applies here.
It is up to you to uphold the tenets of idempotency that we have been learning over the course of this book in each DSC resource you author. The DSC resource is the last stop in the line of executions that determines whether the current state of the target node is the desired state we described in the DSC configuration script. You must write the code that tests and sets this state in a way that will change the state of the target node only once: the time when the system is not in the desired state.
Significant care must be taken when accomplishing this. You must not change the state of the system if it is in the state we desire. Users of PowerShell DSC expect to be able to run DSC as many times as they want and receive no unintended side-effects from doing so. This is accomplished by running near-obsessive testing of the current state to ensure we are changing things when we have to.
A good DSC resource will do one thing, and it will do that one thing well. When crafting your DSC resources, you should choose the smallest tasks possible to tackle. For example, consider SQL Server. A DSC resource could be written to install and configure a SQL Server that, even if it got very complicated, could manage both installation and configuration in one resource. However, installing SQL Server is not a simple task when you take into account that it can be installed in a cluster or as part of a recovery set or a great many other options. How do you handle the long list of options to install SQL Server compared to the equally long list of configuration settings?
Consider the configuration of SQL Server. It is likely that the configuration options of SQL Server will change across versions, but the installation steps won't differ as much. Differing configuration steps or options can be handled in smaller DSC resources, reducing the amount of work each has to do while enabling flexibility of choosing when to execute.
Since DSC resources are PowerShell modules, it is easy to use their built-in discovery methods to share functions and other code artifacts between DSC resources. You must resist overusing this approach, as it can bite you as time goes on. Let's say two DSC resources called ExampleResource1 and ExampleResource2 are in the same module called ExampleFooResource. ExampleResource1 has a function that ExampleResource2 borrows to retrieve the same information in order to do its work.
We've created a dependency, which is now our responsibility to uphold for the life of the DSC resource. If we change the function in ExampleResource1, it has to change in ExampleResource2 too. If we don't have some way of tracking this dependency, it can be become hard to troubleshoot how to break this dependency until the DSC resource is used. If it is a lesser-used function and we don't have a testing practice set up, it could be quite some time before we find out the breakage. While this is not optimal, if you had to copy and paste the code instead of reusing it, you would have to deal with updating code in several files whenever you fixed a bug or updated a functionality.
The point being made here is to be aware of the trade-offs when you develop DSC resources.
Once you have a solid custom DSC resource that works and is well-tested (refer to the following section for advice on testing DSC resources), we highly recommend contributing it to the community. There are several places to contribute your code to.
Microsoft has a wonderful how-to section on its GitHub repo on how to get started with contributing DSC resources: https://github.com/PowerShell/DscResources/blob/master/CONTRIBUTING.md.
The PowerShell.org site hosts its own GitHub repo you can contribute to at https://github.com/PowerShellOrg.
We have touched on how to create PowerShell MOF-based DSC resources briefly as we have worked our way through this book, and here, we will dive into the details of doing that in depth. We will cover the folder structures and files needed as well as the mindset and best practices necessary in order to make custom DSC resources that are effective and useful to you.
This section was titled PowerShell V4 custom DSC resources in the first edition of this book. Since then, the term MOF-based has been solidified as the correct term to use for this type of DSC resource. This makes sense, as you will see that an MOF-based DSC resource is supported from PowerShell V4 up to V6 (at the time of writing this).
Before continuing, ensure that you are familiar with creating PowerShell v2 modules. Knowing how to create and use PowerShell modules is the key to understanding how DSC resources are made.
An MOF-based DSC resource has a strict folder structure. By convention, each PowerShell module that contains one or more DSC resources is stored in the $env:ProgramFiles\WindowsPowerShell\Modules folder. You can store DSC resources in any of the standard $env:PSModulePath paths, or you can store them in a location of your choosing as long as you append the location to the $env:PSModulePath variable before compiling the resource.
The strict folder structure for MOF-based DSC resources is as follows:
$env:ProgramFiles\WindowsPowerShell\Modules
|- ExampleModule
|- ExampleModule.psd1
|- DSCResources
|- Example_InstallFoo
|- Example_InstallFoo.psd1 (optional)
|- Example_InstallFoo.psm1 (required)
|- Example_InstallFoo.schema.mof (required)
|- Example_ConfigureFoo
|- Example_ConfigureFoo.psd1 (optional)
|- Example_ConfigureFoo.psm1 (required)
|- Example_ConfigureFoo.schema.mof (required)
A folder called DSCResources inside the root folder, with other folders that are also DSC resources themselves, and files with schema separate from the code module files. Like with the DSC composite resource, you must be wondering why they make this so hard! It is difficult to keep track at first, but once you have done one or two DSC resources, it's not really that hard to keep track. It is easier to understand once you see that each DSC resource is made up of one or more PowerShell modules.
A DSC resource contains one or more PowerShell modules inside a root PowerShell module. This root PowerSh...