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.
1Install-Module -Name Plaster -Force
Create the initial Plaster template along with the metadata section which contains data about the manifest itself.
1$manifestProperties = @{
2 Path = 'C:\GitHub\PlasterTemplate\PlasterManifest.xml'
3 TemplateName = 'ScriptModuleTemplate'
4 TemplateType = 'Project'
5 Author = 'Mike F Robbins'
6 Description = 'Scaffolds the files required for a PowerShell script module'
7 Tags = 'PowerShell, Module, ModuleManifest'
8}
9
10$Folder = Split-Path -Path $manifestProperties.Path -Parent
11if (-not(Test-Path -Path $Folder -PathType Container)) {
12 New-Item -Path $Folder -ItemType Directory | Out-Null
13}
14
15New-PlasterManifest @manifestProperties
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.
1New-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.
1<?xml version="1.0" encoding="utf-8"?>
2<plasterManifest
3 schemaVersion="1.1"
4 templateType="Project" xmlns="http://www.microsoft.com/schemas/PowerShell/Plaster/v1">
5 <metadata>
6 <name>ScriptModuleTemplate</name>
7 <id>ee4fdc83-1b9a-47ae-b4e6-336053283a86</id>
8 <version>1.0.0</version>
9 <title>ScriptModuleTemplate</title>
10 <description>Scaffolds the files required for a PowerShell script module</description>
11 <author>Mike F Robbins</author>
12 <tags>PowerShell, Module, ModuleManifest</tags>
13 </metadata>
14 <parameters></parameters>
15 <content></content>
16</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.
1<parameters>
2 <parameter name='Name' type='text' prompt='Name of the module' />
3 <parameter name='Description' type='text' prompt='Brief description of module (required for publishing to the PowerShell Gallery)' />
4 <parameter name='Version' type='text' default='0.1.0' prompt='Enter the version number of the module' />
5 <parameter name='Author' type='user-fullname' prompt="Module author's name" store='text' />
6 <parameter name='CompanyName' type='text' prompt='Name of your Company' default='mikefrobbins.com' />
7 <parameter name='PowerShellVersion' default='3.0' type='text' prompt='Minimum PowerShell version' />
8</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.
1<content>
2 <message>
3 Creating folder structure
4 </message>
5 <file source='' destination='${PLASTER_PARAM_Name}'/>
6 <message>
7 Deploying common files
8 </message>
9 <file source='module.psm1' destination='${PLASTER_PARAM_Name}\${PLASTER_PARAM_Name}.psm1'/>
10 <message>
11 Creating Module Manifest
12 </message>
13 <newModuleManifest
14 destination='${PLASTER_PARAM_Name}\${PLASTER_PARAM_Name}.psd1'
15 moduleVersion='$PLASTER_PARAM_Version'
16 rootModule='${PLASTER_PARAM_Name}.psm1'
17 author='$PLASTER_PARAM_Author'
18 companyName='$PLASTER_PARAM_CompanyName'
19 description='$PLASTER_PARAM_Description'
20 powerShellVersion='$PLASTER_PARAM_PowerShellVersion'
21 encoding='UTF8-NoBOM'/>
22</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.
1<?xml version="1.0" encoding="utf-8"?>
2<plasterManifest schemaVersion="1.1" templateType="Project"
3 xmlns="http://www.microsoft.com/schemas/PowerShell/Plaster/v1">
4 <metadata>
5 <name>ScriptModuleTemplate</name>
6 <id>ee4fdc83-1b9a-47ae-b4e6-336053283a86</id>
7 <version>1.0.0</version>
8 <title>ScriptModuleTemplate</title>
9 <description>Scaffolds the files required for a PowerShell script module</description>
10 <author>Mike F Robbins</author>
11 <tags>PowerShell, Module, ModuleManifest</tags>
12 </metadata>
13 <parameters>
14 <parameter name='Name' type='text' prompt='Name of the module' />
15 <parameter name='Description' type='text' prompt='Brief description of module (required for publishing to the PowerShell Gallery)' />
16 <parameter name='Version' type='text' default='0.1.0' prompt='Enter the version number of the module' />
17 <parameter name='Author' type='user-fullname' prompt="Module author's name" store='text' />
18 <parameter name='CompanyName' type='text' prompt='Name of your Company' default='mikefrobbins.com' />
19 <parameter name='PowerShellVersion' default='3.0' type='text' prompt='Minimum PowerShell version' />
20 </parameters>
21 <content>
22 <message>
23 Creating folder structure
24 </message>
25 <file source='' destination='${PLASTER_PARAM_Name}'/>
26 <message>
27 Deploying common files
28 </message>
29 <file source='module.psm1' destination='${PLASTER_PARAM_Name}\${PLASTER_PARAM_Name}.psm1'/>
30 <message>
31 Creating Module Manifest
32 </message>
33 <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'/>
34 </content>
35</plasterManifest>
The verbose parameter provides additional information which can be helpful in troubleshooting problems.
1Invoke-Plaster -TemplatePath C:\GitHub\PlasterTemplate -DestinationPath C:\tmp2 -Verbose
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.
1$plasterParams = @{
2 TemplatePath = 'C:\GitHub\PlasterTemplate'
3 DestinationPath = 'C:\tmp2'
4 Name = 'MrTestModule'
5 Description = 'Mike Robbins Test Module'
6 Version = '0.9.0'
7 Author = 'Mike F Robbins'
8 CompanyName = 'mikefrobbins.com'
9 PowerShellVersion = '3.0'
10}
11If (-not(Test-Path -Path $plasterParams.DestinationPath -PathType Container)) {
12 New-Item -Path $plasterParams.DestinationPath -ItemType Directory | Out-Null
13}
14Invoke-Plaster @plasterParams
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.
1Invoke-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
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:
- Levelling up your PowerShell modules with Plaster by Kieran Jacobsen
- Using Plaster To Create a New PowerShell Module by Rob Sewell
- PowerShell: Adventures in Plaster by Kevin Marquette
- Working with Plaster by David Christian
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.
µ