When writing code, it's important to make it modular by decoupling the code as much as possible. Check out how to accomplish this with Pester tests.

A great practice to get into when writing code is making code modular. What does this mean? Creating modular code indicates decoupling code as much as possible. It means to create small functions that call other small functions instead of creating one large script. This decoupling practice creates a codebase that's more easily modified, easier to understand and is more testable.

When it comes to Desired State Configuration (DSC), this decoupling best practice remains the same. In DSC, we have two different "types" of code; configuration data and code to apply that configuration. Although it is possible to create one large DSC configuration script combining both of them, it's not recommended. Decoupling the configuration data from the actual configuration allows you to add, remove and modify existing items inside of the configuration data without ever actually changing a line of code.

A simple DSC configuration script using a separate configuration data PSD1 file is similar to  what is shown below. This file is structured in a way that DSC understands and can read various attributes of it. Notice, inside of the NonNodeData section we have a WindowsFeature key. This could potentially store one key (as we have here) or more if need be. These will be the Windows features that are added to any targeted nodes.

@{
     AllNodes = @(
         @{
             NodeName = '*'
             PsDscAllowDomainUser = $true
             PsDscAllowPlainTextPassword = $true
         },
         @{
             NodeName = 'DC'
         }
     )
     NonNodeData = @{
         WindowsFeatures = 'XPS-Viewer'
     }
 }

Here's an example DSC configuration script we'll be using to leverage the configuration data below. This scripted is configured to use external configuration data as indicated by using the $AllNodes.NodeName reference, rather than a static hostname. By making this into a variable, we are controlling what hosts get targeted via configuration data.

configuration AddWindowsFeatures
 {
     Import-DscResource -ModuleName PSDesiredStateConfiguration
 
     Node $ConfigurationData.AllNodes.NodeName
     {
         ($ConfigurationData.NonNodeData.WindowsFeatures).foreach( {
             WindowsFeature $_
             {
                 Ensure = 'Present'
                 Name = $_
             }
         })
     }
 }

We can now invoke this DSC configuration against a node by creating the MOF file and invoking the DSC configuration.st

PS> . C:\NewWebServer.ps1
PS> AddWindowsFeatures -ConfigurationData C:\ConfigurationData.psd1
PS? Start-DscConfiguration -Wait -Force -Path .\AddWindowsFeatures -Verbose -ComputerName DC

This will then enable the XPS-Viewer Windows feature on the DC node, but how do you know for sure? I'd like to create some Pester tests for this to run after the DSC configuration. To create a Pester test, I'd have to duplicate my efforts and maintain another list of computers to run against if I wasn't using configuration data. To test the Windows feature we just enabled, a Pester test might look something like this:

describe 'XPS-Viewer Windows feature install' {
 
     it 'should enable the XPS-Viewer Windows feature' {
         $result = Get-WindowsFeature -Name 'XPS-Viewer' -ComputerName DC
         $result.Installed | should be $true
     }
 
 }

This works, but we've got two references to the DC computer name. We need have a central location for all the computer names so that when we update our DSC scripts, our Pester tests will follow right along. Luckily, we're using configuration data and in that external file are all of the names of the node the DSC configuration ran against. Our Pester test can now be updated to query the same source of nodes that DSC does.

#requires -Version 5
 
 $configData = Import-PowerShellDataFile -Path C:\ConfigurationData.psd1
 $nodes = ($configData.AllNodes.NodeName).where({$_ -ne '*'})
 
 describe 'XPS-Viewer Windows feature install' {
 
     foreach ($node in $nodes) {
         it "should enable the XPS-Viewer Windows feature on the [$node] node" {
             $result = Get-WindowsFeature -Name 'XPS-Viewer' -ComputerName $node
             $result.Installed | should be $true
         }
     }
 
 }
 
 Describing XPS-Viewer Windows feature install
   [+] should enable the XPS-Viewer Windows feature on the [DC] node 2.1s

This method now allows us to add as many computers as necessary without updating the Pester tests!