Using Plaster to create a PowerShell Script Module template

I have a function in my MrToolkit module named New-MrScriptModule that creates the scaffolding for a new PowerShell script module. It creates a PSM1 file and a module manifest (PSD1 file) along with the folder structure for a script module. To reduce the learning curve of Plaster as much as possible, I’m simply going to replace that existing functionality with Plaster in this blog article. Then moving forward, I’ll add additional functionality.

For those of you who aren’t familiar with Plaster, per their GitHub readme, “Plaster is a template-based file and project generator written in PowerShell. Its purpose is to streamline the creation of PowerShell module projects, Pester tests, DSC configurations, and more. File generation is performed using crafted templates which allow the user to fill in details and choose from options to get their desired output.”

First, start out by installing the Plaster module.

Install-Module -Name Plaster -Force

plaster1a.jpg

Create the initial Plaster template along with the metadata section which contains data about the manifest itself.

$manifestProperties = @{
    Path         = 'C:\GitHub\PlasterTemplate\PlasterManifest.xml'
    TemplateName = 'ScriptModuleTemplate'
    TemplateType = 'Project'
    Author       = 'Mike F Robbins'
    Description  = 'Scaffolds the files required for a PowerShell script module'
    Tags         = 'PowerShell, Module, ModuleManifest'
}

$Folder = Split-Path -Path $manifestProperties.Path -Parent
if (-not(Test-Path -Path $Folder -PathType Container)) {
    New-Item -Path $Folder -ItemType Directory | Out-Null
}

New-PlasterManifest @manifestProperties

plaster2b.jpg

You could also run the same command without splatting, although you’ll need to make sure the folder structure already exists if you use the following example.

New-PlasterManifest -Path C:\GitHub\PlasterTemplate\PlasterManifest.xml -TemplateName ScriptModuleTemplate -TemplateType Project -Author 'Mike F Robbins' -Description 'Scaffolds the files required for a PowerShell script module' -Tags 'PowerShell, Module, ModuleManifest'

That creates an XML file that looks like the one in the following example.

<?xml version="1.0" encoding="utf-8"?>
<plasterManifest
  schemaVersion="1.1"
  templateType="Project" xmlns="http://www.microsoft.com/schemas/PowerShell/Plaster/v1">
  <metadata>
    <name>ScriptModuleTemplate</name>
    <id>ee4fdc83-1b9a-47ae-b4e6-336053283a86</id>
    <version>1.0.0</version>
    <title>ScriptModuleTemplate</title>
    <description>Scaffolds the files required for a PowerShell script module</description>
    <author>Mike F Robbins</author>
    <tags>PowerShell, Module, ModuleManifest</tags>
  </metadata>
  <parameters></parameters>
  <content></content>
</plasterManifest>

Based on the information from the about_Plaster_CreatingAManifest help file which can be viewed in PowerShell or in their GitHub repository, Name is mandatory, ID is a GUID that’s automatically generated if one isn’t provided, Version defaults to 1.0.0 unless otherwise specified, Title defaults to the same value as Name unless specified, and Description, Tags, and Author are all optional. Description will be added, but blank if not specified. Tags and Author won’t be added unless specified.

Notice that there’s a parameter and content section in the previous example that was created. In this scenario, the parameters section is for adding values that may be different each time a new PowerShell script module is created. It’s the same concept and no different than adding parameters to a PowerShell function.

<parameters>
    <parameter name='Name' type='text' prompt='Name of the module' />
    <parameter name='Description' type='text' prompt='Brief description of module (required for publishing to the PowerShell Gallery)' />
    <parameter name='Version' type='text' default='0.1.0' prompt='Enter the version number of the module' />
    <parameter name='Author' type='user-fullname' prompt="Module author's name" store='text' />
    <parameter name='CompanyName' type='text' prompt='Name of your Company' default='mikefrobbins.com' />
    <parameter name='PowerShellVersion' default='3.0' type='text' prompt='Minimum PowerShell version' />
</parameters>

These are almost the same items that are parameters for my New-MrScriptModule function. I’ve added a Version parameter for the module version. Path will be specified when creating the script module instead of at this point. Also notice that Author is set to "type=user-fullname". In my opinion, this is better than setting a default because if a module author isn’t specified, this information will be retrieved from the users Git config. This also allows conditional defaults for the author depending on whether or not you’re using the "IncludeIf" functionality in your Git config file. IncludeIf was introduced in Git version 2.13.

Next is the content section.

<content>
    <message>
    Creating folder structure
    </message>
      <file source='' destination='${PLASTER_PARAM_Name}'/>
    <message>
      Deploying common files
    </message>
    <file source='module.psm1' destination='${PLASTER_PARAM_Name}\${PLASTER_PARAM_Name}.psm1'/>
    <message>
      Creating Module Manifest
    </message>
    <newModuleManifest
      destination='${PLASTER_PARAM_Name}\${PLASTER_PARAM_Name}.psd1'
      moduleVersion='$PLASTER_PARAM_Version'
      rootModule='${PLASTER_PARAM_Name}.psm1'
      author='$PLASTER_PARAM_Author'
      companyName='$PLASTER_PARAM_CompanyName'
      description='$PLASTER_PARAM_Description'
      powerShellVersion='$PLASTER_PARAM_PowerShellVersion'
      encoding='UTF8-NoBOM'/>
</content>

I’ve placed my standard PSM1 file in my Plaster template folder and now I’m all set to create a new PowerShell script module. The completed Plaster template is shown below.

<?xml version="1.0" encoding="utf-8"?>
<plasterManifest schemaVersion="1.1" templateType="Project"
  xmlns="http://www.microsoft.com/schemas/PowerShell/Plaster/v1">
  <metadata>
    <name>ScriptModuleTemplate</name>
    <id>ee4fdc83-1b9a-47ae-b4e6-336053283a86</id>
    <version>1.0.0</version>
    <title>ScriptModuleTemplate</title>
    <description>Scaffolds the files required for a PowerShell script module</description>
    <author>Mike F Robbins</author>
    <tags>PowerShell, Module, ModuleManifest</tags>
  </metadata>
  <parameters>
    <parameter name='Name' type='text' prompt='Name of the module' />
    <parameter name='Description' type='text' prompt='Brief description of module (required for publishing to the PowerShell Gallery)' />
    <parameter name='Version' type='text' default='0.1.0' prompt='Enter the version number of the module' />
    <parameter name='Author' type='user-fullname' prompt="Module author's name" store='text' />
    <parameter name='CompanyName' type='text' prompt='Name of your Company' default='mikefrobbins.com' />
    <parameter name='PowerShellVersion' default='3.0' type='text' prompt='Minimum PowerShell version' />
  </parameters>
  <content>
    <message>
    Creating folder structure
    </message>
    <file source='' destination='${PLASTER_PARAM_Name}'/>
    <message>
      Deploying common files
    </message>
    <file source='module.psm1' destination='${PLASTER_PARAM_Name}\${PLASTER_PARAM_Name}.psm1'/>
    <message>
      Creating Module Manifest
    </message>
    <newModuleManifest destination='${PLASTER_PARAM_Name}\${PLASTER_PARAM_Name}.psd1' moduleVersion='$PLASTER_PARAM_Version' rootModule='${PLASTER_PARAM_Name}.psm1' author='$PLASTER_PARAM_Author' companyName='$PLASTER_PARAM_CompanyName' description='$PLASTER_PARAM_Description' powerShellVersion='$PLASTER_PARAM_PowerShellVersion' encoding='UTF8-NoBOM'/>
  </content>
</plasterManifest>

The verbose parameter provides additional information which can be helpful in troubleshooting problems.

Invoke-Plaster -TemplatePath C:\GitHub\PlasterTemplate -DestinationPath C:\tmp2 -Verbose

plaster3a.jpg

A hash table can also be used to create the module without being prompted. The parameter names are TemplatePath, DestinationPath, and any parameters defined in the template. You must specify all of the parameters, even the ones that you’ve specified defaults for otherwise you’ll be prompted to either enter a value or confirm the default.

$plasterParams = @{
    TemplatePath      = 'C:\GitHub\PlasterTemplate'
    DestinationPath   = 'C:\tmp2'
    Name              = 'MrTestModule'
    Description       = 'Mike Robbins Test Module'
    Version           = '0.9.0'
    Author            = 'Mike F Robbins'
    CompanyName       = 'mikefrobbins.com'
    PowerShellVersion = '3.0'
}
If (-not(Test-Path -Path $plasterParams.DestinationPath -PathType Container)) {
    New-Item -Path $plasterParams.DestinationPath -ItemType Directory | Out-Null
}
Invoke-Plaster @plasterParams

plaster4a.jpg

Technically, you could also specify all of the parameters and their values via parameter input, but the ones specified in the Plaster template won’t tab-expand or show up in intellisense.

Invoke-Plaster -TemplatePath C:\GitHub\PlasterTemplate -DestinationPath C:\tmp2 -Name MrTestModule -Description 'Mike Robbins Test Module' -Version 0.9.0 -Author 'Mike F Robbins' -CompanyName mikefrobbins.com  -PowerShellVersion 3.0

plaster5a.jpg

Sometimes learning new things can seem overwhelming based on examples you’ll find on the Internet. I’m not sure about others, but I’ve learned to keep it super simple to start with and add additional complexity one step at a time to reduce the learning curve. No one starts out as an expert at anything.

This is only the tip of the iceberg for Plaster. Other recommended resources include the documentation and examples in the Plaster GitHub repository and the following blog articles by others who have written about it:

There’s also a video on Working with Plaster that David Christian presented for one of our Mississippi PowerShell User Group meetings.

The Plaster template shown in this blog article and ongoing updates to it can be downloaded from my PlasterTemplate repository on GitHub.


Update February 16th, 2018 Modified the code to create the Plaster manifest based on feedback from Lee Dailey on the PowerShell SubReddit.

ยต