PowerShell Problem Solver: Cleaning Up Old DSC Resources

PowerShell-Text-Purple-hero
Desired State Configuration (DSC) is a fascinating technology that can dramatically alter an IT pro’s job description. This is a big topic, but today I want to focus on one small piece, and one that I have found a little annoying. One of the elements that makes DSC work is a DSC Resource. A resource is a configuration element that is defined generally through PowerShell script and packaged as a PowerShell module. In the beginning we only had a small number of resources and everything was essentially version 1.0. Now there are many, many resources, often in varying stages of maturity as reflected by version number.
 

 
You can find and install many of these resources from the PowerShell Gallery.
Finding DSC Resources
The challenge is you can download new versions and end up with multiple versions locally. For example, look at a single DSC Resource I have installed.
Multiple versions installed
When I go to use the resource in a DSC Configuration I am presented with a related error condition.
Configuration Error
If I hover over the red squiggle, I’ll get a message about multiple resource versions detected. The solution is simple enough. I merely have to explicitly state which resource module to import.

Configuration WebConfig {
Param([string[]]$Computername)
Import-DscResource -ModuleName PSDesiredStateConfiguration,
@{ModuleName='xWebAdministration';RequiredVersion='1.13.0.0'}
Node $Computername {
  xWebSite My {
    Name = 'MyWebSite'
    Ensure = 'Present'
    PhysicalPath = 'C:\Web\MyWebSite'
    EnabledProtocols = 'http'
  }
  #additional configuration
}
}

This is probably for the best because I can guarantee exactly which version to use. This is also a good practice if I’m creating configurations I plan to share. But if I always keep my resources up-to-date with the latest version, I can save myself a little time. So what I need is a tool to prune my installed DSC Resources, always keeping the most current version and uninstalling everything else. There is one big caveat here: some DSC Resources take dependencies on other resources and often a specific version. PowerShell doesn’t like it if you try to uninstall a dependent resource. In addition, your configurations might need to be updated to work with newer versions.  But if you are good, or merely want to see how you can find and remove older DSC resources, then you can use this PowerShell function.

#requires -version 5.0
Function Remove-DSCResourceModule {
#Remove all but newest DSC Resource module
[cmdletbinding(SupportsShouldProcess=$True,ConfirmImpact="high")]
Param(
[Parameter(Position=0,HelpMessage="Enter the module name of a DSC resource.")]
[ValidateNotNullorEmpty()]
[string]$Name,
[switch]$All
)
if ($Name) {
    Write-Verbose "Getting module $Name"
    $clean = Get-Module -Name $Name -ListAvailable | Group Name | where Count -gt 1
}
else {
    Write-Verbose "Getting all DSC resource modules"
    $clean = Get-DSCResource |
    Select @{Name="Name";Expression={$_.ModuleName}},Version -Unique |
    Group Name |
    where Count -gt 1
}
if ($clean.Group) {
    Write-Verbose "Processing module group"
    foreach ($mod in $clean) {
        Write-Host "Cleaning up $($mod.name)" -ForegroundColor Cyan
        #sort modules from Group by version, skipping the first one which should
        #be the newest. Uninstall the rest
        $mod.group | Sort Version -Descending | Select -Skip 1 |
        Foreach {
          <#
            There appears to be an issue in passing -WhatIf to
            Uninstall-Module so I'll insert my own code
          #>
          if ($PSCmdlet.ShouldProcess($_.name)) {
                Write-Host "Removing version $($_.version)" -ForegroundColor Cyan
                Uninstall-Module -Name $_.Name -RequiredVersion $_.Version
          } #should process
      }
    } #foreach
}
else {
    Write-Warning "No modules found to remove"
}
}

The function gets all DSC Resources and groups them by module name and then selects those with more than result. It then sorts each group by version number and uninstalls all but the most recent. I can cleanup a single resource:
Clean up a single resource
Or all of them.
Clean up all
I’ll go ahead and re-run the command without -WhatIf to clean up. I have flagged the removal process with a high impact so that you will be prompted to remove. And if a dependency is detected the module won’t be uninstalled.
Removing older resources

If, for some reason, I find I need a specific resource version, I can download it again from the PowerShell Gallery, so there shouldn’t be that much risk in periodically cleaning up. Still, this is significant process so please, please, test and and plan accordingly.