Automatically create a checksum and publish DSC MOF configuration files to an SMB pull server

You've configured one or more DSC (Desired State Configuration) SMB pull servers in your environment. You've also configured the target nodes appropriately. One problem that seems to be a constant problem in your environment when authoring and updating DSC configuration files (MOF files) is keeping track of what GUID belongs to which machine and it's also a common problem to forget to update the corresponding checksum when a configuration file is updated. Last week, you spent an entire day troubleshooting an issue where a machine wasn't pulling the updated configuration only to learn that the checksum hadn't been updated. Sound familiar?

To alleviate this problem, you've decided to write a PowerShell function that will be used to automatically determine the appropriate SMB share to copy the MOF configuration files to, determine the correct ConfigurationID (GUID), and generate the necessary checksum. The function to accomplish that task is shown in the following code example:

  1#Requires -Version 4.0
  2function Publish-MrMOFToSMB {
  3
  4<#
  5.SYNOPSIS
  6    Publishes a DSC MOF configuration file to the pull server that's configured on a target node(s).
  7
  8.DESCRIPTION
  9    Publish-MrMOFToSMB is an advanced PowerShell function that publishes one or more MOF configuration files
 10    to the an SMB DSC server by determining the ConfigurationID (GUID) that's configured on the target node along
 11    with the UNC path of the SMB pull server and creates the necessary checksum along with copying the MOF and
 12    checksum to the pull server.
 13
 14.PARAMETER ConfigurationPath
 15    The folder path on the local computer that contains the mof configuration files.
 16
 17.PARAMETER ComputerName
 18    The computer name of the target node that the DSC configuration is created for.
 19
 20.EXAMPLE
 21     Publish-MrMOFToSMB -ConfigurationPath 'C:\MyMofFiles'
 22
 23.EXAMPLE
 24     Publish-MrMOFToSMB -ConfigurationPath 'C:\MyMofFiles' -ComputerName 'Server01', 'Server02'
 25
 26.EXAMPLE
 27     'Server01', 'Server02' | Publish-MrMOFToSMB -ConfigurationPath 'C:\MyMofFiles'
 28
 29.EXAMPLE
 30     MyDscConfiguration -Param1 Value1 -Parm2 Value2 | Publish-MrMOFToSMB
 31
 32.INPUTS
 33    String
 34
 35.OUTPUTS
 36    None
 37
 38.NOTES
 39    Author:  Mike F Robbins
 40    Website: http://mikefrobbins.com
 41    Twitter: @mikefrobbins
 42#>
 43
 44    [CmdletBinding()]
 45    param (
 46        [Parameter(Mandatory,
 47                   ValueFromPipelineByPropertyName)]
 48        [ValidateScript({Test-Path -Path $_ -PathType Container})]
 49        [Alias('Directory')]
 50        [string]$ConfigurationPath,
 51
 52        [Parameter(ValueFromPipeline,
 53                   ValueFromPipelineByPropertyName)]
 54        [Alias('BaseName')]
 55        [string[]]$ComputerName
 56    )
 57
 58    BEGIN {
 59        if (-not($PSBoundParameters['ComputerName'])) {
 60            $ComputerName = (Get-ChildItem -Path $ConfigurationPath\*.mof).basename
 61        }
 62    }
 63
 64    PROCESS {
 65        foreach ($Computer in $ComputerName) {
 66
 67            try {
 68                Write-Verbose -Message "Retrieving LCM information from $Computer"
 69                $LCMConfig = Get-DscLocalConfigurationManager -CimSession $Computer -ErrorAction Stop
 70            }
 71            catch {
 72                Write-Error -Message "An error has occurred. Error details: $_.Exception.Message"
 73                continue
 74            }
 75
 76            $servermof = "$ConfigurationPath\$Computer.mof"
 77
 78            if (-not(Get-ChildItem -Path $servermof -ErrorAction SilentlyContinue)) {
 79                Write-Error -Message "Unable to find MOF file for $Computer in location: $ConfigurationPath"
 80            }
 81            elseif ($LCMConfig.RefreshMode -ne 'Pull') {
 82                Write-Error -Message "The LCM on $Computer is not configured for DSC pull mode."
 83            }
 84            elseif ($LCMConfig.DownloadManagerName -ne 'DscFileDownloadManager' -and $LCMConfig.ConfigurationDownloadManagers.ResourceId -notlike '`[ConfigurationRepositoryShare`]*') {
 85                Write-Error -Message "LCM on $Computer not configured to receive configuration from DSC SMB pull server"
 86            }
 87            elseif (-not($LCMConfig.ConfigurationID)) {
 88                Write-Error -Message "A ConfigurationID (GUID) has not been set in the LCM on $Computer"
 89            }
 90            else {
 91                if ($LCMConfig.ConfigurationDownloadManagers.SourcePath) {
 92                    $SMBPath = "$($LCMConfig.ConfigurationDownloadManagers.SourcePath)"
 93                }
 94                elseif ($LCMConfig.DownloadManagerCustomData.Value) {
 95                    $SMBPath = "$($LCMConfig.DownloadManagerCustomData.Value)"
 96                }
 97
 98                Write-Verbose -Message "Creating DSCChecksum for $servermof"
 99                New-DSCCheckSum -ConfigurationPath $servermof -Force
100
101                if (Test-Path -Path $SMBPath) {
102
103                    $guidmof = Join-Path -Path $SMBPath -ChildPath "$($LCMConfig.ConfigurationID).mof"
104
105                    try {
106                        Write-Verbose -Message "Copying $servermof.checksum to $guidmof.checksum"
107                        Copy-Item -Path "$servermof.checksum" -Destination "$guidmof.checksum" -ErrorAction Stop
108
109                        Write-Verbose -Message "Copying $servermof to $guidmof"
110                        Copy-Item -Path $servermof -Destination $guidmof -ErrorAction Stop
111                    }
112                    catch {
113                        Write-Error -Message "An error has occurred. Error details: $_.Exception.Message"
114                    }
115
116                }
117                else {
118                    Write-Error -Message "Unable to connect to $SMBPath as specified in the LCM on $Computer for it's DSC pull server"
119                }
120            }
121        }
122    }
123}

A very basic configuration will be used to test the function shown in the previous example.

 1configuration TelnetClient {
 2    param (
 3        [Parameter(Mandatory)]
 4        [string[]]$ComputerName
 5    )
 6    node $ComputerName {
 7        WindowsFeature TelnetClient {
 8            Name = 'Telnet-Client'
 9            Ensure = 'Present'
10        }
11    }
12}

After the configuration is loaded into memory, it's called as it normally would be to generate the MOF configuration files and then it can simply be piped to the Publish-MrMOFToSMB function which will query the target node for the necessary information to automatically generate the required checksum and copy both the MOF configuration and checksum files to the SMB pull server, renaming the files to the GUID name on the destination end:

1TelnetClient -ComputerName 'Server01', 'Server02' | Publish-MrMOFToSMB -Verbose

dscsmb1a.jpg

If the MOF files have already been generated and you want to publish all of the ones in the .\TelnetClient folder to the appropriate SMB pull server, just specify that path via the ConfigurationPath parameter:

1Publish-MrMOFToSMB -ConfigurationPath .\TelnetClient -Verbose

dscsmb2a.jpg

If you only want to publish some of the existing MOF files in that folder, specify one or more computer names via the ComputerName parameter:

1Publish-MrMOFToSMB -ComputerName server01 -ConfigurationPath .\TelnetClient -Verbose

dscsmb3a.jpg

For preexisting MOF files, the computer names can also be specified via parameter input which allows you to programmatically determine the computer names you want to publish the existing MOF files for by piping the results of some other cmdlet into this one:

1'server02' | Publish-MrMOFToSMB -ConfigurationPath .\TelnetClient -Verbose

dscsmb4a.jpg

Parameter validation is performed on the ConfigurationPath parameter which means if the path specified for the MOF files doesn't exist, the function will stop at that point and not attempt to continue any further:

1Publish-MrMOFToSMB -ComputerName server01 -ConfigurationPath .\DoesNotExist -Verbose

dscsmb5b.jpg

There are various other checks in place as well. Errors will be generated if the target node is offline, if the MOF file doesn't already exist, if the LCM on the target node is not configured for pull mode or for SMB, if the ConfigurationID hasn't been set to a GUID, and if the SMB share that's specified in the target node's LCM cannot be contacted:

1Publish-MrMOFToSMB -ComputerName server01, server05, sql01, dc01, server03, server04, dc02, server02 -ConfigurationPath .\TelnetClient -Verbose

dscsmb6c.jpg

Any of the ones that are specified which are valid will complete successfully as shown in the previous set of results where server01 and server02 (the first and last one) completed successfully even though all of the others failed for the specified reasons.

µ