Thursday, September 1, 2016

Making PS DSC modules available to nodes.

Editor's Note: Started working with Desired State Configuration (DSC) and boy do I miss the things Puppet automates for me!

I've been working with PowerShell DSC and have an interesting scenario:
  • You want to use other resources, like xFirewall, to manage firewall state on a node.
  • You are 'push'ing your configuration to nodes.
  • Problem: nodes need the modules installed on them.
You might run into an error similar to this:
Cannot find module xNetworking_2.11.0.0 from the server'c41c31d5-67e4-4f80-8204-f749ff1ae192',ModuleName='xNetworking',ModuleVersion='')/ModuleContent. Could not install module dependencies needed by the configuration.
You could switch your nodes from push to pull mode.  Nodes will pull modules in addition to their configuration.  But maybe you don't want to use pull mode.

First you'll need to configure your nodes to use a pull server.  Configure the DSC Local Manager like you normally would for a node but only use this configuration.  Leave the node in push mode.  But now the node knows where to look for modules, if required.  Replace ServerUrl with your pull server.  What do you mean you don't have a pull server?
  ConfigurationRepositoryWeb PullServer
  ServerUrl = ''
See the gist if you haven't opened it yet.  The gist shows a DSC where we're using xFirewall and xSmbShare resources, both resources aren't installed by default.  They come from the xNetworking and xSmbShare modules, respectively.

The gist includes some additional powershell to package the modules we're using and place them in the pull server's modules directory.

Packaging modules for download by nodes is a pain in the ass.  Maybe you've come across an error like this:
module file did not contain a module with required version
You cannot simply archive the module at its path:
C:\Program Files\WindowsPowerShell\Modules\xNetworking
You must archive the files within the directory corresponding the module version you want.  Assuming you want the latest version, you can use the cmdlet Get-Module find the version.
$version = (Get-Module -ListAvailable $module).Version.ToString()
Then you can package all the files in the module's base directory, ala the method .ModuleBase from Get-Module, into an archive named "$module_$"

I'd like to find a way to use the $modules array in the Import-DscResource statement on line 11.  It would make this sort of snippet more reusable.  I wouldn't be repeating myself by defining required modules in two places.