Unpacking Software Livestream

Join our monthly Unpacking Software livestream to hear about the latest news, chat and opinion on packaging, software deployment and lifecycle management!

Learn More

Chocolatey Product Spotlight

Join the Chocolatey Team on our regular monthly stream where we put a spotlight on the most recent Chocolatey product releases. You'll have a chance to have your questions answered in a live Ask Me Anything format.

Learn More

Chocolatey Coding Livestream

Join us for the Chocolatey Coding Livestream, where members of our team dive into the heart of open source development by coding live on various Chocolatey projects. Tune in to witness real-time coding, ask questions, and gain insights into the world of package management. Don't miss this opportunity to engage with our team and contribute to the future of Chocolatey!

Learn More

Calling All Chocolatiers! Whipping Up Windows Automation with Chocolatey Central Management

Webinar from
Wednesday, 17 January 2024

We are delighted to announce the release of Chocolatey Central Management v0.12.0, featuring seamless Deployment Plan creation, time-saving duplications, insightful Group Details, an upgraded Dashboard, bug fixes, user interface polishing, and refined documentation. As an added bonus we'll have members of our Solutions Engineering team on-hand to dive into some interesting ways you can leverage the new features available!

Watch On-Demand
Chocolatey Community Coffee Break

Join the Chocolatey Team as we discuss all things Community, what we do, how you can get involved and answer your Chocolatey questions.

Watch The Replays
Chocolatey and Intune Overview

Webinar Replay from
Wednesday, 30 March 2022

At Chocolatey Software we strive for simple, and teaching others. Let us teach you just how simple it could be to keep your 3rd party applications updated across your devices, all with Intune!

Watch On-Demand
Chocolatey For Business. In Azure. In One Click.

Livestream from
Thursday, 9 June 2022

Join James and Josh to show you how you can get the Chocolatey For Business recommended infrastructure and workflow, created, in Azure, in around 20 minutes.

Watch On-Demand
The Future of Chocolatey CLI

Livestream from
Thursday, 04 August 2022

Join Paul and Gary to hear more about the plans for the Chocolatey CLI in the not so distant future. We'll talk about some cool new features, long term asks from Customers and Community and how you can get involved!

Watch On-Demand
Hacktoberfest Tuesdays 2022

Livestreams from
October 2022

For Hacktoberfest, Chocolatey ran a livestream every Tuesday! Re-watch Cory, James, Gary, and Rain as they share knowledge on how to contribute to open-source projects such as Chocolatey CLI.

Watch On-Demand

Downloads:

1,019

Downloads of v 1.1.4:

221

Last Update:

20 Oct 2022

Package Maintainer(s):

Software Author(s):

  • Microsoft Corporation

Tags:

admin powershell module template

Plaster (PowerShell Module)

  • 1
  • 2
  • 3

1.1.4 | Updated: 20 Oct 2022

Downloads:

1,019

Downloads of v 1.1.4:

221

Maintainer(s):

Software Author(s):

  • Microsoft Corporation

Plaster (PowerShell Module) 1.1.4

Legal Disclaimer: Neither this package nor Chocolatey Software, Inc. are affiliated with or endorsed by Microsoft Corporation. The inclusion of Microsoft Corporation trademark(s), if any, upon this webpage is solely to identify Microsoft Corporation goods or services and not for commercial purposes.

  • 1
  • 2
  • 3

All Checks are Passing

3 Passing Tests


Validation Testing Passed


Verification Testing Passed

Details

Scan Testing Successful:

No detections found in any package files

Details
Learn More

Deployment Method: Individual Install, Upgrade, & Uninstall

To install Plaster (PowerShell Module), run the following command from the command line or from PowerShell:

>

To upgrade Plaster (PowerShell Module), run the following command from the command line or from PowerShell:

>

To uninstall Plaster (PowerShell Module), run the following command from the command line or from PowerShell:

>

Deployment Method:

NOTE

This applies to both open source and commercial editions of Chocolatey.

1. Enter Your Internal Repository Url

(this should look similar to https://community.chocolatey.org/api/v2/)


2. Setup Your Environment

1. Ensure you are set for organizational deployment

Please see the organizational deployment guide

2. Get the package into your environment

  • Open Source or Commercial:
    • Proxy Repository - Create a proxy nuget repository on Nexus, Artifactory Pro, or a proxy Chocolatey repository on ProGet. Point your upstream to https://community.chocolatey.org/api/v2/. Packages cache on first access automatically. Make sure your choco clients are using your proxy repository as a source and NOT the default community repository. See source command for more information.
    • You can also just download the package and push it to a repository Download

3. Copy Your Script

choco upgrade plaster -y --source="'INTERNAL REPO URL'" [other options]

See options you can pass to upgrade.

See best practices for scripting.

Add this to a PowerShell script or use a Batch script with tools and in places where you are calling directly to Chocolatey. If you are integrating, keep in mind enhanced exit codes.

If you do use a PowerShell script, use the following to ensure bad exit codes are shown as failures:


choco upgrade plaster -y --source="'INTERNAL REPO URL'" 
$exitCode = $LASTEXITCODE

Write-Verbose "Exit code was $exitCode"
$validExitCodes = @(0, 1605, 1614, 1641, 3010)
if ($validExitCodes -contains $exitCode) {
  Exit 0
}

Exit $exitCode

- name: Install plaster
  win_chocolatey:
    name: plaster
    version: '1.1.4'
    source: INTERNAL REPO URL
    state: present

See docs at https://docs.ansible.com/ansible/latest/modules/win_chocolatey_module.html.


chocolatey_package 'plaster' do
  action    :install
  source   'INTERNAL REPO URL'
  version  '1.1.4'
end

See docs at https://docs.chef.io/resource_chocolatey_package.html.


cChocoPackageInstaller plaster
{
    Name     = "plaster"
    Version  = "1.1.4"
    Source   = "INTERNAL REPO URL"
}

Requires cChoco DSC Resource. See docs at https://github.com/chocolatey/cChoco.


package { 'plaster':
  ensure   => '1.1.4',
  provider => 'chocolatey',
  source   => 'INTERNAL REPO URL',
}

Requires Puppet Chocolatey Provider module. See docs at https://forge.puppet.com/puppetlabs/chocolatey.


4. If applicable - Chocolatey configuration/installation

See infrastructure management matrix for Chocolatey configuration elements and examples.

Package Approved

This package was approved by moderator Windos on 26 Oct 2022.

Description

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.

You can think of Plaster as Yeoman for the PowerShell community.

NOTE: This module requires a minimum of PowerShell v3.

NOTE: This is an automatically updated package. If you find it is out of date by more than a week, please contact the maintainer(s) and let them know the package is no longer updating correctly.


tools\.skipAutoUninstaller
 
tools\chocolateyBeforeModify.ps1
$ErrorActionPreference = 'Stop'

$moduleName = $env:ChocolateyPackageName      # this could be different from package name
Remove-Module -Name $moduleName -Force -ErrorAction SilentlyContinue
tools\chocolateyInstall.ps1
$ErrorActionPreference = 'Stop'

$toolsDir   = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)"
$moduleName = 'Plaster'  # this may be different from the package name and different case

if ($PSVersionTable.PSVersion.Major -lt 3) {
    throw "PSCodeHealth module requires a minimum of PowerShell v3."
}

# module may already be installed outside of Chocolatey
Remove-Module -Name $moduleName -Force -ErrorAction SilentlyContinue

$sourcePath = Join-Path -Path $toolsDir -ChildPath "$modulename\*"
$destPath   = Join-Path -Path $env:ProgramFiles -ChildPath "WindowsPowerShell\Modules\$moduleName"

if ($PSVersionTable.PSVersion.Major -ge 5)
{
    $manifestFile = Join-Path -Path $toolsDir -ChildPath "$moduleName\$moduleName.psd1"
    $manifest     = Test-ModuleManifest -Path $manifestFile -WarningAction Ignore -ErrorAction Stop
    $destPath     = Join-Path -Path $destPath -ChildPath $manifest.Version.ToString()
}

Write-Verbose "Creating destination directory '$destPath' for module."
New-Item -Path $destPath -ItemType Directory -Force -ErrorAction SilentlyContinue | Out-Null

Write-Verbose "Moving '$moduleName' files from '$sourcePath' to '$destPath'."
Move-Item -Path $sourcePath -Destination $destPath -Force

if ($PSVersionTable.PSVersion.Major -lt 4)
{
    $modulePaths = [Environment]::GetEnvironmentVariable('PSModulePath', 'Machine') -split ';'
    if ($modulePaths -notcontains $destPath)
    {
        Write-Verbose "Adding '$destPath' to PSModulePath."
        $newModulePath = @($destPath, $modulePaths) -join ';'

        [Environment]::SetEnvironmentVariable('PSModulePath', $newModulePath, 'Machine')
        $env:PSModulePath = $newModulePath
    }
}
tools\chocolateyUninstall.ps1
$ErrorActionPreference = 'Stop'

$moduleName = $env:ChocolateyPackageName
$sourcePath = Join-Path -Path $env:ProgramFiles -ChildPath "WindowsPowerShell\Modules\$moduleName"

Write-Verbose "Removing all version of '$moduleName' from '$sourcePath'."
Remove-Item -Path $sourcePath -Recurse -Force -ErrorAction SilentlyContinue

if ($PSVersionTable.PSVersion.Major -lt 4) {
    $modulePaths = [Environment]::GetEnvironmentVariable('PSModulePath', 'Machine') -split ';'

    Write-Verbose "Removing '$sourcePath' from PSModulePath."
    $newModulePath = $modulePaths | Where-Object { $_ -ne $sourcePath }

    [Environment]::SetEnvironmentVariable('PSModulePath', $newModulePath, 'Machine')
    $env:PSModulePath = $newModulePath
}
tools\LICENSE.txt
From: https://raw.githubusercontent.com/PowerShell/Plaster/master/LICENSE

LICENSE

The MIT License (MIT)

Copyright (c) Microsoft Corporation.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
tools\VERIFICATION.txt
VERIFICATION
Verification is intended to assist the Chocolatey moderators and community in verifying that this package's contents are trustworthy.

To verify the files using the project source:

1. Please go to the project source location (https://github.com/PowerShell/Plaster) and download the source files;
2. Build the source to create the binary files to verify;
3. Use Get-FileHash -Path <FILE TO VERIFY> to get the file hash value from both the built file (from step 1 above) and the file from the package and compare them;

Alternatively you can download the module from the PowerShell Gallery ...

    Save-Module -Name Plaster -Path <PATH TO DOWNLOAD TO>

... and compare the files from the package against those in the installed module. Again use Get-FileHash -Path <FILE TO VERIFY> to retrieve those hash values.
tools\Plaster\GetModuleExtension.ps1
function Get-ModuleExtension {
    [CmdletBinding()]
    param(
        [string]
        $ModuleName,

        [Version]
        $ModuleVersion,

        [Switch]
        $ListAvailable
    )

    #Only get the latest version of each module
    $modules = Get-Module -ListAvailable
    if (!$ListAvailable) {
        $modules = $modules |
        Group-Object Name |
        Foreach-Object {
            $_.group |
            Sort-Object Version |
            Select-Object -Last 1
        }
    }

    Write-Verbose "`nFound $($modules.Length) installed modules to scan for extensions."

    function ParseVersion($versionString) {
        $parsedVersion = $null

        if ($versionString) {
            # We're targeting Semantic Versioning 2.0 so make sure the version has
            # at least 3 components (X.X.X).  This logic ensures that the "patch"
            # (third) component has been specified.
            $versionParts = $versionString.Split('.');
            if ($versionParts.Length -lt 3) {
                $versionString = "$versionString.0"
            }

            if ($PSVersionTable.PSEdition -eq "Core") {
                $parsedVersion = New-Object -TypeName "System.Management.Automation.SemanticVersion" -ArgumentList $versionString
            } else {
                $parsedVersion = New-Object -TypeName "System.Version" -ArgumentList $versionString
            }
        }

        return $parsedVersion
    }

    foreach ($module in $modules) {
        if ($module.PrivateData -and
            $module.PrivateData.PSData -and
            $module.PrivateData.PSData.Extensions) {

            Write-Verbose "Found module with extensions: $($module.Name)"

            foreach ($extension in $module.PrivateData.PSData.Extensions) {

                Write-Verbose "Comparing against module extension: $($extension.Module)"

                $minimumVersion = ParseVersion $extension.MinimumVersion
                $maximumVersion = ParseVersion $extension.MaximumVersion

                if (($extension.Module -eq $ModuleName) -and
                    (!$minimumVersion -or $ModuleVersion -ge $minimumVersion) -and
                    (!$maximumVersion -or $ModuleVersion -le $maximumVersion)) {
                    # Return a new object with the extension information
                    [PSCustomObject]@{
                        Module         = $module
                        MinimumVersion = $minimumVersion
                        MaximumVersion = $maximumVersion
                        Details        = $extension.Details
                    }
                }
            }
        }
    }
}
tools\Plaster\GetPlasterTemplate.ps1
. $PSScriptRoot\GetModuleExtension.ps1

function Get-PlasterTemplate {
    [CmdletBinding()]
    param(
        [Parameter(Position = 0,
            ParameterSetName = "Path",
            HelpMessage = "Specifies a path to a folder containing a Plaster template or multiple template folders.  Can also be a path to plasterManifest.xml.")]
        [ValidateNotNullOrEmpty()]
        [string]
        $Path,

        [Parameter(Position = 1,
            ParameterSetName = "Path",
            HelpMessage = "Will return templates that match the name.")]
        [Parameter(Position = 1,
            ParameterSetName = "IncludedTemplates",
            HelpMessage = "Will return templates that match the name.")]
        [ValidateNotNullOrEmpty()]
        [string]
        $Name = "*",

        [Parameter(ParameterSetName = "Path",
            HelpMessage = "Will return templates that match the tag.")]
        [Parameter(ParameterSetName = "IncludedTemplates",
            HelpMessage = "Will return templates that match the tag.")]
        [ValidateNotNullOrEmpty()]
        [string]
        $Tag = "*",

        [Parameter(ParameterSetName = "Path",
            HelpMessage = "Indicates that this cmdlet gets the items in the specified locations and in all child items of the locations.")]
        [switch]
        $Recurse,

        [Parameter(Position = 0,
            Mandatory = $true,
            ParameterSetName = "IncludedTemplates",
            HelpMessage = "Initiates a search for latest version Plaster templates inside of installed modules.")]
        [switch]
        [Alias("IncludeModules")]
        $IncludeInstalledModules,

        [Parameter(ParameterSetName = "IncludedTemplates",
            HelpMessage = "If specified, searches for Plaster templates inside of all installed module versions.")]
        [switch]
        $ListAvailable
    )

    process {
        function CreateTemplateObjectFromManifest([System.IO.FileInfo]$manifestPath, [string]$name, [string]$tag) {

            $manifestXml = Test-PlasterManifest -Path $manifestPath
            $metadata = $manifestXml["plasterManifest"]["metadata"]

            $manifestObj = [PSCustomObject]@{
                Name         = $metadata["name"].InnerText
                Title        = $metadata["title"].InnerText
                Author       = $metadata["author"].InnerText
                Version      = New-Object -TypeName "System.Version" -ArgumentList $metadata["version"].InnerText
                Description  = $metadata["description"].InnerText
                Tags         = $metadata["tags"].InnerText.split(",") | ForEach-Object { $_.Trim() }
                TemplatePath = $manifestPath.Directory.FullName
            }

            $manifestObj.PSTypeNames.Insert(0, "Microsoft.PowerShell.Plaster.PlasterTemplate")
            Add-Member -MemberType ScriptMethod -InputObject $manifestObj -Name "InvokePlaster" -Value { Invoke-Plaster -TemplatePath $this.TemplatePath }
            return $manifestObj | Where-Object Name -like $name | Where-Object Tags -like $tag
        }

        function GetManifestsUnderPath([string]$rootPath, [bool]$recurse, [string]$name, [string]$tag) {
            $manifestPaths = Get-ChildItem -Path $rootPath -Include "plasterManifest.xml" -Recurse:$recurse
            foreach ($manifestPath in $manifestPaths) {
                CreateTemplateObjectFromManifest $manifestPath $name $tag -ErrorAction SilentlyContinue
            }
        }

        if ($Path) {
            # Is this a folder path or a Plaster manifest file path?
            if (!$Recurse.IsPresent) {
                if (Test-Path $Path -PathType Container) {
                    $Path = Resolve-Path "$Path/plasterManifest.xml"
                }

                # Use Test-PlasterManifest to load the manifest file
                Write-Verbose "Attempting to get Plaster template at path: $Path"
                CreateTemplateObjectFromManifest $Path $Name $Tag
            } else {
                Write-Verbose "Attempting to get Plaster templates recursively under path: $Path"
                GetManifestsUnderPath $Path $Recurse.IsPresent $Name $Tag
            }
        } else {
            # Return all templates included with Plaster
            GetManifestsUnderPath "$PSScriptRoot\Templates" $true $Name $Tag

            if ($IncludeInstalledModules.IsPresent) {
                # Search for templates in module path
                $GetModuleExtensionParams = @{
                    ModuleName    = "Plaster"
                    ModuleVersion = $PlasterVersion
                    ListAvailable = $ListAvailable
                }

                $extensions = Get-ModuleExtension @GetModuleExtensionParams

                foreach ($extension in $extensions) {
                    # Scan all module paths registered in the module
                    foreach ($templatePath in $extension.Details.TemplatePaths) {
                        $expandedTemplatePath =
                        [System.IO.Path]::Combine(
                            $extension.Module.ModuleBase,
                            $templatePath,
                            "plasterManifest.xml")

                        CreateTemplateObjectFromManifest $expandedTemplatePath $Name $Tag -ErrorAction SilentlyContinue
                    }
                }
            }
        }
    }
}
tools\Plaster\InvokePlaster.ps1
## DEVELOPERS NOTES & CONVENTIONS
##
##  1. All text displayed to the user except for Write-Debug (or $PSCmdlet.WriteDebug()) text must be added to the
##     string tables in:
##         en-US\Plaster.psd1
##         Plaster.psm1
##  2. If a new manifest element is added, it must be added to the Schema\PlasterManifest-v1.xsd file and then
##     processed in the appropriate function in this script.  Any changes to <parameter> attributes must be
##     processed not only in the ProcessParameter function but also in the dynamicparam function.
##
##  3. Non-exported functions should avoid using the PowerShell standard Verb-Noun naming convention.
##     They should use PascalCase instead.
##
##  4. Please follow the scripting style of this file when adding new script.

function Invoke-Plaster {
    [System.Diagnostics.CodeAnalysis.SuppressMessage('PSAvoidShouldContinueWithoutForce', '', Scope='Function', Target='CopyFileWithConflictDetection')]
    [System.Diagnostics.CodeAnalysis.SuppressMessage('PSAvoidUsingConvertToSecureStringWithPlainText', '', Scope='Function', Target='ProcessParameter')]
    [System.Diagnostics.CodeAnalysis.SuppressMessage('PSShouldProcess', '', Scope='Function', Target='CopyFileWithConflictDetection')]
    [System.Diagnostics.CodeAnalysis.SuppressMessage('PSShouldProcess', '', Scope='Function', Target='ProcessFile')]
    [System.Diagnostics.CodeAnalysis.SuppressMessage('PSShouldProcess', '', Scope='Function', Target='ProcessModifyFile')]
    [System.Diagnostics.CodeAnalysis.SuppressMessage('PSShouldProcess', '', Scope='Function', Target='ProcessNewModuleManifest')]
    [System.Diagnostics.CodeAnalysis.SuppressMessage('PSShouldProcess', '', Scope='Function', Target='ProcessRequireModule')]
    [CmdletBinding(SupportsShouldProcess=$true)]
    param(
        [Parameter(Position = 0, Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]
        $TemplatePath,

        [Parameter(Position = 1, Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]
        $DestinationPath,

        [Parameter()]
        [switch]
        $Force,

        [Parameter()]
        [switch]
        $NoLogo,

        [Parameter()]
        [switch]
        $PassThru
    )

    # Process the template's Plaster manifest file to convert parameters defined there into dynamic parameters.
    dynamicparam {
        $paramDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary

        $manifest = $null
        $manifestPath = $null
        $templateAbsolutePath = $null

        # Nothing to do until the TemplatePath parameter has been provided.
        if ($null -eq $TemplatePath) {
            return
        }

        try {
            # Let's convert non-terminating errors in this function to terminating so we
            # catch and format the error message as a warning.
            $ErrorActionPreference = 'Stop'

            # The constrained runspace is not available in the dynamicparam block.  Shouldn't be needed
            # since we are only evaluating the parameters in the manifest - no need for EvaluateConditionAttribute as we
            # are not building up multiple parametersets.  And no need for EvaluateAttributeValue since we are only
            # grabbing the parameter's value which is static.
            $templateAbsolutePath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($TemplatePath)
            if (!(Test-Path -LiteralPath $templateAbsolutePath -PathType Container)) {
                throw ($LocalizedData.ErrorTemplatePathIsInvalid_F1 -f $templateAbsolutePath)
            }

            # Load manifest file using culture lookup
            $manifestPath = GetPlasterManifestPathForCulture $templateAbsolutePath $PSCulture
            if (($null -eq $manifestPath) -or (!(Test-Path $manifestPath))) {
                return
            }

            $manifest = Test-PlasterManifest -Path $manifestPath -ErrorAction Stop 3>$null

            # The user-defined parameters in the Plaster manifest are converted to dynamic parameters
            # which allows the user to provide the parameters via the command line.
            # This enables non-interactive use cases.
            foreach ($node in $manifest.plasterManifest.parameters.ChildNodes) {
                if ($node -isnot [System.Xml.XmlElement]) {
                    continue
                }

                $name = $node.name
                $type = $node.type
                $prompt = if ($node.prompt) { $node.prompt } else { $LocalizedData.MissingParameterPrompt_F1 -f $name }

                if (!$name -or !$type) { continue }

                # Configure ParameterAttribute and add to attr collection
                $attributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
                $paramAttribute = New-Object System.Management.Automation.ParameterAttribute
                $paramAttribute.HelpMessage = $prompt
                $attributeCollection.Add($paramAttribute)

                switch -regex ($type) {
                    'text|user-fullname|user-email' {
                        $param = New-Object System.Management.Automation.RuntimeDefinedParameter `
                                     -ArgumentList ($name, [string], $attributeCollection)
                        break
                    }

                    'choice|multichoice' {
                        $choiceNodes = $node.ChildNodes
                        $setValues = New-Object string[] $choiceNodes.Count
                        $i = 0

                        foreach ($choiceNode in $choiceNodes){
                            $setValues[$i++] = $choiceNode.value
                        }

                        $validateSetAttr = New-Object System.Management.Automation.ValidateSetAttribute $setValues
                        $attributeCollection.Add($validateSetAttr)
                        $type = if ($type -eq 'multichoice') { [string[]] } else { [string] }
                        $param = New-Object System.Management.Automation.RuntimeDefinedParameter `
                                     -ArgumentList ($name, $type, $attributeCollection)
                        break
                    }

                    default { throw ($LocalizedData.UnrecognizedParameterType_F2 -f $type,$name) }
                }

                $paramDictionary.Add($name, $param)
            }
        }
        catch {
            Write-Warning ($LocalizedData.ErrorProcessingDynamicParams_F1 -f $_)
        }

        $paramDictionary
    }

    begin {
        # Write out the Plaster logo if necessary
        $plasterLogo = @'
  ____  _           _
 |  _ \| | __ _ ___| |_ ___ _ __
 | |_) | |/ _` / __| __/ _ \ '__|
 |  __/| | (_| \__ \ ||  __/ |
 |_|   |_|\__,_|___/\__\___|_|
'@

        if (!$NoLogo) {
            $versionString = "v$PlasterVersion"
            Write-Host $plasterLogo
            Write-Host ((" " * (50 - $versionString.Length)) + $versionString)
            Write-Host ("=" * 50)
        }

        $boundParameters = $PSBoundParameters
        $constrainedRunspace = $null
        $templateCreatedFiles = @{}
        $defaultValueStore = @{}
        $fileConflictConfirmNoToAll = $false
        $fileConflictConfirmYesToAll = $false
        $flags = @{
            DefaultValueStoreDirty = $false
        }

        # Verify TemplatePath parameter value is valid.
        $templateAbsolutePath = $PSCmdlet.GetUnresolvedProviderPathFromPSPath($TemplatePath)
        if (!(Test-Path -LiteralPath $templateAbsolutePath -PathType Container)) {
            throw ($LocalizedData.ErrorTemplatePathIsInvalid_F1 -f $templateAbsolutePath)
        }

        # We will have a null manifest if the dynamicparam scriptblock was unable to load the template manifest
        # or it wasn't valid. If so, let's try to load it here. If anything, we can provide better errors here.
        if ($null -eq $manifest) {
            if ($null -eq $manifestPath) {
                $manifestPath = GetPlasterManifestPathForCulture $templateAbsolutePath $PSCulture
            }

            if (Test-Path -LiteralPath $manifestPath -PathType Leaf) {
                $manifest = Test-PlasterManifest -Path $manifestPath -ErrorAction Stop 3>$null
                $PSCmdlet.WriteDebug("In begin, loading manifest file '$manifestPath'")
            }
            else {
                throw ($LocalizedData.ManifestFileMissing_F1 -f $manifestPath)
            }
        }

        # If the destination path doesn't exist, create it.
        $destinationAbsolutePath = $PSCmdlet.GetUnresolvedProviderPathFromPSPath($DestinationPath)
        if (!(Test-Path -LiteralPath $destinationAbsolutePath)) {
            New-Item $destinationAbsolutePath -ItemType Directory > $null
        }

        # Prepare output object if user has specified the -PassThru parameter.
        if ($PassThru) {
            $InvokePlasterInfo = [PSCustomObject]@{
                TemplatePath = $templateAbsolutePath
                DestinationPath = $destinationAbsolutePath
                Success = $false
                TemplateType = if ($manifest.plasterManifest.templateType) {$manifest.plasterManifest.templateType} else {'Unspecified'}
                CreatedFiles = [string[]]@()
                UpdatedFiles = [string[]]@()
                MissingModules = [string[]]@()
                OpenFiles = [string[]]@()
            }
        }

        # Create the pre-defined Plaster variables.
        InitializePredefinedVariables $templateAbsolutePath $destinationAbsolutePath

        # Check for any existing default value store file and load default values if file exists.
        $templateId = $manifest.plasterManifest.metadata.id
        $templateVersion = $manifest.plasterManifest.metadata.version
        $templateName = $manifest.plasterManifest.metadata.name
        $storeFilename = "$templateName-$templateVersion-$templateId.clixml"
        $defaultValueStorePath = Join-Path $ParameterDefaultValueStoreRootPath $storeFilename
        if (Test-Path $defaultValueStorePath) {
            try {
                $PSCmdlet.WriteDebug("Loading default value store from '$defaultValueStorePath'.")
                $defaultValueStore = Import-Clixml $defaultValueStorePath -ErrorAction Stop
            }
            catch {
                Write-Warning ($LocalizedData.ErrorFailedToLoadStoreFile_F1 -f $defaultValueStorePath)
            }
        }

        function NewConstrainedRunspace() {
            $iss = [System.Management.Automation.Runspaces.InitialSessionState]::Create()
            if (!$IsCoreCLR) {
                $iss.ApartmentState = [System.Threading.ApartmentState]::STA
            }
            $iss.LanguageMode = [System.Management.Automation.PSLanguageMode]::ConstrainedLanguage
            $iss.DisableFormatUpdates = $true

            $sspe = New-Object System.Management.Automation.Runspaces.SessionStateProviderEntry 'Environment',([Microsoft.PowerShell.Commands.EnvironmentProvider]),$null
            $iss.Providers.Add($sspe)

            $sspe = New-Object System.Management.Automation.Runspaces.SessionStateProviderEntry 'FileSystem',([Microsoft.PowerShell.Commands.FileSystemProvider]),$null
            $iss.Providers.Add($sspe)

            $ssce = New-Object System.Management.Automation.Runspaces.SessionStateCmdletEntry 'Get-Content',([Microsoft.PowerShell.Commands.GetContentCommand]),$null
            $iss.Commands.Add($ssce)

            $ssce = New-Object System.Management.Automation.Runspaces.SessionStateCmdletEntry 'Get-Date',([Microsoft.PowerShell.Commands.GetDateCommand]),$null
            $iss.Commands.Add($ssce)

            $ssce = New-Object System.Management.Automation.Runspaces.SessionStateCmdletEntry 'Get-ChildItem',([Microsoft.PowerShell.Commands.GetChildItemCommand]),$null
            $iss.Commands.Add($ssce)

            $ssce = New-Object System.Management.Automation.Runspaces.SessionStateCmdletEntry 'Get-Item',([Microsoft.PowerShell.Commands.GetItemCommand]),$null
            $iss.Commands.Add($ssce)

            $ssce = New-Object System.Management.Automation.Runspaces.SessionStateCmdletEntry 'Get-ItemProperty',([Microsoft.PowerShell.Commands.GetItemPropertyCommand]),$null
            $iss.Commands.Add($ssce)

            $ssce = New-Object System.Management.Automation.Runspaces.SessionStateCmdletEntry 'Get-Module',([Microsoft.PowerShell.Commands.GetModuleCommand]),$null
            $iss.Commands.Add($ssce)

            $ssce = New-Object System.Management.Automation.Runspaces.SessionStateCmdletEntry 'Get-Variable',([Microsoft.PowerShell.Commands.GetVariableCommand]),$null
            $iss.Commands.Add($ssce)

            $ssce = New-Object System.Management.Automation.Runspaces.SessionStateCmdletEntry 'Test-Path',([Microsoft.PowerShell.Commands.TestPathCommand]),$null
            $iss.Commands.Add($ssce)

            $ssce = New-Object System.Management.Automation.Runspaces.SessionStateCmdletEntry 'Out-String',([Microsoft.PowerShell.Commands.OutStringCommand]),$null
            $iss.Commands.Add($ssce)

            $ssce = New-Object System.Management.Automation.Runspaces.SessionStateCmdletEntry 'Compare-Object',([Microsoft.PowerShell.Commands.CompareObjectCommand]),$null
            $iss.Commands.Add($ssce)

            $scopedItemOptions = [System.Management.Automation.ScopedItemOptions]::AllScope
            $plasterVars = Get-Variable -Name PLASTER_*,PSVersionTable
            if (Test-Path Variable:\IsLinux) {
                $plasterVars += Get-Variable -Name IsLinux
            }
            if (Test-Path Variable:\IsOSX) {
                $plasterVars += Get-Variable -Name IsOSX
            }
            if (Test-Path Variable:\IsMacOS) {
                $plasterVars += Get-Variable -Name IsMacOS
            }
            if (Test-Path Variable:\IsWindows) {
                $plasterVars += Get-Variable -Name IsWindows
            }
            foreach ($var in $plasterVars) {
                $ssve = New-Object System.Management.Automation.Runspaces.SessionStateVariableEntry `
                            $var.Name,$var.Value,$var.Description,$scopedItemOptions
                $iss.Variables.Add($ssve)
            }

            # Create new runspace with the above defined entries. Then open and set its working dir to $destinationAbsolutePath
            # so all condition attribute expressions can use a relative path to refer to file paths e.g.
            # condition="Test-Path src\${PLASTER_PARAM_ModuleName}.psm1"
            $runspace = [System.Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace($iss)
            $runspace.Open()
            if ($destinationAbsolutePath) {
                $runspace.SessionStateProxy.Path.SetLocation($destinationAbsolutePath) > $null
            }
            $runspace
        }

        function ExecuteExpressionImpl([string]$Expression) {
            try {
                $powershell = [PowerShell]::Create()

                if ($null -eq $constrainedRunspace) {
                    $constrainedRunspace = NewConstrainedRunspace
                }
                $powershell.Runspace = $constrainedRunspace

                try {
                    $powershell.AddScript($Expression) > $null
                    $res = $powershell.Invoke()
                    $res
                }
                catch {
                    throw ($LocalizedData.ExpressionInvalid_F2 -f $Expression,$_)
                }

                # Check for non-terminating errors.
                if ($powershell.Streams.Error.Count -gt 0) {
                    $err = $powershell.Streams.Error[0]
                    throw ($LocalizedData.ExpressionNonTermErrors_F2 -f $Expression,$err)
                }
            }
            finally {
                if ($powershell) {
                    $powershell.Dispose()
                }
            }
        }

        function InterpolateAttributeValue([string]$Value, [string]$Location) {
            if ($null -eq $Value) {
                return [string]::Empty
            }
            elseif ([string]::IsNullOrWhiteSpace($Value)) {
                return $Value
            }

            try {
                $res = @(ExecuteExpressionImpl "`"$Value`"")
                [string]$res[0]
            }
            catch {
                throw ($LocalizedData.InterpolationError_F3 -f $Value.Trim(),$Location,$_)
            }
        }

        function EvaluateConditionAttribute([string]$Expression, [string]$Location) {
            if ($null -eq $Expression) {
                return [string]::Empty
            }
            elseif ([string]::IsNullOrWhiteSpace($Expression)) {
                return $Expression
            }

            try {
                $res = @(ExecuteExpressionImpl $Expression)
                [bool]$res[0]
            }
            catch {
                throw ($LocalizedData.ExpressionInvalidCondition_F3 -f $Expression,$Location,$_)
            }
        }

        function EvaluateExpression([string]$Expression, [string]$Location) {
            if ($null -eq $Expression) {
                return [string]::Empty
            }
            elseif ([string]::IsNullOrWhiteSpace($Expression)) {
                return $Expression
            }

            try {
                $res = @(ExecuteExpressionImpl $Expression)
                [string]$res[0]
            }
            catch {
                throw ($LocalizedData.ExpressionExecError_F2 -f $Location,$_)
            }
        }

        function EvaluateScript([string]$Script, [string]$Location) {
            if ($null -eq $Script) {
                return @([string]::Empty)
            }
            elseif ([string]::IsNullOrWhiteSpace($Script)) {
                return $Script
            }

            try {
                $res = @(ExecuteExpressionImpl $Script)
                [string[]]$res
            }
            catch {
                throw ($LocalizedData.ExpressionExecError_F2 -f $Location,$_)
            }
        }

        function GetErrorLocationFileAttrVal([string]$ElementName, [string]$AttributeName) {
            $LocalizedData.ExpressionErrorLocationFile_F2 -f $ElementName,$AttributeName
        }

        function GetErrorLocationModifyAttrVal([string]$AttributeName) {
            $LocalizedData.ExpressionErrorLocationModify_F1 -f $AttributeName
        }

        function GetErrorLocationNewModManifestAttrVal([string]$AttributeName) {
            $LocalizedData.ExpressionErrorLocationNewModManifest_F1 -f $AttributeName
        }

        function GetErrorLocationParameterAttrVal([string]$ParameterName, [string]$AttributeName) {
            $LocalizedData.ExpressionErrorLocationParameter_F2 -f $ParameterName,$AttributeName
        }

        function GetErrorLocationRequireModuleAttrVal([string]$ModuleName, [string]$AttributeName) {
            $LocalizedData.ExpressionErrorLocationRequireModule_F2 -f $ModuleName,$AttributeName
        }

        function GetPSSnippetFunction([String]$FilePath) {
            # Test if Path Exists
            if (!(Test-Path $substitute -PathType Leaf)) {
                throw ($LocalizedData.ErrorPathDoesNotExist_F1 -f $FilePath)
            }
            # Load File
            return Get-Content -LiteralPath $substitute -Raw
        }

        function ConvertToDestinationRelativePath($Path) {
            $fullDestPath = $DestinationPath
            if (![System.IO.Path]::IsPathRooted($fullDestPath)) {
                $fullDestPath = $PSCmdlet.GetUnresolvedProviderPathFromPSPath($DestinationPath)
            }

            $fullPath = $PSCmdlet.GetUnresolvedProviderPathFromPSPath($Path)
            if (!$fullPath.StartsWith($fullDestPath, 'OrdinalIgnoreCase')) {
                throw ($LocalizedData.ErrorPathMustBeUnderDestPath_F2 -f $fullPath, $fullDestPath)
            }

            $fullPath.Substring($fullDestPath.Length).TrimStart('\','/')
        }

        function VerifyPathIsUnderDestinationPath([ValidateNotNullOrEmpty()][string]$FullPath) {
            if (![System.IO.Path]::IsPathRooted($FullPath)) {
                $PSCmdlet.WriteDebug("The FullPath parameter '$FullPath' must be an absolute path.")
            }

            $fullDestPath = $DestinationPath
            if (![System.IO.Path]::IsPathRooted($fullDestPath)) {
                $fullDestPath = $PSCmdlet.GetUnresolvedProviderPathFromPSPath($DestinationPath)
            }

            if (!$FullPath.StartsWith($fullDestPath, [StringComparison]::OrdinalIgnoreCase)) {
                throw ($LocalizedData.ErrorPathMustBeUnderDestPath_F2 -f $FullPath, $fullDestPath)
            }
        }

        function WriteContentWithEncoding([string]$path, [string[]]$content, [string]$encoding) {
            if ($encoding -match '-nobom') {
                $encoding,$dummy = $encoding -split '-'

                $noBomEncoding = $null
                switch ($encoding) {
                    'utf8' { $noBomEncoding = New-Object System.Text.UTF8Encoding($false) }
                }

                if ($null -eq $content) {
                    $content = [string]::Empty
                }

                [System.IO.File]::WriteAllLines($path, $content, $noBomEncoding)
            }
            else {
                Set-Content -LiteralPath $path -Value $content -Encoding $encoding
            }
        }

        function ColorForOperation($operation) {
            switch ($operation) {
                $LocalizedData.OpConflict      { 'Red' }
                $LocalizedData.OpCreate        { 'Green' }
                $LocalizedData.OpForce         { 'Yellow' }
                $LocalizedData.OpIdentical     { 'Cyan' }
                $LocalizedData.OpModify        { 'Magenta' }
                $LocalizedData.OpUpdate        { 'Green' }
                $LocalizedData.OpMissing       { 'Red' }
                $LocalizedData.OpVerify        { 'Green' }
                default { $Host.UI.RawUI.ForegroundColor }
            }
        }

        function GetMaxOperationLabelLength {
            ($LocalizedData.OpCreate,   $LocalizedData.OpIdentical,
             $LocalizedData.OpConflict, $LocalizedData.OpForce,
             $LocalizedData.OpMissing,  $LocalizedData.OpModify,
             $LocalizedData.OpUpdate,   $LocalizedData.OpVerify |
                 Measure-Object -Property Length -Maximum).Maximum
        }

        function WriteOperationStatus($operation, $message) {
            $maxLen = GetMaxOperationLabelLength
            Write-Host ("{0,$maxLen} " -f $operation) -ForegroundColor (ColorForOperation $operation) -NoNewline
            Write-Host $message
        }

        function WriteOperationAdditionalStatus([string[]]$Message) {
            $maxLen = GetMaxOperationLabelLength
            foreach ($msg in $Message) {
                $lines = $msg -split "`n"
                foreach ($line in $lines) {
                    Write-Host ("{0,$maxLen} {1}" -f "",$line)
                }
            }
        }

        function GetGitConfigValue($name) {
            # Very simplistic git config lookup
            # Won't work with namespace, just use final element, e.g. 'name' instead of 'user.name'

            # The $Home dir may not be reachable e.g. if on network share and/or script not running as admin.
            # See issue https://github.com/PowerShell/Plaster/issues/92
            if (!(Test-Path -LiteralPath $Home)) {
                return
            }

            $gitConfigPath = Join-Path $Home '.gitconfig'
            $PSCmdlet.WriteDebug("Looking for '$name' value in Git config: $gitConfigPath")

            if (Test-Path -LiteralPath $gitConfigPath) {
                $matches = Select-String -LiteralPath $gitConfigPath -Pattern "\s+$name\s+=\s+(.+)$"
                if (@($matches).Count -gt 0)
                {
                    $matches.Matches.Groups[1].Value
                }
            }
        }

        function PromptForInput($prompt, $default) {
            do {
                $value = Read-Host -Prompt $prompt
                if (!$value -and $default) {
                    $value = $default
                }
            } while (!$value)

            $value
        }

        function PromptForChoice([string]$ParameterName, [ValidateNotNull()]$ChoiceNodes, [string]$prompt,
                                 [int[]]$defaults, [switch]$IsMultiChoice) {
            $choices = New-Object 'System.Collections.ObjectModel.Collection[System.Management.Automation.Host.ChoiceDescription]'
            $values = New-Object object[] $ChoiceNodes.Count
            $i = 0

            foreach ($choiceNode in $ChoiceNodes) {
                $label = InterpolateAttributeValue $choiceNode.label (GetErrorLocationParameterAttrVal $ParameterName label)
                $help  = InterpolateAttributeValue $choiceNode.help  (GetErrorLocationParameterAttrVal $ParameterName help)
                $value = InterpolateAttributeValue $choiceNode.value (GetErrorLocationParameterAttrVal $ParameterName value)

                $choice = New-Object System.Management.Automation.Host.ChoiceDescription -Arg $label,$help
                $choices.Add($choice)
                $values[$i++] = $value
            }

            $retval = [PSCustomObject]@{Values=@(); Indices=@()}

            if ($IsMultiChoice) {
                $selections = $Host.UI.PromptForChoice('', $prompt, $choices, $defaults)
                foreach ($selection in $selections) {
                    $retval.Values += $values[$selection]
                    $retval.Indices += $selection
                }
            }
            else {
                if ($defaults.Count -gt 1) {
                    throw ($LocalizedData.ParameterTypeChoiceMultipleDefault_F1 -f $ChoiceNodes.ParentNode.name)
                }

                $selection = $Host.UI.PromptForChoice('', $prompt, $choices, $defaults[0])
                $retval.Values = $values[$selection]
                $retval.Indices = $selection
            }

            $retval
        }

        # All Plaster variables should be set via this method so that the ConstrainedRunspace can be
        # configured to use the new variable. This method will null out the ConstrainedRunspace so that
        # later, when we need to evaluate script in that runspace, it will get recreated first with all
        # the latest Plaster variables.
        function SetPlasterVariable() {
            param(
                [Parameter(Mandatory=$true)]
                [ValidateNotNullOrEmpty()]
                [string]$Name,

                [Parameter(Mandatory=$true)]
                [AllowNull()]
                $Value,

                [Parameter()]
                [bool]
                $IsParam = $true
            )

            # Variables created from a <parameter> in the Plaster manifset are prefixed PLASTER_PARAM all others
            # are just PLASTER_.
            $variableName = if ($IsParam) { "PLASTER_PARAM_$Name" } else { "PLASTER_$Name" }

            Set-Variable -Name $variableName -Value $Value -Scope Script -WhatIf:$false

            # If the constrained runspace has been created, it needs to be disposed so that the next string
            # expansion (or condition eval) gets an updated runspace that contains this variable or its new value.
            if ($null -ne $script:ConstrainedRunspace) {
                $script:ConstrainedRunspace.Dispose()
                $script:ConstrainedRunspace = $null
            }
        }

        function ProcessParameter([ValidateNotNull()]$Node) {
            $name = $Node.name
            $type = $Node.type
            $store = $Node.store

            $condition = $Node.condition

            $default = InterpolateAttributeValue $Node.default (GetErrorLocationParameterAttrVal $name default)

            if ($condition -and !(EvaluateConditionAttribute $condition "'<$($Node.LocalName)>'")) {
                if (-not [string]::IsNullOrEmpty($default) -and $type -eq 'text') {
                    SetPlasterVariable -Name $name -Value $default -IsParam $true
                    $PSCmdlet.WriteDebug("The condition of the parameter $($name) with the type 'text' evaluated to false. The parameter has a default value which will be used.")
                }
                else {
                    # Define the parameter so later conditions can use it but its value will be $null
                    SetPlasterVariable -Name $name -Value $null -IsParam $true
                    $PSCmdlet.WriteDebug("Skipping parameter $($name), condition evaluated to false.")
                }

                return
            }

            $prompt = InterpolateAttributeValue $Node.prompt (GetErrorLocationParameterAttrVal $name prompt)

            # Check if parameter was provided via a dynamic parameter.
            if ($boundParameters.ContainsKey($name)) {
                $value = $boundParameters[$name]
            }
            else {
                # Not a dynamic parameter so prompt user for the value but first check for a stored default value.
                if ($store -and ($null -ne $defaultValueStore[$name])) {
                    $default = $defaultValueStore[$name]
                    $PSCmdlet.WriteDebug("Read default value '$default' for parameter '$name' from default value store.")

                    if (($store -eq 'encrypted') -and ($default -is [System.Security.SecureString])) {
                        try {
                            $cred = New-Object -TypeName PSCredential -ArgumentList 'jsbplh',$default
                            $default = $cred.GetNetworkCredential().Password
                            $PSCmdlet.WriteDebug("Unencrypted default value for parameter '$name'.")
                        }
                        catch [System.Exception] {
                            Write-Warning ($LocalizedData.ErrorUnencryptingSecureString_F1 -f $name)
                        }
                    }
                }

                # If the prompt message failed to evaluate or was empty, supply a diagnostic prompt message
                if (!$prompt) {
                    $prompt = $LocalizedData.MissingParameterPrompt_F1 -f $name
                }

                # Some default values might not come from the template e.g. some are harvested from .gitconfig if it exists.
                $defaultNotFromTemplate = $false

                # Now prompt user for parameter value based on the parameter type.
                switch -regex ($type) {
                    'text' {
                        # Display an appropriate "default" value in the prompt string.
                        if ($default) {
                            if ($store -eq 'encrypted') {
                                $obscuredDefault = $default -replace '(....).*', '$1****'
                                $prompt += " ($obscuredDefault)"
                            }
                            else {
                                $prompt += " ($default)"
                            }
                        }
                        # Prompt the user for text input.
                        $value = PromptForInput $prompt $default
                        $valueToStore = $value
                    }
                    'user-fullname' {
                        # If no default, try to get a name from git config.
                        if (!$default) {
                            $default = GetGitConfigValue('name')
                            $defaultNotFromTemplate = $true
                        }

                        if ($default) {
                            if ($store -eq 'encrypted') {
                                $obscuredDefault = $default -replace '(....).*', '$1****'
                                $prompt += " ($obscuredDefault)"
                            }
                            else {
                                $prompt += " ($default)"
                            }
                        }

                        # Prompt the user for text input.
                        $value = PromptForInput $prompt $default
                        $valueToStore = $value
                    }
                    'user-email' {
                        # If no default, try to get an email from git config
                        if (-not $default) {
                            $default = GetGitConfigValue('email')
                            $defaultNotFromTemplate = $true
                        }

                        if ($default) {
                            if ($store -eq 'encrypted') {
                                $obscuredDefault = $default -replace '(....).*', '$1****'
                                $prompt += " ($obscuredDefault)"
                            }
                            else {
                                $prompt += " ($default)"
                            }
                        }

                        # Prompt the user for text input.
                        $value = PromptForInput $prompt $default
                        $valueToStore = $value
                    }
                    'choice|multichoice' {
                        $choices = $Node.ChildNodes
                        $defaults = [int[]]($default -split ',')

                        # Prompt the user for choice or multichoice selection input.
                        $selections = PromptForChoice $name $choices $prompt $defaults -IsMultiChoice:($type -eq 'multichoice')
                        $value = $selections.Values
                        $OFS = ","
                        $valueToStore = "$($selections.Indices)"
                    }
                    default  { throw ($LocalizedData.UnrecognizedParameterType_F2 -f $type, $Node.LocalName) }
                }

                # If parameter specifies that user's input be stored as the default value,
                # store it to file if the value has changed.
                if ($store -and (($default -ne $valueToStore) -or $defaultNotFromTemplate)) {
                    if ($store -eq 'encrypted') {
                        $PSCmdlet.WriteDebug("Storing new, encrypted default value for parameter '$name' to default value store.")
                        $defaultValueStore[$name] = ConvertTo-SecureString -String $valueToStore -AsPlainText -Force
                    }
                    else {
                        $PSCmdlet.WriteDebug("Storing new default value '$valueToStore' for parameter '$name' to default value store.")
                        $defaultValueStore[$name] = $valueToStore
                    }

                    $flags.DefaultValueStoreDirty = $true
                }
            }

            # Make template defined parameters available as a PowerShell variable PLASTER_PARAM_<parameterName>.
            SetPlasterVariable -Name $name -Value $value -IsParam $true
        }

        function ProcessMessage([ValidateNotNull()]$Node) {
            $text = InterpolateAttributeValue $Node.InnerText '<message>'
            $nonewline = $Node.nonewline -eq 'true'

            # Eliminate whitespace before and after the text that just happens to get inserted because you want
            # the text on different lines than the start/end element tags.
            $trimmedText = $text -replace '^[ \t]*\n','' -replace '\n[ \t]*$',''

            $condition  = $Node.condition
            if ($condition -and !(EvaluateConditionAttribute $condition "'<$($Node.LocalName)>'")) {
                $debugText = $trimmedText -replace '\r|\n',' '
                $maxLength = [Math]::Min(40, $debugText.Length)
                $PSCmdlet.WriteDebug("Skipping message '$($debugText.Substring(0, $maxLength))', condition evaluated to false.")
                return
            }

            Write-Host $trimmedText -NoNewline:($nonewline -eq 'true')
        }

        function ProcessNewModuleManifest([ValidateNotNull()]$Node) {
            $moduleVersion = InterpolateAttributeValue $Node.moduleVersion (GetErrorLocationNewModManifestAttrVal moduleVersion)
            $rootModule = InterpolateAttributeValue $Node.rootModule (GetErrorLocationNewModManifestAttrVal rootModule)
            $author = InterpolateAttributeValue $Node.author (GetErrorLocationNewModManifestAttrVal author)
            $companyName = InterpolateAttributeValue $Node.companyName (GetErrorLocationNewModManifestAttrVal companyName)
            $description = InterpolateAttributeValue $Node.description (GetErrorLocationNewModManifestAttrVal description)
            $dstRelPath = InterpolateAttributeValue $Node.destination (GetErrorLocationNewModManifestAttrVal destination)
            $powerShellVersion = InterpolateAttributeValue $Node.powerShellVersion (GetErrorLocationNewModManifestAttrVal powerShellVersion)
            $nestedModules = InterpolateAttributeValue $Node.NestedModules (GetErrorLocationNewModManifestAttrVal NestedModules)
            $dscResourcesToExport = InterpolateAttributeValue $Node.DscResourcesToExport (GetErrorLocationNewModManifestAttrVal DscResourcesToExport)
            $copyright = InterpolateAttributeValue $Node.copyright (GetErrorLocationNewModManifestAttrVal copyright)

            # We could choose to not check this if the condition eval'd to false
            # but I think it is better to let the template author know they've broken the
            # rules for any of the file directives (not just the ones they're testing/enabled).
            if ([System.IO.Path]::IsPathRooted($dstRelPath)) {
                throw ($LocalizedData.ErrorPathMustBeRelativePath_F2 -f $dstRelPath,$Node.LocalName)
            }

            $dstPath = $PSCmdlet.GetUnresolvedProviderPathFromPSPath((Join-Path $DestinationPath $dstRelPath))

            $condition  = $Node.condition
            if ($condition -and !(EvaluateConditionAttribute $condition "'<$($Node.LocalName)>'")) {
                $PSCmdlet.WriteDebug("Skipping module manifest generation for '$dstPath', condition evaluated to false.")
                return
            }

            $encoding = $Node.encoding
            if (!$encoding) {
                $encoding = $DefaultEncoding
            }

            if ($PSCmdlet.ShouldProcess($dstPath, $LocalizedData.ShouldProcessNewModuleManifest)) {
                $manifestDir = Split-Path $dstPath -Parent
                if (!(Test-Path $manifestDir)) {
                    VerifyPathIsUnderDestinationPath $manifestDir
                    Write-Verbose ($LocalizedData.NewModManifest_CreatingDir_F1 -f $manifestDir)
                    New-Item $manifestDir -ItemType Directory > $null
                }

                $newModuleManifestParams = @{}

                # If there is an existing module manifest, load it so we can reuse old values not specified by
                # template.
                if (Test-Path -LiteralPath $dstPath) {
                    $manifestFileName = Split-Path $dstPath -leaf
                    $newModuleManifestParams = Import-LocalizedData -BaseDirectory $manifestDir -FileName $manifestFileName
                    if ($newModuleManifestParams.PrivateData) {
                        $newModuleManifestParams += $newModuleManifestParams.PrivateData.psdata
                        $newModuleManifestParams.Remove('PrivateData')
                    }
                }

                if (![string]::IsNullOrWhiteSpace($moduleVersion)) {
                    $newModuleManifestParams['ModuleVersion'] = $moduleVersion
                }
                if (![string]::IsNullOrWhiteSpace($rootModule)) {
                    $newModuleManifestParams['RootModule'] = $rootModule
                }
                if (![string]::IsNullOrWhiteSpace($author)) {
                    $newModuleManifestParams['Author'] = $author
                }
                if (![string]::IsNullOrWhiteSpace($companyName)) {
                    $newModuleManifestParams['CompanyName'] = $companyName
                }
                if (![string]::IsNullOrWhiteSpace($copyright)) {
                    $newModuleManifestParams['Copyright'] = $copyright
                }
                if (![string]::IsNullOrWhiteSpace($description)) {
                    $newModuleManifestParams['Description'] = $description
                }
                if (![string]::IsNullOrWhiteSpace($powerShellVersion)) {
                    $newModuleManifestParams['PowerShellVersion'] = $powerShellVersion
                }
                if (![string]::IsNullOrWhiteSpace($nestedModules)) {
                    $newModuleManifestParams['NestedModules'] = $nestedModules
                }
                if (![string]::IsNullOrWhiteSpace($dscResourcesToExport)) {
                    $newModuleManifestParams['DscResourcesToExport'] = $dscResourcesToExport
                }

                $tempFile = $null

                try {
                    $tempFileBaseName = "moduleManifest-" + [Guid]::NewGuid()
                    $tempFile = [System.IO.Path]::GetTempPath() + "${tempFileBaseName}.psd1"
                    $PSCmdlet.WriteDebug("Created temp file for new module manifest - $tempFile")
                    $newModuleManifestParams['Path'] = $tempFile

                    # Generate manifest into a temp file.
                    New-ModuleManifest @newModuleManifestParams

                    # Typically the manifest is re-written with a new encoding (UTF8-NoBOM) because Git hates UTF-16.
                    $content = Get-Content -LiteralPath $tempFile -Raw

                    # Replace the temp filename in the generated manifest file's comment header with the actual filename.
                    $dstBaseName = [System.IO.Path]::GetFileNameWithoutExtension($dstPath)
                    $content = $content -replace "(?<=\s*#.*?)$tempFileBaseName", $dstBaseName

                    WriteContentWithEncoding -Path $tempFile -Content $content -Encoding $encoding

                    CopyFileWithConflictDetection $tempFile $dstPath

                    if ($PassThru -and ($Node.openInEditor -eq 'true')) {
                        $InvokePlasterInfo.OpenFiles += $dstPath
                    }
                }
                finally {
                    if ($tempFile -and (Test-Path $tempFile)) {
                        Remove-Item -LiteralPath $tempFile
                        $PSCmdlet.WriteDebug("Removed temp file for new module manifest - $tempFile")
                    }
                }
            }
        }

        #
        # Begin ProcessFile helper methods
        #
        function NewBackupFilename([string]$Path) {
            $dir = [System.IO.Path]::GetDirectoryName($Path)
            $filename = [System.IO.Path]::GetFileName($Path)
            $backupPath = Join-Path -Path $dir -ChildPath "${filename}.bak"
            $i = 1;
            while (Test-Path -LiteralPath $backupPath) {
                $backupPath = Join-Path -Path $dir -ChildPath "${filename}.bak$i"
                $i++
            }

            $backupPath
        }

        function AreFilesIdentical($Path1, $Path2) {
            $file1 = Get-Item -LiteralPath $Path1 -Force
            $file2 = Get-Item -LiteralPath $Path2 -Force

            if ($file1.Length -ne $file2.Length) {
                return $false
            }

            $hash1 = (Get-FileHash -LiteralPath $path1 -Algorithm SHA1).Hash
            $hash2 = (Get-FileHash -LiteralPath $path2 -Algorithm SHA1).Hash

            $hash1 -eq $hash2
        }

        function NewFileSystemCopyInfo([string]$srcPath, [string]$dstPath) {
            [PSCustomObject]@{SrcFileName=$srcPath; DstFileName=$dstPath}
        }

        function ExpandFileSourceSpec([string]$srcRelPath, [string]$dstRelPath) {
            $srcPath = Join-Path $templateAbsolutePath $srcRelPath
            $dstPath = Join-Path $destinationAbsolutePath $dstRelPath

            if ($srcRelPath.IndexOfAny([char[]]('*','?')) -lt 0) {
                # No wildcard spec in srcRelPath so return info on single file.
                # Also, if dstRelPath is empty, then use source rel path.
                if (!$dstRelPath) {
                    $dstPath = Join-Path $destinationAbsolutePath $srcRelPath
                }

                return NewFileSystemCopyInfo $srcPath $dstPath
            }

            # Prepare parameter values for call to Get-ChildItem to get list of files based on wildcard spec.
            $gciParams = @{}
            $parent = Split-Path $srcPath -Parent
            $leaf = Split-Path $srcPath -Leaf
            $gciParams['LiteralPath'] = $parent
            $gciParams['File'] = $true

            if ($leaf -eq '**') {
                $gciParams['Recurse'] = $true
            }
            else {
                if ($leaf.IndexOfAny([char[]]('*','?')) -ge 0) {
                    $gciParams['Filter'] = $leaf
                }

                $leaf = Split-Path $parent -Leaf
                if ($leaf -eq '**') {
                    $parent = Split-Path $parent -Parent
                    $gciParams['LiteralPath'] = $parent
                    $gciParams['Recurse'] = $true
                }
            }

            $srcRelRootPathLength = $gciParams['LiteralPath'].Length

            # Generate a FileCopyInfo object for every file expanded by the wildcard spec.
            $files = @(Microsoft.PowerShell.Management\Get-ChildItem @gciParams)
            foreach ($file in $files) {
                $fileSrcPath = $file.FullName
                $relPath = $fileSrcPath.Substring($srcRelRootPathLength)
                $fileDstPath = Join-Path $dstPath $relPath
                NewFileSystemCopyInfo $fileSrcPath $fileDstPath
            }

            # Copy over empty directories - if any.
            $gciParams.Remove('File')
            $gciParams['Directory'] = $true
            $dirs = @(Microsoft.PowerShell.Management\Get-ChildItem @gciParams |
                          Where-Object {$_.GetFileSystemInfos().Length -eq 0})
            foreach ($dir in $dirs) {
                $dirSrcPath = $dir.FullName
                $relPath = $dirSrcPath.Substring($srcRelRootPathLength)
                $dirDstPath = Join-Path $dstPath $relPath
                NewFileSystemCopyInfo $dirSrcPath $dirDstPath
            }
        }

        # Plaster zen for file handling.  All file related operations should use this method
        # to actually write/overwrite/modify files in the DestinationPath.  This method
        # handles detecting conflicts, gives the user a chance to determine how to handle
        # conflicts.  The user can choose to use the Force parameter to force the overwriting
        # of existing files at the destination path.
        # File processing (expanding substitution variable, modifying file contents) should always
        # be done to a temp file (be sure to always remove temp file when done).  That temp file
        # is what gets passed to this function as the $SrcPath.  This allows Plaster to alert the
        # user when the repeated application of a template will modify any existing file.
        # NOTE: Plaster keeps track of which files it has "created" (as opposed to overwritten)
        # so that any later change to that file doesn't trigger conflict handling.
        function CopyFileWithConflictDetection([string]$SrcPath, [string]$DstPath) {
            # Just double-checking that DstPath parameter is an absolute path otherwise
            # it could fail the check that the DstPath is under the overall DestinationPath.
            if (![System.IO.Path]::IsPathRooted($DstPath)) {
                $DstPath = $PSCmdlet.GetUnresolvedProviderPathFromPSPath($DstPath)
            }

            # Check if DstPath file conflicts with an existing SrcPath file.
            $operation = $LocalizedData.OpCreate
            $opmessage = (ConvertToDestinationRelativePath $DstPath)
            if (Test-Path -LiteralPath $DstPath) {
                if (AreFilesIdentical $SrcPath $DstPath) {
                    $operation = $LocalizedData.OpIdentical
                }
                elseif ($templateCreatedFiles.ContainsKey($DstPath)) {
                    # Plaster created this file previously during template invocation
                    # therefore, there is no conflict.  We're simply updating the file.
                    $operation = $LocalizedData.OpUpdate
                }
                elseif ($Force) {
                    $operation = $LocalizedData.OpForce
                }
                else  {
                    $operation = $LocalizedData.OpConflict
                }
            }

            # Copy the file to the destination
            if ($PSCmdlet.ShouldProcess($DstPath, $operation)) {
                WriteOperationStatus $operation $opmessage

                if ($operation -eq $LocalizedData.OpIdentical) {
                    # If the files are identical, no need to do anything
                    return
                }

                if (($operation -eq $LocalizedData.OpCreate) -or ($operation -eq $LocalizedData.OpUpdate)) {
                    Copy-Item -LiteralPath $SrcPath -Destination $DstPath
                    if ($PassThru) {
                        $InvokePlasterInfo.CreatedFiles += $DstPath
                    }
                    $templateCreatedFiles[$DstPath] = $null
                }
                elseif ($Force -or $PSCmdlet.ShouldContinue(($LocalizedData.OverwriteFile_F1 -f $DstPath),
                                                             $LocalizedData.FileConflict,
                                                             [ref]$fileConflictConfirmYesToAll,
                                                             [ref]$fileConflictConfirmNoToAll)) {
                    $backupFilename = NewBackupFilename $DstPath
                    Copy-Item -LiteralPath $DstPath -Destination $backupFilename
                    Copy-Item -LiteralPath $SrcPath -Destination $DstPath
                    if ($PassThru) {
                        $InvokePlasterInfo.UpdatedFiles += $DstPath
                    }
                    $templateCreatedFiles[$DstPath] = $null
                }
            }
        }

        #
        # End ProcessFile helper methods
        #

        # Processes both the <file> and <templateFile> directives.
        function ProcessFile([ValidateNotNull()]$Node) {
            $srcRelPath = InterpolateAttributeValue $Node.source (GetErrorLocationFileAttrVal $Node.localName source)
            $dstRelPath = InterpolateAttributeValue $Node.destination (GetErrorLocationFileAttrVal $Node.localName destination)

            $condition  = $Node.condition
            if ($condition -and !(EvaluateConditionAttribute $condition "'<$($Node.LocalName)>'")) {
                $PSCmdlet.WriteDebug("Skipping $($Node.localName) '$srcRelPath' -> '$dstRelPath', condition evaluated to false.")
                return
            }

            # Only validate paths for conditions that evaluate to true.
            # The path may not be valid if it evaluates to false depending
            # on whether or not conditional parameters are used in the template.
            if ([System.IO.Path]::IsPathRooted($srcRelPath)) {
                throw ($LocalizedData.ErrorPathMustBeRelativePath_F2 -f $srcRelPath,$Node.LocalName)
            }

            if ([System.IO.Path]::IsPathRooted($dstRelPath)) {
                throw ($LocalizedData.ErrorPathMustBeRelativePath_F2 -f $dstRelPath,$Node.LocalName)
            }

            # Check if node is the specialized, <templateFile> node.
            # Only <templateFile> nodes expand templates and use the encoding attribute.
            $isTemplateFile = $Node.localName -eq 'templateFile'
            if ($isTemplateFile) {
                $encoding = $Node.encoding
                if (!$encoding) {
                    $encoding = $DefaultEncoding
                }
            }

            # Check if source specifies a wildcard and if so, expand the wildcard
            # and then process each file system object (file or empty directory).
            $fileSystemCopyInfoObjs = ExpandFileSourceSpec $srcRelPath $dstRelPath
            foreach ($fileSystemCopyInfo in $fileSystemCopyInfoObjs) {
                $srcPath = $fileSystemCopyInfo.SrcFileName
                $dstPath = $fileSystemCopyInfo.DstFileName

                # The file's destination path must be under the DestinationPath specified by the user.
                VerifyPathIsUnderDestinationPath $dstPath

                # Check to see if we're copying an empty dir
                if (Test-Path -LiteralPath $srcPath -PathType Container) {
                    if (!(Test-Path -LiteralPath $dstPath)) {
                        if ($PSCmdlet.ShouldProcess($parentDir, $LocalizedData.ShouldProcessCreateDir)) {
                            WriteOperationStatus $LocalizedData.OpCreate `
                                ($dstRelPath.TrimEnd(([char]'\'),([char]'/')) + [System.IO.Path]::DirectorySeparatorChar)
                            New-Item -Path $dstPath -ItemType Directory > $null
                        }
                    }

                    continue
                }

                # If the file's parent dir doesn't exist, create it.
                $parentDir = Split-Path $dstPath -Parent
                if (!(Test-Path -LiteralPath $parentDir)) {
                    if ($PSCmdlet.ShouldProcess($parentDir, $LocalizedData.ShouldProcessCreateDir)) {
                        New-Item -Path $parentDir -ItemType Directory > $null
                    }
                }

                $tempFile = $null

                try {
                    # If processing a <templateFile>, copy to a temp file to expand the template file,
                    # then apply the normal file conflict detection/resolution handling.
                    $target = $LocalizedData.TempFileTarget_F1 -f (ConvertToDestinationRelativePath $dstPath)
                    if ($isTemplateFile -and $PSCmdlet.ShouldProcess($target, $LocalizedData.ShouldProcessExpandTemplate)) {
                        $content = Get-Content -LiteralPath $srcPath -Raw

                        # Eval script expression delimiters
                        if ($content -and ($content.Count -gt 0)) {
                            $newContent = [regex]::Replace($content, '(<%=)(.*?)(%>)', {
                                param($match)
                                $expr = $match.groups[2].value
                                $res = EvaluateExpression $expr "templateFile '$srcRelPath'"
                                $PSCmdlet.WriteDebug("Replacing '$expr' with '$res' in contents of template file '$srcPath'")
                                $res
                            },  @('IgnoreCase'))

                            # Eval script block delimiters
                            $newContent = [regex]::Replace($newContent, '(^<%)(.*?)(^%>)', {
                                param($match)
                                $expr = $match.groups[2].value
                                $res = EvaluateScript $expr "templateFile '$srcRelPath'"
                                $res = $res -join [System.Environment]::NewLine
                                $PSCmdlet.WriteDebug("Replacing '$expr' with '$res' in contents of template file '$srcPath'")
                                $res
                            },  @('IgnoreCase', 'SingleLine', 'MultiLine'))

                            $srcPath = $tempFile = [System.IO.Path]::GetTempFileName()
                            $PSCmdlet.WriteDebug("Created temp file for expanded templateFile - $tempFile")

                            WriteContentWithEncoding -Path $tempFile -Content $newContent -Encoding $encoding
                        }
                        else {
                            $PSCmdlet.WriteDebug("Skipping template file expansion for $($Node.localName) '$srcPath', file is empty.")
                        }
                    }

                    CopyFileWithConflictDetection $srcPath $dstPath

                    if ($PassThru -and ($Node.openInEditor -eq 'true')) {
                        $InvokePlasterInfo.OpenFiles += $dstPath
                    }
                }
                finally {
                    if ($tempFile -and (Test-Path $tempFile)) {
                        Remove-Item -LiteralPath $tempFile
                        $PSCmdlet.WriteDebug("Removed temp file for expanded templateFile - $tempFile")
                    }
                }
            }
        }

        function ProcessModifyFile([ValidateNotNull()]$Node) {
            $path = InterpolateAttributeValue $Node.path (GetErrorLocationModifyAttrVal path)

            # We could choose to not check this if the condition eval'd to false
            # but I think it is better to let the template author know they've broken the
            # rules for any of the file directives (not just the ones they're testing/enabled).
            if ([System.IO.Path]::IsPathRooted($path)) {
                throw ($LocalizedData.ErrorPathMustBeRelativePath_F2 -f $path,$Node.LocalName)
            }

            $filePath = $PSCmdlet.GetUnresolvedProviderPathFromPSPath((Join-Path $DestinationPath $path))

            # The file's path must be under the DestinationPath specified by the user.
            VerifyPathIsUnderDestinationPath $filePath

            $condition = $Node.condition
            if ($condition -and !(EvaluateConditionAttribute $condition "'<$($Node.LocalName)>'")) {
                $PSCmdlet.WriteDebug("Skipping $($Node.LocalName) of '$filePath', condition evaluated to false.")
                return
            }

            $fileContent = [string]::Empty
            if (Test-Path -LiteralPath $filePath) {
                $fileContent = Get-Content -LiteralPath $filePath -Raw
            }

            # Set a Plaster (non-parameter) variable in this and the constrained runspace.
            SetPlasterVariable -Name FileContent -Value $fileContent -IsParam $false

            $encoding = $Node.encoding
            if (!$encoding) {
                $encoding = $DefaultEncoding
            }

            # If processing a <modify> directive, write the modified contents to a temp file,
            # then apply the normal file conflict detection/resolution handling.
            $target = $LocalizedData.TempFileTarget_F1 -f $filePath
            if ($PSCmdlet.ShouldProcess($target, $LocalizedData.OpModify)) {
                WriteOperationStatus $LocalizedData.OpModify ($LocalizedData.TempFileOperation_F1 -f (ConvertToDestinationRelativePath $filePath))

                $modified = $false

                foreach ($childNode in $Node.ChildNodes) {
                    if ($childNode -isnot [System.Xml.XmlElement]) { continue }

                    switch ($childNode.LocalName) {
                        'replace' {
                            $condition  = $childNode.condition
                            if ($condition -and !(EvaluateConditionAttribute $condition "'<$($Node.LocalName)><$($childNode.LocalName)>'")) {
                                $PSCmdlet.WriteDebug("Skipping $($Node.LocalName) $($childNode.LocalName) of '$filePath', condition evaluated to false.")
                                continue
                            }

                            if ($childNode.original -is [string]) {
                                $original = $childNode.original
                            }
                            else {
                                $original = $childNode.original.InnerText
                            }

                            if ($childNode.original.expand -eq 'true') {
                                $original = InterpolateAttributeValue $original (GetErrorLocationModifyAttrVal original)
                            }

                            if ($childNode.substitute -is [string]) {
                                $substitute = $childNode.substitute
                            }
                            else {
                                $substitute = $childNode.substitute.InnerText
                            }

                            if ($childNode.substitute.isFile -eq 'true') {
                                $substitute = GetPSSnippetFunction $substitute
                            } elseif ($childNode.substitute.expand -eq 'true') {
                                $substitute = InterpolateAttributeValue $substitute (GetErrorLocationModifyAttrVal substitute)
                            }

                            # Perform Literal Replacement on FileContent (since it will have regex characters)
                            if ($childNode.substitute.isFile) {
                                $fileContent = $fileContent.Replace($original,$substitute)
                            } else {
                                $fileContent = $fileContent -replace $original,$substitute
                            }

                            # Update the Plaster (non-parameter) variable's value in this and the constrained runspace.
                            SetPlasterVariable -Name FileContent -Value $fileContent -IsParam $false

                            $modified = $true
                        }
                        default { throw ($LocalizedData.UnrecognizedContentElement_F1 -f $childNode.LocalName) }
                    }
                }

                $tempFile = $null

                try {
                    # We could use CopyFileWithConflictDetection to handle the "identical" (not modified) case
                    # but if nothing was changed, I'd prefer not to generate a temp file, copy the unmodified contents
                    # into that temp file with hopefully the right encoding and then potentially overwrite the original file
                    # (different encoding will make the files look different) with the same contents but different encoding.
                    # If the intent of the <modify> was simply to change an existing file's encoding then the directive will
                    # need to make a whitespace change to the file.
                    if ($modified) {
                        $tempFile = [System.IO.Path]::GetTempFileName()
                        $PSCmdlet.WriteDebug("Created temp file for modified file - $tempFile")

                        WriteContentWithEncoding -Path $tempFile -Content $PLASTER_FileContent -Encoding $encoding
                        CopyFileWithConflictDetection $tempFile $filePath

                        if ($PassThru -and ($Node.openInEditor -eq 'true')) {
                            $InvokePlasterInfo.OpenFiles += $filePath
                        }
                    }
                    else {
                        WriteOperationStatus $LocalizedData.OpIdentical (ConvertToDestinationRelativePath $filePath)
                    }
                }
                finally {
                    if ($tempFile -and (Test-Path $tempFile)) {
                        Remove-Item -LiteralPath $tempFile
                        $PSCmdlet.WriteDebug("Removed temp file for modified file - $tempFile")
                    }
                }
            }
        }

        function ProcessRequireModule([ValidateNotNull()]$Node) {
            $name = $Node.name

            $condition = $Node.condition
            if ($condition -and !(EvaluateConditionAttribute $condition "'<$($Node.LocalName)>'")) {
                $PSCmdlet.WriteDebug("Skipping $($Node.localName) for module '$name', condition evaluated to false.")
                return
            }

            $message = InterpolateAttributeValue $Node.message (GetErrorLocationRequireModuleAttrVal $name message)
            $minimumVersion = $Node.minimumVersion
            $maximumVersion = $Node.maximumVersion
            $requiredVersion = $Node.requiredVersion

            $getModuleParams = @{
                ListAvailable = $true
                ErrorAction = 'SilentlyContinue'
            }

            # Configure $getModuleParams with correct parameters based on parameterset to be used.
            # Also construct an array of version strings that can be displayed to the user.
            $versionInfo = @()
            if ($requiredVersion) {
                $getModuleParams["FullyQualifiedName"] = @{ModuleName = $name; RequiredVersion = $requiredVersion}
                $versionInfo += $LocalizedData.RequireModuleRequiredVersion_F1 -f $requiredVersion
            }
            elseif ($minimumVersion -or $maximumVersion) {
                $getModuleParams["FullyQualifiedName"] = @{ModuleName = $name}

                if ($minimumVersion) {
                    $getModuleParams.FullyQualifiedName["ModuleVersion"] = $minimumVersion
                    $versionInfo += $LocalizedData.RequireModuleMinVersion_F1 -f $minimumVersion
                }
                if ($maximumVersion) {
                    $getModuleParams.FullyQualifiedName["MaximumVersion"] = $maximumVersion
                    $versionInfo += $LocalizedData.RequireModuleMaxVersion_F1 -f $maximumVersion
                }
            }
            else {
                $getModuleParams["Name"] = $name
            }

            # Flatten array of version strings into a single string.
            $versionRequirements = ""
            if ($versionInfo.Length -gt 0) {
                $OFS = ", "
                $versionRequirements = " ($versionInfo)"
            }

            # PowerShell v3 Get-Module command does not have the FullyQualifiedName parameter.
            if ($PSVersionTable.PSVersion.Major -lt 4) {
                $getModuleParams.Remove("FullyQualifiedName")
                $getModuleParams["Name"] = $name
            }

            $module = Get-Module @getModuleParams

            $moduleDesc = if ($versionRequirements) { "${name}:$versionRequirements" } else { $name }

            if ($null -eq $module) {
                WriteOperationStatus $LocalizedData.OpMissing ($LocalizedData.RequireModuleMissing_F2 -f $name,$versionRequirements)
                if ($message) {
                    WriteOperationAdditionalStatus $message
                }
                if ($PassThru) {
                    $InvokePlasterInfo.MissingModules += $moduleDesc
                }
            }
            else {
                if ($PSVersionTable.PSVersion.Major -gt 3) {
                    WriteOperationStatus $LocalizedData.OpVerify ($LocalizedData.RequireModuleVerified_F2 -f $name,$versionRequirements)
                }
                else {
                    # On V3, we have to the version matching with the results that Get-Module return.
                    $installedVersion = $module | Sort-Object Version -Descending | Select-Object -First 1 | Foreach-Object Version
                    if ($installedVersion.Build -eq -1) {
                        $installedVersion = [System.Version]"${installedVersion}.0.0"
                    }
                    elseif ($installedVersion.Revision -eq -1) {
                        $installedVersion = [System.Version]"${installedVersion}.0"
                    }

                    if (($requiredVersion -and ($installedVersion -ne $requiredVersion)) -or
                        ($minimumVersion -and ($installedVersion -lt $minimumVersion)) -or
                        ($maximumVersion -and ($installedVersion -gt $maximumVersion))) {

                        WriteOperationStatus $LocalizedData.OpMissing ($LocalizedData.RequireModuleMissing_F2 -f $name,$versionRequirements)
                        if ($PassThru) {
                            $InvokePlasterInfo.MissingModules += $moduleDesc
                        }
                    }
                    else {
                        WriteOperationStatus $LocalizedData.OpVerify ($LocalizedData.RequireModuleVerified_F2 -f $name,$versionRequirements)
                    }
                }
            }
        }
    }

    end {
        try {
            # Process parameters
            foreach ($node in $manifest.plasterManifest.parameters.ChildNodes) {
                if ($node -isnot [System.Xml.XmlElement]) { continue }
                switch ($node.LocalName) {
                    'parameter'  { ProcessParameter $node }
                    default      { throw ($LocalizedData.UnrecognizedParametersElement_F1 -f $node.LocalName) }
                }
            }

            # Outputs the processed template parameters to the debug stream
            $parameters = Get-Variable -Name PLASTER_* | Out-String
            $PSCmdlet.WriteDebug("Parameter values are:`n$($parameters -split "`n")")

            # Stores any updated default values back to the store file.
            if ($flags.DefaultValueStoreDirty) {
                $directory = Split-Path $defaultValueStorePath -Parent
                if (!(Test-Path $directory)) {
                    $PSCmdlet.WriteDebug("Creating directory for template's DefaultValueStore '$directory'.")
                    New-Item $directory -ItemType Directory > $null
                }

                $PSCmdlet.WriteDebug("DefaultValueStore is dirty, saving updated values to '$defaultValueStorePath'.")
                $defaultValueStore | Export-Clixml -LiteralPath $defaultValueStorePath
            }

            # Output the DestinationPath
            Write-Host ($LocalizedData.DestPath_F1 -f $destinationAbsolutePath)

            # Process content
            foreach ($node in $manifest.plasterManifest.content.ChildNodes) {
                if ($node -isnot [System.Xml.XmlElement]) { continue }

                switch -Regex ($node.LocalName) {
                    'file|templateFile' { ProcessFile $node; break }
                    'message'           { ProcessMessage $node; break }
                    'modify'            { ProcessModifyFile $node; break }
                    'newModuleManifest' { ProcessNewModuleManifest $node; break }
                    'requireModule'     { ProcessRequireModule $node; break }
                    default             { throw ($LocalizedData.UnrecognizedContentElement_F1 -f $node.LocalName) }
                }
            }

            if ($PassThru) {
                $InvokePlasterInfo.Success = $true
                $InvokePlasterInfo
            }
        }
        finally {
            # Dispose of the ConstrainedRunspace.
            if ($constrainedRunspace) {
                $constrainedRunspace.Dispose()
                $constrainedRunspace = $null
            }
        }
    }
}

###############################################################################
# Helper functions
###############################################################################

function InitializePredefinedVariables([string]$TemplatePath, [string]$DestPath) {
    # Always set these variables, even if the command has been run with -WhatIf
    $WhatIfPreference = $false

    Set-Variable -Name PLASTER_TemplatePath -Value $TemplatePath.TrimEnd('\','/') -Scope Script

    $destName = Split-Path -Path $DestPath -Leaf
    Set-Variable -Name PLASTER_DestinationPath -Value $DestPath.TrimEnd('\','/') -Scope Script
    Set-Variable -Name PLASTER_DestinationName -Value $destName -Scope Script
    Set-Variable -Name PLASTER_DirSepChar      -Value ([System.IO.Path]::DirectorySeparatorChar) -Scope Script
    Set-Variable -Name PLASTER_HostName        -Value $Host.Name -Scope Script
    Set-Variable -Name PLASTER_Version         -Value $MyInvocation.MyCommand.Module.Version -Scope Script

    Set-Variable -Name PLASTER_Guid1 -Value ([Guid]::NewGuid()) -Scope Script
    Set-Variable -Name PLASTER_Guid2 -Value ([Guid]::NewGuid()) -Scope Script
    Set-Variable -Name PLASTER_Guid3 -Value ([Guid]::NewGuid()) -Scope Script
    Set-Variable -Name PLASTER_Guid4 -Value ([Guid]::NewGuid()) -Scope Script
    Set-Variable -Name PLASTER_Guid5 -Value ([Guid]::NewGuid()) -Scope Script

    $now = [DateTime]::Now
    Set-Variable -Name PLASTER_Date -Value ($now.ToShortDateString()) -Scope Script
    Set-Variable -Name PLASTER_Time -Value ($now.ToShortTimeString()) -Scope Script
    Set-Variable -Name PLASTER_Year -Value ($now.Year) -Scope Script
}

function GetPlasterManifestPathForCulture([string]$TemplatePath, [ValidateNotNull()][CultureInfo]$Culture) {
    if (![System.IO.Path]::IsPathRooted($TemplatePath)) {
        $TemplatePath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($TemplatePath)
    }

    # Check for culture-locale first.
    $plasterManifestBasename = "plasterManifest"
    $plasterManifestFilename = "${plasterManifestBasename}_$($culture.Name).xml"
    $plasterManifestPath = Join-Path $TemplatePath $plasterManifestFilename
    if (Test-Path $plasterManifestPath) {
        return $plasterManifestPath
    }

    # Check for culture next.
    if ($culture.Parent.Name) {
        $plasterManifestFilename = "${plasterManifestBasename}_$($culture.Parent.Name).xml"
        $plasterManifestPath = Join-Path $TemplatePath $plasterManifestFilename
        if (Test-Path $plasterManifestPath) {
            return $plasterManifestPath
        }
    }

    # Fallback to invariant culture manifest.
    $plasterManifestPath = Join-Path $TemplatePath "${plasterManifestBasename}.xml"
    if (Test-Path $plasterManifestPath) {
        return $plasterManifestPath
    }

    $null
}
tools\Plaster\NewPlasterManifest.ps1
function New-PlasterManifest {
    [CmdletBinding(SupportsShouldProcess=$true)]
    param(
        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string]
        $Path = "$pwd\plasterManifest.xml",

        [Parameter(Mandatory=$true)]
        [ValidatePattern('^[0-9a-zA-Z_-]+$')]
        [string]
        $TemplateName,

        [Parameter(Mandatory=$true)]
        [ValidateSet('Item','Project')]
        [string]
        $TemplateType,

        [Parameter()]
        [Guid]
        $Id = [guid]::NewGuid(),

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [ValidatePattern('^\d+\.\d+(\.\d+((\.\d+|(\+|-).*)?)?)?$')]
        [string]
        $TemplateVersion = "1.0.0",

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string]
        $Title = $TemplateName,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string]
        $Description,

        [Parameter()]
        [string[]]
        $Tags,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string]
        $Author,

        [Parameter()]
        [switch]
        $AddContent
    )

    begin {
        $resolvedPath = $PSCmdLet.GetUnresolvedProviderPathFromPSPath($Path)

        $caseCorrectedTemplateType = [System.Char]::ToUpper($TemplateType[0]) + $TemplateType.Substring(1).ToLower()

        $manifestStr = @"
<?xml version="1.0" encoding="utf-8"?>
<plasterManifest schemaVersion="$LatestSupportedSchemaVersion"
                 templateType="$caseCorrectedTemplateType"
                 xmlns="http://www.microsoft.com/schemas/PowerShell/Plaster/v1">

    <metadata>
        <name></name>
        <id></id>
        <version></version>
        <title></title>
        <description></description>
        <author></author>
        <tags></tags>
    </metadata>
    <parameters>
    </parameters>
    <content>
    </content>
</plasterManifest>
"@
    }

    end {
        $manifest = [xml]$manifestStr

        # Set via .innerText to get .NET to encode special XML chars as entity references.
        $manifest.plasterManifest.metadata["name"].innerText = "$TemplateName"
        $manifest.plasterManifest.metadata["id"].innerText = "$Id"
        $manifest.plasterManifest.metadata["version"].innerText = "$TemplateVersion"
        $manifest.plasterManifest.metadata["title"].innerText = "$Title"
        $manifest.plasterManifest.metadata["description"].innerText = "$Description"
        $manifest.plasterManifest.metadata["author"].innerText = "$Author"

        $OFS = ", "
        $manifest.plasterManifest.metadata["tags"].innerText = "$Tags"

        if ($AddContent) {
            $baseDir = Split-Path $Path -Parent
            $filenames = Get-ChildItem $baseDir -Recurse -File -Name
            foreach ($filename in $filenames) {
                if ($filename -match "plasterManifest.*\.xml") {
                    continue
                }

                $fileElem = $manifest.CreateElement('file', $TargetNamespace)

                $srcAttr = $manifest.CreateAttribute("source")
                $srcAttr.Value = $filename
                $fileElem.Attributes.Append($srcAttr) > $null

                $dstAttr = $manifest.CreateAttribute("destination")
                $dstAttr.Value = $filename
                $fileElem.Attributes.Append($dstAttr) > $null

                $manifest.plasterManifest["content"].AppendChild($fileElem) > $null
            }
        }

        # This configures the XmlWriter to put attributes on a new line
        $xmlWriterSettings = New-Object System.Xml.XmlWriterSettings
        $xmlWriterSettings.Indent = $true
        $xmlWriterSettings.NewLineOnAttributes = $true

        try {
            if ($PSCmdlet.ShouldProcess($resolvedPath, $LocalizedData.ShouldCreateNewPlasterManifest)) {
                $xmlWriter = [System.Xml.XmlWriter]::Create($resolvedPath, $xmlWriterSettings)
                $manifest.Save($xmlWriter)
            }
        }
        finally {
            if ($xmlWriter) {
                $xmlWriter.Dispose()
            }
        }
    }
}
tools\Plaster\Plaster.psd1
@{
    # Script module or binary module file associated with this manifest.
    RootModule        = 'Plaster.psm1'

    # ID used to uniquely identify this module
    GUID              = 'cfce3c5e-402f-412a-a83a-7b7ee9832ff4'

    # Version number of this module.
    ModuleVersion     = '1.1.4'

    # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a
    # PSData hashtable with additional module metadata used by PowerShell.
    PrivateData       = @{
        PSData = @{
            # Tags applied to this module. These help with module discovery in online galleries.
            Tags       = @('Plaster', 'CodeGenerator', 'Scaffold')

            # A URL to the license for this module.
            LicenseUri = 'https://github.com/PowerShellOrg/Plaster/blob/master/LICENSE'

            # A URL to the main website for this project.
            ProjectUri = 'https://github.com/PowerShellOrg/Plaster'

            # A URL to an icon representing this module.
            #IconUri = 'https://github.com/PowerShell/Plaster/icon.png'

            # ReleaseNotes of this module - our ReleaseNotes are in
            # the file ReleaseNotes.md
            # ReleaseNotes = ''

        }
    }

    # Author of this module
    Author            = 'PowerShell.org'

    # Company or vendor of this module
    CompanyName       = 'The DevOps Collective Inc.'

    # Copyright statement for this module
    Copyright         = '(c) The DevOps Collective Inc.2016-2021. All rights reserved.'

    # Description of the functionality provided by this module
    Description       = 'Plaster scaffolds PowerShell projects and files.'

    # Minimum version of the Windows PowerShell engine required by this module
    PowerShellVersion = '3.0'

    # Functions to export from this module - explicitly list each function that should be
    # exported.  This improves performance of PowerShell when discovering the commands in
    # module.
    FunctionsToExport = @(
        'Invoke-Plaster'
        'New-PlasterManifest'
        'Get-PlasterTemplate',
        'Test-PlasterManifest'
    )

    # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
    CmdletsToExport   = @()

    # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.
    AliasesToExport   = @()

    # HelpInfo URI of this module
    # HelpInfoURI = ''
}
tools\Plaster\Plaster.psm1

data LocalizedData {
    # culture="en-US"
    ConvertFrom-StringData @'
    DestPath_F1=Destination path: {0}
    ErrorFailedToLoadStoreFile_F1=Failed to load the default value store file: '{0}'.
    ErrorProcessingDynamicParams_F1=Failed to create dynamic parameters from the template's manifest file.  Template-based dynamic parameters will not be available until the error is corrected.  The error was: {0}
    ErrorTemplatePathIsInvalid_F1=The TemplatePath parameter value must refer to an existing directory. The specified path '{0}' does not.
    ErrorUnencryptingSecureString_F1=Failed to unencrypt value for parameter '{0}'.
    ErrorPathDoesNotExist_F1=Cannot find path '{0}' because it does not exist.
    ErrorPathMustBeRelativePath_F2=The path '{0}' specified in the {1} directive in the template manifest cannot be an absolute path.  Change the path to a relative path.
    ErrorPathMustBeUnderDestPath_F2=The path '{0}' must be under the specified DestinationPath '{1}'.
    ExpressionInvalid_F2=The expression '{0}' is invalid or threw an exception. Error: {1}
    ExpressionNonTermErrors_F2=The expression '{0}' generated error output - {1}
    ExpressionExecError_F2=PowerShell expression failed execution. Location: {0}. Error: {1}
    ExpressionErrorLocationFile_F2=<{0}> attribute '{1}'
    ExpressionErrorLocationModify_F1=<modify> attribute '{0}'
    ExpressionErrorLocationNewModManifest_F1=<newModuleManifest> attribute '{0}'
    ExpressionErrorLocationParameter_F2=<parameter> name='{0}', attribute '{1}'
    ExpressionErrorLocationRequireModule_F2=<requireModule> name='{0}', attribute '{1}'
    ExpressionInvalidCondition_F3=The Plaster manifest condition '{0}' failed. Location: {1}. Error: {2}
    InterpolationError_F3=The Plaster manifest attribute value '{0}' failed string interpolation. Location: {1}. Error: {2}
    FileConflict=Plaster file conflict
    ManifestFileMissing_F1=The Plaster manifest file '{0}' was not found.
    ManifestMissingDocElement_F2=The Plaster manifest file '{0}' is missing the document element. It should be specified as <plasterManifest xmlns="{1}"></plasterManifest>.
    ManifestMissingDocTargetNamespace_F2=The Plaster manifest file '{0}' is missing or has an invalid target namespace on the document element. It should be specified as <plasterManifest xmlns="{1}"></plasterManifest>.
    ManifestPlasterVersionNotSupported_F2=The template file '{0}' specifies a plasterVersion of {1} which is greater than the installed version of Plaster. Update the Plaster module and try again.
    ManifestSchemaInvalidAttrValue_F5=Invalid '{0}' attribute value '{1}' on '{2}' element in file '{3}'. Error: {4}
    ManifestSchemaInvalidCondition_F3=Invalid condition '{0}' in file '{1}'. Error: {2}
    ManifestSchemaInvalidChoiceDefault_F3=Invalid default attribute value '{0}' for parameter '{1}' in file '{2}'. The default value must specify a zero-based integer index that corresponds to the default choice.
    ManifestSchemaInvalidMultichoiceDefault_F3=Invalid default attribute value '{0}' for parameter '{1}' in file '{2}'. The default value must specify one or more zero-based integer indexes in a comma separated list that correspond to the default choices.
    ManifestSchemaInvalidRequireModuleAttrs_F2=The requireModule attribute 'requiredVersion' for module '{0}' in file '{1}' cannot be used together with either the 'minimumVersion' or 'maximumVersion' attribute.
    ManifestSchemaValidationError_F2=Plaster manifest schema error in file '{0}'. Error: {1}
    ManifestSchemaVersionNotSupported_F2=The template's manifest schema version ({0}) in file '{1}' requires a newer version of Plaster. Update the Plaster module and try again.
    ManifestErrorReading_F1=Error reading Plaster manifest: {0}
    ManifestNotValid_F1=The Plaster manifest '{0}' is not valid.
    ManifestNotValidVerbose_F1=The Plaster manifest '{0}' is not valid. Specify -Verbose to see the specific schema errors.
    ManifestNotWellFormedXml_F2=The Plaster manifest '{0}' is not a well-formed XML file. {1}
    ManifestWrongFilename_F1=The Plaster manifest filename '{0}' is not valid. The value of the Path argument must refer to a file named 'plasterManifest.xml' or 'plasterManifest_<culture>.xml'. Change the Plaster manifest filename and then try again.
    MissingParameterPrompt_F1=<Missing prompt value for parameter '{0}'>
    NewModManifest_CreatingDir_F1=Creating destination directory for module manifest: {0}
    OpConflict=Conflict
    OpCreate=Create
    OpForce=Force
    OpIdentical=Identical
    OpMissing=Missing
    OpModify=Modify
    OpUpdate=Update
    OpVerify=Verify
    OverwriteFile_F1=Overwrite {0}
    ParameterTypeChoiceMultipleDefault_F1=Parameter name {0} is of type='choice' and can only have one default value.
    RequireModuleVerified_F2=The required module {0}{1} is already installed.
    RequireModuleMissing_F2=The required module {0}{1} was not found.
    RequireModuleMinVersion_F1=minimum version: {0}
    RequireModuleMaxVersion_F1=maximum version: {0}
    RequireModuleRequiredVersion_F1=required version: {0}
    ShouldCreateNewPlasterManifest=Create Plaster manifest
    ShouldProcessCreateDir=Create directory
    ShouldProcessExpandTemplate=Expand template file
    ShouldProcessNewModuleManifest=Create new module manifest
    TempFileOperation_F1={0} into temp file before copying to destination
    TempFileTarget_F1=temp file for '{0}'
    TestPlasterNoXmlSchemaValidationWarning=The version of .NET Core that PowerShell is running on does not support XML schema-based validation. Test-PlasterManifest will operate in "limited validation" mode primarily verifying the specified manifest file is well-formed XML. For full, XML schema-based validation, run this command on Windows PowerShell.
    UnrecognizedParametersElement_F1=Unrecognized manifest parameters child element: {0}.
    UnrecognizedParameterType_F2=Unrecognized parameter type '{0}' on parameter name '{1}'.
    UnrecognizedContentElement_F1=Unrecognized manifest content child element: {0}.
'@
}

Microsoft.PowerShell.Utility\Import-LocalizedData LocalizedData -FileName Plaster.Resources.psd1 -ErrorAction SilentlyContinue

# Module variables
[System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')]
$PlasterVersion = (Test-ModuleManifest -Path $PSScriptRoot\Plaster.psd1).Version
[System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')]
$LatestSupportedSchemaVersion = [System.Version]'1.2'
[System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')]
$TargetNamespace = "http://www.microsoft.com/schemas/PowerShell/Plaster/v1"
[System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')]
$DefaultEncoding = 'Default'

if (($PSVersionTable.PSVersion.Major -le 5) -or ($PSVersionTable.PSEdition -eq 'Desktop') -or $IsWindows) {
    [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')]
    $ParameterDefaultValueStoreRootPath = "$env:LOCALAPPDATA\Plaster"
}
elseif ($IsLinux) {
    # https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
    [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')]
    $ParameterDefaultValueStoreRootPath = if ($XDG_DATA_HOME) { "$XDG_DATA_HOME/plaster"  } else { "$Home/.local/share/plaster" }
}
else {
    [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')]
    $ParameterDefaultValueStoreRootPath = "$Home/.plaster"
}

# Dot source the individual module command scripts.
. $PSScriptRoot\NewPlasterManifest.ps1
. $PSScriptRoot\TestPlasterManifest.ps1
. $PSScriptRoot\GetPlasterTemplate.ps1
. $PSScriptRoot\InvokePlaster.ps1

Export-ModuleMember -Function *-*
tools\Plaster\Plaster.Resources.psd1
# Localized PlasterResources.psd1

ConvertFrom-StringData @'
###PSLOC
DestPath_F1=Destination path: {0}
ErrorFailedToLoadStoreFile_F1=Failed to load the default value store file: '{0}'.
ErrorProcessingDynamicParams_F1=Failed to create dynamic parameters from the template's manifest file.  Template-based dynamic parameters will not be available until the error is corrected.  The error was: {0}
ErrorTemplatePathIsInvalid_F1=The TemplatePath parameter value must refer to an existing directory. The specified path '{0}' does not.
ErrorUnencryptingSecureString_F1=Failed to unencrypt value for parameter '{0}'.
ErrorPathDoesNotExist_F1=Cannot find path '{0}' because it does not exist.
ErrorPathMustBeRelativePath_F2=The path '{0}' specified in the {1} directive in the template manifest cannot be an absolute path.  Change the path to a relative path.
ErrorPathMustBeUnderDestPath_F2=The path '{0}' must be under the specified DestinationPath '{1}'.
ExpressionInvalid_F2=The expression '{0}' is invalid or threw an exception. Error: {1}
ExpressionNonTermErrors_F2=The expression '{0}' generated error output - {1}
ExpressionExecError_F2=PowerShell expression failed execution. Location: {0}. Error: {1}
ExpressionErrorLocationFile_F2=<{0}> attribute '{1}'
ExpressionErrorLocationModify_F1=<modify> attribute '{0}'
ExpressionErrorLocationNewModManifest_F1=<newModuleManifest> attribute '{0}'
ExpressionErrorLocationParameter_F2=<parameter> name='{0}', attribute '{1}'
ExpressionErrorLocationRequireModule_F2=<requireModule> name='{0}', attribute '{1}'
ExpressionInvalidCondition_F3=The Plaster manifest condition '{0}' failed. Location: {1}. Error: {2}
InterpolationError_F3=The Plaster manifest attribute value '{0}' failed string interpolation. Location: {1}. Error: {2}
FileConflict=Plaster file conflict
ManifestFileMissing_F1=The Plaster manifest file '{0}' was not found.
ManifestMissingDocElement_F2=The Plaster manifest file '{0}' is missing the document element. It should be specified as <plasterManifest xmlns="{1}"></plasterManifest>.
ManifestMissingDocTargetNamespace_F2=The Plaster manifest file '{0}' is missing or has an invalid target namespace on the document element. It should be specified as <plasterManifest xmlns="{1}"></plasterManifest>.
ManifestPlasterVersionNotSupported_F2=The template file '{0}' specifies a plasterVersion of {1} which is greater than the installed version of Plaster. Update the Plaster module and try again.
ManifestSchemaInvalidAttrValue_F5=Invalid '{0}' attribute value '{1}' on '{2}' element in file '{3}'. Error: {4}
ManifestSchemaInvalidCondition_F3=Invalid condition '{0}' in file '{1}'. Error: {2}
ManifestSchemaInvalidChoiceDefault_F3=Invalid default attribute value '{0}' for parameter '{1}' in file '{2}'. The default value must specify a zero-based integer index that corresponds to the default choice.
ManifestSchemaInvalidMultichoiceDefault_F3=Invalid default attribute value '{0}' for parameter '{1}' in file '{2}'. The default value must specify one or more zero-based integer indexes in a comma separated list that correspond to the default choices.
ManifestSchemaInvalidRequireModuleAttrs_F2=The requireModule attribute 'requiredVersion' for module '{0}' in file '{1}' cannot be used together with either the 'minimumVersion' or 'maximumVersion' attribute.
ManifestSchemaValidationError_F2=Plaster manifest schema error in file '{0}'. Error: {1}
ManifestSchemaVersionNotSupported_F2=The template's manifest schema version ({0}) in file '{1}' requires a newer version of Plaster. Update the Plaster module and try again.
ManifestErrorReading_F1=Error reading Plaster manifest: {0}
ManifestNotValid_F1=The Plaster manifest '{0}' is not valid.
ManifestNotValidVerbose_F1=The Plaster manifest '{0}' is not valid. Specify -Verbose to see the specific schema errors.
ManifestNotWellFormedXml_F2=The Plaster manifest '{0}' is not a well-formed XML file. {1}
ManifestWrongFilename_F1=The Plaster manifest filename '{0}' is not valid. The value of the Path argument must refer to a file named 'plasterManifest.xml' or 'plasterManifest_<culture>.xml'. Change the Plaster manifest filename and then try again.
MissingParameterPrompt_F1=<Missing prompt value for parameter '{0}'>
NewModManifest_CreatingDir_F1=Creating destination directory for module manifest: {0}
OpConflict=Conflict
OpCreate=Create
OpForce=Force
OpIdentical=Identical
OpMissing=Missing
OpModify=Modify
OpUpdate=Update
OpVerify=Verify
OverwriteFile_F1=Overwrite {0}
ParameterTypeChoiceMultipleDefault_F1=Parameter name {0} is of type='choice' and can only have one default value.
RequireModuleVerified_F2=The required module {0}{1} is already installed.
RequireModuleMissing_F2=The required module {0}{1} was not found.
RequireModuleMinVersion_F1=minimum version: {0}
RequireModuleMaxVersion_F1=maximum version: {0}
RequireModuleRequiredVersion_F1=required version: {0}
ShouldCreateNewPlasterManifest=Create Plaster manifest
ShouldProcessCreateDir=Create directory
ShouldProcessExpandTemplate=Expand template file
ShouldProcessNewModuleManifest=Create new module manifest
TempFileOperation_F1={0} into temp file before copying to destination
TempFileTarget_F1=temp file for '{0}'
TestPlasterNoXmlSchemaValidationWarning=The version of .NET Core that PowerShell is running on does not support XML schema-based validation. Test-PlasterManifest will operate in "limited validation" mode primarily verifying the specified manifest file is well-formed XML. For full, XML schema-based validation, run this command on Windows PowerShell.
UnrecognizedParametersElement_F1=Unrecognized manifest parameters child element: {0}.
UnrecognizedParameterType_F2=Unrecognized parameter type '{0}' on parameter name '{1}'.
UnrecognizedContentElement_F1=Unrecognized manifest content child element: {0}.
###PSLOC
'@
tools\Plaster\PSGetModuleInfo.xml
<Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04">
  <Obj RefId="0">
    <TN RefId="0">
      <T>Microsoft.PowerShell.Commands.PSRepositoryItemInfo</T>
      <T>System.Management.Automation.PSCustomObject</T>
      <T>System.Object</T>
    </TN>
    <MS>
      <S N="Name">Plaster</S>
      <S N="Version">1.1.4</S>
      <S N="Type">Module</S>
      <S N="Description">Plaster scaffolds PowerShell projects and files.</S>
      <S N="Author">PowerShell.org</S>
      <S N="CompanyName">Psjamesp</S>
      <S N="Copyright">(c) The DevOps Collective Inc.2016-2021. All rights reserved.</S>
      <DT N="PublishedDate">2022-10-19T20:49:59+00:00</DT>
      <Nil N="InstalledDate" />
      <Nil N="UpdatedDate" />
      <URI N="LicenseUri">https://github.com/PowerShellOrg/Plaster/blob/master/LICENSE</URI>
      <URI N="ProjectUri">https://github.com/PowerShellOrg/Plaster</URI>
      <Nil N="IconUri" />
      <Obj N="Tags" RefId="1">
        <TN RefId="1">
          <T>System.Object[]</T>
          <T>System.Array</T>
          <T>System.Object</T>
        </TN>
        <LST>
          <S>Plaster</S>
          <S>CodeGenerator</S>
          <S>Scaffold</S>
          <S>PSModule</S>
        </LST>
      </Obj>
      <Obj N="Includes" RefId="2">
        <TN RefId="2">
          <T>System.Collections.Hashtable</T>
          <T>System.Object</T>
        </TN>
        <DCT>
          <En>
            <S N="Key">Function</S>
            <Obj N="Value" RefId="3">
              <TNRef RefId="1" />
              <LST>
                <S>Invoke-Plaster</S>
                <S>New-PlasterManifest</S>
                <S>Get-PlasterTemplate</S>
                <S>Test-PlasterManifest</S>
              </LST>
            </Obj>
          </En>
          <En>
            <S N="Key">RoleCapability</S>
            <Obj N="Value" RefId="4">
              <TNRef RefId="1" />
              <LST />
            </Obj>
          </En>
          <En>
            <S N="Key">Command</S>
            <Obj N="Value" RefId="5">
              <TNRef RefId="1" />
              <LST>
                <S>Invoke-Plaster</S>
                <S>New-PlasterManifest</S>
                <S>Get-PlasterTemplate</S>
                <S>Test-PlasterManifest</S>
              </LST>
            </Obj>
          </En>
          <En>
            <S N="Key">DscResource</S>
            <Obj N="Value" RefId="6">
              <TNRef RefId="1" />
              <LST />
            </Obj>
          </En>
          <En>
            <S N="Key">Workflow</S>
            <Obj N="Value" RefId="7">
              <TNRef RefId="1" />
              <LST />
            </Obj>
          </En>
          <En>
            <S N="Key">Cmdlet</S>
            <Obj N="Value" RefId="8">
              <TNRef RefId="1" />
              <LST />
            </Obj>
          </En>
        </DCT>
      </Obj>
      <Nil N="PowerShellGetFormatVersion" />
      <Nil N="ReleaseNotes" />
      <Obj N="Dependencies" RefId="9">
        <TNRef RefId="1" />
        <LST />
      </Obj>
      <S N="RepositorySourceLocation">https://www.powershellgallery.com/api/v2</S>
      <S N="Repository">PSGallery</S>
      <S N="PackageManagementProvider">NuGet</S>
      <Obj N="AdditionalMetadata" RefId="10">
        <TN RefId="3">
          <T>System.Management.Automation.PSCustomObject</T>
          <T>System.Object</T>
        </TN>
        <MS>
          <S N="copyright">(c) The DevOps Collective Inc.2016-2021. All rights reserved.</S>
          <S N="description">Plaster scaffolds PowerShell projects and files.</S>
          <S N="requireLicenseAcceptance">False</S>
          <S N="isLatestVersion">True</S>
          <S N="isAbsoluteLatestVersion">True</S>
          <S N="versionDownloadCount">9</S>
          <S N="downloadCount">130461</S>
          <S N="packageSize">55464</S>
          <S N="published">10/19/2022 8:49:59 PM +00:00</S>
          <S N="created">10/19/2022 8:49:59 PM +00:00</S>
          <S N="lastUpdated">10/19/2022 11:37:52 PM +00:00</S>
          <S N="tags">Plaster CodeGenerator Scaffold PSModule PSFunction_Invoke-Plaster PSCommand_Invoke-Plaster PSFunction_New-PlasterManifest PSCommand_New-PlasterManifest PSFunction_Get-PlasterTemplate PSCommand_Get-PlasterTemplate PSFunction_Test-PlasterManifest PSCommand_Test-PlasterManifest PSIncludes_Function</S>
          <S N="developmentDependency">False</S>
          <S N="updated">2022-10-19T23:37:52Z</S>
          <S N="NormalizedVersion">1.1.4</S>
          <S N="Authors">PowerShell.org</S>
          <S N="IsPrerelease">false</S>
          <S N="ItemType">Module</S>
          <S N="FileList">Plaster.nuspec|en-US\Plaster-help.xml|GetModuleExtension.ps1|en-US\Plaster.Resources.psd1|GetPlasterTemplate.ps1|Schema\PlasterManifest-v1.xsd|InvokePlaster.ps1|Templates\AddPSScriptAnalyzerSettings\plasterManifest.xml|NewPlasterManifest.ps1|Templates\AddPSScriptAnalyzerSettings\PSScriptAnalyzerSettings.psd1|Templates\AddPSScriptAnalyzerSettings\.vscode\settings.json|Plaster.psd1|Templates\NewPowerShellScriptModule\Module.psm1|Plaster.psm1|Templates\NewPowerShellScriptModule\plasterManifest.xml|Plaster.Resources.psd1|Templates\NewPowerShellScriptModule\editor\VSCode\settings.json|TestPlasterManifest.ps1|Templates\NewPowerShellScriptModule\editor\VSCode\tasks.json|en-US\about_Plaster.help.help.txt|Templates\NewPowerShellScriptModule\test\Module.T.ps1|en-US\about_Plaster_CreatingAManifest.help.help.txt</S>
          <S N="GUID">cfce3c5e-402f-412a-a83a-7b7ee9832ff4</S>
          <S N="PowerShellVersion">3.0</S>
          <S N="CompanyName">The DevOps Collective Inc.</S>
        </MS>
      </Obj>
      <S N="InstalledLocation">C:\Users\appveyor\AppData\Local\Temp\1\2ca2cecd-05bb-454a-9a46-669495e3a36b\Plaster\1.1.4</S>
    </MS>
  </Obj>
</Objs>
tools\Plaster\TestPlasterManifest.ps1
function Test-PlasterManifest {
    [CmdletBinding()]
    [OutputType([System.Xml.XmlDocument])]
    param(
        [Parameter(Position=0,
                   ParameterSetName="Path",
                   ValueFromPipeline=$true,
                   ValueFromPipelineByPropertyName=$true,
                   HelpMessage="Specifies a path to a plasterManifest.xml file.")]
        [Alias("PSPath")]
        [ValidateNotNullOrEmpty()]
        [string[]]
        $Path = @("$pwd\plasterManifest.xml")
    )

    begin {
        $schemaPath = [System.IO.Path]::Combine($PSScriptRoot, "Schema", "PlasterManifest-v1.xsd")

        # Schema validation is not available on .NET Core - at the moment.
        if ('System.Xml.Schema.XmlSchemaSet' -as [type]) {
            $xmlSchemaSet = New-Object System.Xml.Schema.XmlSchemaSet
            $xmlSchemaSet.Add($TargetNamespace, $schemaPath) > $null
        }
        else {
            $PSCmdLet.WriteWarning($LocalizedData.TestPlasterNoXmlSchemaValidationWarning)
        }
    }

    process {
        foreach ($aPath in $Path) {
            $aPath = $PSCmdLet.GetUnresolvedProviderPathFromPSPath($aPath)

            if (!(Test-Path -LiteralPath $aPath)) {
                $ex = New-Object System.Management.Automation.ItemNotFoundException ($LocalizedData.ErrorPathDoesNotExist_F1 -f $aPath)
                $category = [System.Management.Automation.ErrorCategory]::ObjectNotFound
                $errRecord = New-Object System.Management.Automation.ErrorRecord $ex,'PathNotFound',$category,$aPath
                $PSCmdLet.WriteError($errRecord)
                return
            }

            $filename = Split-Path $aPath -Leaf

            # Verify the manifest has the correct filename. Allow for localized template manifest files as well.
            if (!(($filename -eq 'plasterManifest.xml') -or ($filename -match 'plasterManifest_[a-zA-Z]+(-[a-zA-Z]+){0,2}.xml'))) {
                Write-Error ($LocalizedData.ManifestWrongFilename_F1 -f $filename)
                return
            }

            # Verify the manifest loads into an XmlDocument i.e. verify it is well-formed.
            $manifest = $null
            try {
                $manifest = [xml](Get-Content $aPath)
            }
            catch {
                $ex = New-Object System.Exception ($LocalizedData.ManifestNotWellFormedXml_F2 -f $aPath, $_.Exception.Message), $_.Exception
                $category = [System.Management.Automation.ErrorCategory]::InvalidData
                $errRecord = New-Object System.Management.Automation.ErrorRecord $ex,'InvalidManifestFile',$category,$aPath
                $psCmdlet.WriteError($errRecord)
                return
            }

            # Validate the manifest contains the required root element and target namespace that the following
            # XML schema validation will apply to.
            if (!$manifest.plasterManifest) {
                Write-Error ($LocalizedData.ManifestMissingDocElement_F2 -f $aPath,$TargetNamespace)
                return
            }

            if ($manifest.plasterManifest.NamespaceURI -cne $TargetNamespace) {
                Write-Error ($LocalizedData.ManifestMissingDocTargetNamespace_F2 -f $aPath,$TargetNamespace)
                return
            }

            # Valid flag is stashed in a hashtable so the ValidationEventHandler scriptblock can set the value.
            $manifestIsValid = @{Value = $true}

            # Configure an XmlReader and XmlReaderSettings to perform schema validation on xml file.
            $xmlReaderSettings = New-Object System.Xml.XmlReaderSettings

            # Schema validation is not available on .NET Core - at the moment.
            if ($xmlSchemaSet) {
                $xmlReaderSettings.ValidationFlags = [System.Xml.Schema.XmlSchemaValidationFlags]::ReportValidationWarnings
                $xmlReaderSettings.ValidationType = [System.Xml.ValidationType]::Schema
                $xmlReaderSettings.Schemas = $xmlSchemaSet
            }

            # Schema validation is not available on .NET Core - at the moment.
            if ($xmlSchemaSet) {
                # Event handler scriptblock for the ValidationEventHandler event.
                $validationEventHandler = {
                    param($sender, $eventArgs)

                    if ($eventArgs.Severity -eq [System.Xml.Schema.XmlSeverityType]::Error)
                    {
                        Write-Verbose ($LocalizedData.ManifestSchemaValidationError_F2 -f $aPath,$eventArgs.Message)
                        $manifestIsValid.Value = $false
                    }
                }

                $xmlReaderSettings.add_ValidationEventHandler($validationEventHandler)
            }

            [System.Xml.XmlReader]$xmlReader = $null
            try {
                $xmlReader = [System.Xml.XmlReader]::Create($aPath, $xmlReaderSettings)
                while ($xmlReader.Read()) {}
            }
            catch {
                Write-Error ($LocalizedData.ManifestErrorReading_F1 -f $_)
                $manifestIsValid.Value = $false
            }
            finally {
                # Schema validation is not available on .NET Core - at the moment.
                if ($xmlSchemaSet) {
                    $xmlReaderSettings.remove_ValidationEventHandler($validationEventHandler)
                }
                if ($xmlReader) { $xmlReader.Dispose() }
            }

            # Validate default values for choice/multichoice parameters containing 1 or more ints
            $xpath = "//tns:parameter[@type='choice'] | //tns:parameter[@type='multichoice']"
            $choiceParameters = Select-Xml -Xml $manifest -XPath $xpath  -Namespace @{tns=$TargetNamespace}
            foreach ($choiceParameterXmlInfo in $choiceParameters) {
                $choiceParameter = $choiceParameterXmlInfo.Node
                if (!$choiceParameter.default) { continue }

                if ($choiceParameter.type -eq 'choice') {
                    if ($null -eq ($choiceParameter.default -as [int])) {
                        $PSCmdLet.WriteVerbose(($LocalizedData.ManifestSchemaInvalidChoiceDefault_F3 -f $choiceParameter.default,$choiceParameter.name,$aPath))
                        $manifestIsValid.Value = $false
                    }
                }
                else {
                    if ($null -eq (($choiceParameter.default -split ',') -as [int[]])) {
                        $PSCmdLet.WriteVerbose(($LocalizedData.ManifestSchemaInvalidMultichoiceDefault_F3 -f $choiceParameter.default,$choiceParameter.name,$aPath))
                        $manifestIsValid.Value = $false
                    }
                }
            }

            # Validate that the requireModule attribute requiredVersion is mutually exclusive from both
            # the version and maximumVersion attributes.
            $requireModules = Select-Xml -Xml $manifest -XPath '//tns:requireModule' -Namespace @{tns = $TargetNamespace}
            foreach ($requireModuleInfo in $requireModules) {
                $requireModuleNode = $requireModuleInfo.Node
                if ($requireModuleNode.requiredVersion -and ($requireModuleNode.minimumVersion -or $requireModuleNode.maximumVersion)) {
                    $PSCmdLet.WriteVerbose(($LocalizedData.ManifestSchemaInvalidRequireModuleAttrs_F2 -f $requireModuleNode.name,$aPath))
                    $manifestIsValid.Value = $false
                }
            }

            # Validate that all the condition attribute values are valid PowerShell script.
            $conditionAttrs = Select-Xml -Xml $manifest -XPath '//@condition'
            foreach ($conditionAttr in $conditionAttrs) {
                $tokens = $errors = $null
                $null = [System.Management.Automation.Language.Parser]::ParseInput($conditionAttr.Node.Value, [ref] $tokens, [ref] $errors)
                if ($errors.Count -gt 0) {
                    $msg = $LocalizedData.ManifestSchemaInvalidCondition_F3 -f $conditionAttr.Node.Value, $aPath, $errors[0]
                    $PSCmdLet.WriteVerbose($msg)
                    $manifestIsValid.Value = $false
                }
            }

            # Validate all interpolated attribute values are valid within a PowerShell string interpolation context.
            $interpolatedAttrs  = @(Select-Xml -Xml $manifest -XPath '//tns:parameter/@default' -Namespace @{tns = $TargetNamespace})
            $interpolatedAttrs += @(Select-Xml -Xml $manifest -XPath '//tns:parameter/@prompt' -Namespace @{tns = $TargetNamespace})
            $interpolatedAttrs += @(Select-Xml -Xml $manifest -XPath '//tns:content/tns:*/@*' -Namespace @{tns = $TargetNamespace})
            foreach ($interpolatedAttr in $interpolatedAttrs) {
                $name = $interpolatedAttr.Node.LocalName
                if ($name -eq 'condition') { continue }

                $tokens = $errors = $null
                $value = $interpolatedAttr.Node.Value
                $null = [System.Management.Automation.Language.Parser]::ParseInput("`"$value`"", [ref] $tokens, [ref] $errors)
                if ($errors.Count -gt 0) {
                    $ownerName = $interpolatedAttr.Node.OwnerElement.LocalName
                    $msg = $LocalizedData.ManifestSchemaInvalidAttrValue_F5 -f $name, $value, $ownerName, $aPath, $errors[0]
                    $PSCmdLet.WriteVerbose($msg)
                    $manifestIsValid.Value = $false
                }
            }

            if ($manifestIsValid.Value) {
                # Verify manifest schema version is supported.
                $manifestSchemaVersion = [System.Version]$manifest.plasterManifest.schemaVersion

                # Use a simplified form (no patch version) of semver for checking XML schema version compatibility.
                if (($manifestSchemaVersion.Major -gt $LatestSupportedSchemaVersion.Major) -or
                    (($manifestSchemaVersion.Major -eq $LatestSupportedSchemaVersion.Major) -and
                     ($manifestSchemaVersion.Minor -gt $LatestSupportedSchemaVersion.Minor))) {

                    Write-Error ($LocalizedData.ManifestSchemaVersionNotSupported_F2 -f $manifestSchemaVersion,$aPath)
                    return
                }

                # Verify that the plasterVersion is supported.
                if ($manifest.plasterManifest.plasterVersion) {
                    $requiredPlasterVersion = [System.Version]$manifest.plasterManifest.plasterVersion

                    # Is user specifies major.minor, change build to 0 (from default of -1) so compare works correctly.
                    if ($requiredPlasterVersion.Build -eq -1) {
                        $requiredPlasterVersion = [System.Version]"${requiredPlasterVersion}.0"
                    }

                    if ($requiredPlasterVersion -gt $MyInvocation.MyCommand.Module.Version) {
                        $plasterVersion = $manifest.plasterManifest.plasterVersion
                        Write-Error ($LocalizedData.ManifestPlasterVersionNotSupported_F2 -f $aPath,$plasterVersion)
                        return
                    }
                }

                $manifest
            }
            else {
                if ($PSBoundParameters['Verbose']) {
                    Write-Error ($LocalizedData.ManifestNotValid_F1 -f $aPath)
                }
                else {
                    Write-Error ($LocalizedData.ManifestNotValidVerbose_F1 -f $aPath)
                }
            }
        }
    }
}
tools\Plaster\en-US\about_Plaster.help.help.txt
TOPIC
    about_plaster

SHORT DESCRIPTION
    Plaster is a scaffolding engine for PowerShell. It can be used to create a
    single file such as a DSC resource script file. Or it can be used to
    scaffold a set of related files and directories such as the files required
    for a PowerShell module with Pester tests.

LONG DESCRIPTION
    Plaster operates on a Plaster template which consists of a manifest file
    (plasterManifest.xml) and a set of content files and directories that will
    be copied to the destination path the user chooses. The manifest is an XML
    file that consists of the three sections: metadata, parameters, and content.
    The metadata section of the manifest is used to supply information about the
    template e.g. a unique id, name, version, title, author and tags.
    The parameters section of the manifest is used to describe choices the
    template user can choose from. Those choices are then used to conditionally
    create files and folders and modify existing files under the specified
    destination path. These parameters can be specified via dynamic parameters
    for non-interactive scenarios.
    The content section is used to specify what actions the template will
    perform under the user's chosen destination directory. This includes copying
    files to the destination, copy & expanding template files, modifying files,
    verifying required modules are installed and displaying messages to the user.
    See the help topic about_Plaster_CreatingAManifest for more details on
    authoring a Plaster manifest file.

LOCALIZATION
    Plaster supports template localization by support multiple manifest files
    suffixed with the local e.g. plasterManifest_fr-FR.xml or
    plasterManifest_de-DE.xml. The strings displayed to the user in these
    manifest files should be in the corresponding language.

SEE ALSO
    - https://github.com/PowerShell/Plaster -
    about_Plaster_CreatingAManifest

tools\Plaster\en-US\about_Plaster_CreatingAManifest.help.help.txt
TOPIC
    about_plaster_creatingamanifest

SHORT DESCRIPTION
    This about topic will explain the schema of a Plaster Manifest.
    For nearly all uses the `New-PlasterManifest` command is correct approach to
    start with, as this will ensure you generate a valid base manifest.

LONG DESCRIPTION
    This about topic will explain the schema of a Plaster Manifest.
    For nearly all uses the `New-PlasterManifest` command is correct approach to
    start with, as this will ensure you generate a valid base manifest.
    This topic serves to document the existing manifest schema, currently
    version `0.4`, using the example Plaster manifest `NewModuleTemplate`. The
    current Plaster manifest schema can be found at
    PlasterManifest-v1.xsd

The manifest base
    The overall structure of the manifest (shown below) consists of three main
    sections, `metadata`, `parameters` and `content`.
    The `metadata` section contains data about the manifest itself, including
    `title` and `version` of the manifest.
    The `parameters` section contains information about the data that needs to
    be gathered from the user, either as text, or as prompted choices.
    The final section, `content`, contains the information on how to name and
    structure the output of the template from the source files, as well as other
    data like required modules or informational messages to the user.
    The Plaster manifest attribute `schemaVersion` indicates the minimum
    required Plaster schema capable of reading the manifest. This will be
    automatically created when running `New-PlasterManifest` and populated with
    the current schema version.

    <?xml version="1.0" encoding="utf-8"?>
    <plasterManifest
      schemaVersion="0.4" xmlns="http://www.microsoft.com/schemas/PowerShell/Plaster/v1">
      <metadata></metadata>
      <parameters></parameters>
      <content></content>
    </plasterManifest>

Metadata
    The `metadata` section contains information about the Plaster manifest
    itself and requires the following data:
    - `name`        - Manifest name. This value is mandatory.
    - `id`          - The ID is the unique identifier used for the storing users
    parameter data and makes sure that the store doesn't get used with another
    template. This field is automatically generated if not given a value. -
    `version`     - Version of the manifest. Defaults to 1.0.0.
    - `title`       - Manifest name used in menu lists. Defaults to `name`.
    - `description` - Manifest description summary.
    - `tags`        - Tags used to describe the purpose of the template.
    - `author`      - (Optional) Authors name or details.
    An example of the settings explained above can be shown by running
    `New-PlasterManifest` this would give you something similar to the the
    following:

    <?xml version="1.0" encoding="utf-8"?>
    <plasterManifest
      schemaVersion="0.4" xmlns="http://www.microsoft.com/schemas/PowerShell/Plaster/v1">
      <metadata>
        <name>NewPowerShellModule</title>
        <id>38bd80d9-3a47-4916-9220-bed383d90876</id>
        <version>0.2.0</version>
        <title>New PowerShell Module</title>
        <description>Plaster template for creating the files for a PowerShell module.</description>
        <author>Plaster Project</author>
        <tags>Module, ModuleManifest, Build</tags>
      </metadata>
      <parameters></parameters>
      <content></content>
    </plasterManifest>

Parameters
    Parameters are pieces of information that Plaster will ask for to build the
    template. Parameters are optional directives, as all the data for the
    template could potentially be pre-configured, but that would defeat the
    purpose of the Plaster!
    Data for these parameters can be taken either as parameters to
    `Invoke-Plaster`, or will be prompted for by Plaster. Specifying the
    attributes for parameters is done using the following data:
    - `name`    - The name of the parameter. This is used as the identity to
    refer to and store the parameter value.
    - `type`    - the type of parameter, currently supported values are `text`,
    `choice`, `multichoice`, `user-fullname` and `user-email`.
    - `default` - The default value of the parameter, displayed in the UI inside
    parentheses. This is an optional attribute to assist users of your template.
    Default values for the `user-fullname` and `user-email` parameter types are
    retrieved from the user's .gitconfig if no stored value is available.
    - `condition` - A condition which, when evaluated to be True, allows for the
    interactive prompt to be displayed at the console for this parameter. If not
    specified, the interactive prompt is always displayed. It is important to
    note that this condition will
    NOT
    be evaluated during dynamic parameter creation. As such, it is possible for
    data to still be passed for this parameter via the command line invocation
    of invoke-plaster regardless of how this condition evaluates. - `store`   -
    Specifies the store type of the value. This is optional, as not all input
    data should necessarily be stored. Stored values will then be used as the
    default value the next time the template is run with `Invoke-Plaster`. The
    supported values are the same as the available parameter types and
    determines whether the stored value will be text, or encrypted. Stored data
    is saved to a user profile folder and the filename is a function of the
    template name and version (`Name-Version-ID.clixml`).
    Locations for the template input store differ based on operating system.
    Here is the list of possible locations and when they will be used:
    Windows - `$env:LOCALAPPDATA\Plaster`.
    Linux - `$XDG_DATA_HOME/plaster` (If `$XDG_DATA_HOME` has a value).
    - `$Home/.local/share/plaster` (No `$XDG_DATA_HOME` value).
    Other - `$Home/.plaster`.

    PARAMETER TYPE: TEXT
    In interactive mode, the `text` parameter type results in a prompt for a
    string of text:

    <parameter name='ModuleName'
               type='text'
               prompt='Enter the name of the module'/>

    This parameter definition results in the following prompt:

    Enter the name of the module: FooUtils

    Additionally, a default value can be specified, as shown in the next
    example:

    <parameter name='Version'
               type='text'
               default='0.1.0'
               prompt='Enter the version number for the module'/>

    This results in the following output, with the default value in parentheses:

    Enter the version number for the module (0.1.0):

    PARAMETER TYPE: CHOICE
    In interactive mode, the `choice` parameter type asks for a single choice
    from the available options. Place the ampersand in front of the character in
    the `label` that is best suitable as a "keyboard accelerator" for the given
    choice. This choice type also provides a help option '`?`' used to display
    the help text:

    <parameter name='License' type='choice' default='2' store='text' prompt='Select a license for your module'>
      <choice label='&amp;Apache'
              help="Adds an Apache license file."
              value="Apache"/>
      <choice label='&amp;MIT'
              help="Adds an MIT license file."
              value="MIT"/>
      <choice label='&amp;None'
              help="No license specified."
              value="None"/>
    </parameter>

    This parameter definition results in the following output:

    Select a license for your module
    [A] Apache  [M] MIT  [N] None  [?] Help (default is "M"): ?
    A - Adds an Apache license file.
    M - Adds an MIT license file.
    N - No license specified.
    [A] Apache  [M] MIT  [N] None  [?] Help (default is "M"):

    PARAMETER TYPE: MULTI CHOICE
    The `multichoice` parameter asks for one or more of the available options
    (supplied as a comma separated list of choices). Place the ampersand in
    front of the character in the `label` that is best suitable as a "keyboard
    accelerator" for the given choice. This choice type also provides a help
    option '`?`' used to display the help text:

    <parameter name='Options' type='multichoice' default='0,1,2' store='text' prompt='Select desired options'>
      <choice label='&amp;Pester test support'
              help="Adds Tests directory and a starter Pester Tests file."
              value="Pester"/>
      <choice label='P&amp;Sake build script'
              help="Adds a PSake build script that generates the module directory for publishing to the PSGallery."
              value="PSake"/>
      <choice label='&amp;Git'
              help="Adds a .gitignore file."
              value="Git"/>
      <choice label='&amp;None'
              help="No options specified."
              value="None"/>
    </parameter>

    This parameter definition results in the following output, including the
    help output:

    Select desired options
    [P] Pester test support
    [S] PSake build script
    [G] Git
    [N] None
    [?] Help
    (default choices are P,S,G)
    Choice[0]: ?
    P - Adds Tests directory and a starter Pester Tests file.
    S - Adds a PSake build script that generates the module directory for publishing to the PSGallery.
    G - Adds a .gitignore file.
    N - No options specified.
    Choice[0]:

    PARAMETER TYPE: OTHER
    The `user-fullname` and `user-email` parameter types are the same as the
    `text` type, except that they get their default values from from the user's
    .gitconfig file (if the user has one, and no default is set in the manifest).
    Here is an example of the XML for this parameter.

    <parameter name='FullName'
               type='user-fullname'
               prompt='Enter your full name'
               store='text' />

    This results in the following prompt, with the default value of 'Your Name'
    coming from your .gitconfig file:

    Enter your full name (Your Name):

Content
    There are a selection of elements in the `content` block that can be used to
    specify all of the content that should be included with your template and
    how it should be created and transformed into the end product that the
    template provides.
    The available element types are: - `file`              - Specify one or more
    files to copy under the destination folder.
    - `templateFile`      - Specify one or more template files to copy and
    expand under the destination folder.
    - `message`           - Display a message to the user.
    - `modify`            - Modify an existing file under the destination folder
    (Used with the `file`/`templateFile` elements).
    - `newModuleManifest` - Create a new module manifest file using the
    `New-ModuleManifest` command.
    - `requireModule`     - Checks to see if the specified module is installed.
    If not, the user is notified of the need to install the module.

    CONTENT ELEMENT: COMMON
    Currently, there is only one common attribute shared between all current
    content elements. - `condition` - Used to determine whether a directive is
    executed. If the condition (a PowerShell expression) evaluates to true, it
    will execute.
    Some elements use the encoding attribute, while not a common attribute, has
    a common set of possible values: - `Default`
    - `Ascii`
    - `BigEndianUnicode`
    - `BigEndianUTF32`
    - `Oem`
    - `Unicode`
    - `UTF32`
    - `UTF7`
    - `UTF8`
    - `UTF8-NoBOM`
    Element attribute values support the use of Plaster parameters, which are
    parameter values that can be expanded into file names, or other pieces of
    information the template deals with. These map to available parameters,
    licenses and other data that is provided in the template.

    CONTENT ELEMENT: FILE
    One or more files can be selected (using wild cards like `*`) with each file
    element. Attribute values support the inclusion of Plaster parameters to
    control (as an example) the location or the name of the resulting file.
    Available attributes for this content element: - `source`       - Specifies
    the relative path to the file in the template's root folder.
    - `destination`  - Specifies the relative path, under the destination
    folder, to where the file will be copied. Files can only be copied to a
    location under the destination folder.
    - `condition`    - Used to determine whether a directive is executed. If the
    condition (a PowerShell expression) evaluates to true, it will execute.
    - `openInEditor` - Specifies whether the file should be opened in the editor
    (true) after scaffolding or not (false).  The PowerShell extension for
    Visual Studio Code honors this setting.
    A basic example of this content element would be:

    <file source='ReleaseNotes.md'
          destination=''/>

    A basic example of this content element, to create an empty directory, would
    be:

    <file source=''
          destination='src\bin'/>

    Two more complex examples are:

    <file source='Tests\*.tests.ps1'
          destination='test\'
          condition='$PLASTER_PARAM_Options -contains "Pester"'/>

    <templateFile source='en-US\about_Module.help.txt'
                  destination='en-US\about_${PLASTER_PARAM_ModuleName}.help.txt'/>

    CONTENT ELEMENT: TEMPLATEFILE
    Specify one or more template files (using wild cards, as with the file
    element) to copy and expand under the destination folder. Expansion is done
    by looking through the file and expanding out any Plaster parameters that
    are found.
    Available attributes for this content element: - `source`       - Specifies
    the relative path to the file in the template's root folder.
    - `destination`  - Specifies the relative path, under the destination
    folder, to where the file will be copied.
    - `encoding`     - Specifies the encoding of the file, see `Content Element:
    Common` for possible values. If you do not specify an encoding, ASCII
    encoding will be used.
    - `condition`    - Used to determine whether a directive is executed. If the
    condition (a PowerShell expression) evaluates to true, it will execute.
    - `openInEditor` - Specifies whether the file should be opened in the editor
    (true) after scaffolding or not (false).  The PowerShell extension for
    Visual Studio Code honors this setting.
    An example of using the template file element:

    <templateFile source='test\Shared.ps1'
                  destination='test\Shared.ps1'
                  condition="$PLASTER_PARAM_Options -contains 'Pester'"/>

    CONTENT ELEMENT: MESSAGE
    The message type is a pretty straightforward element with two potential
    attributes (both optional).
    Available attributes for this content element: - `nonewline` - If true,
    suppresses output of a newline at the end of the message.
    - `condition` - Used to determine whether a directive is executed. If the
    condition (a PowerShell expression) evaluates to true, it will execute.
    Here is an example of the message element:

    <message>
    A message to the user
    
    and other interesting information.
    </message>

    This example shows Plaster parameter expansion working in the message
    element:

    <message nonewline='true'>`n`nYour new PowerShell module project $PLASTER_PARAM_ModuleName </message>

    CONTENT ELEMENT: MODIFY
    The modify element allows you to replace file contents, allowing you to copy
    files using the `file` element, then substituting content to meet your
    needs. Multiple replace elements can be used in a single modify element.
    Each replace element has one original and one substitute element to define
    what that replace operation does to the file.
    Available attributes for this content element are: - `replace`   - Specify a
    replacement operation of the file content.     - `original`   - The original
    text, or regular expression match to replace. If just searching for text,
    regular expression syntax must be used by escaping backslashes and other
    special characters. Additionally, because XML has to encode certain element
    content characters you might use in a regular expression (`<`, `>`), you may
    want to use a CDATA section to encode the expression.         - `expand` -
    Whether to expand variables within the original for match.     -
    `substitute` - The replacement text to substitute in place of the original
    text.         - `expand` - Whether to expand variables within the substitute
    text.         - `isFile` - Specifies `substitute` to be a relative filename.
    Reads file content. Cannot be used with `expand`.     - `condition` - `path`
            - Specifies the relative path, under the destination folder, of the
    file to be modified.
    - `encoding`     - Specifies the encoding of the file, see `Content Element:
    Common` for possible values. If you do not specify an encoding, ASCII
    encoding will be used.
    - `condition`    - Used to determine whether a directive is executed. If the
    condition (a PowerShell expression) evaluates to true, it will execute.
    - `openInEditor` - Specifies whether the file should be opened in the editor
    (true) after scaffolding or not (false).  The PowerShell extension for
    Visual Studio Code honors this setting.
    Here is a simple example of the modify element, using a regular expressions:

    <modify path='.vscode\tasks.json' encoding='UTF8'
            condition="$PLASTER_PARAM_Editor -eq 'VSCode'">
        <replace condition="$PLASTER_FileContent -notmatch '// Author:'">
            <original>(?s)^(.*)</original>
            <substitute expand='true'>// Author: $PLASTER_PARAM_FullName`r`n`$1</substitute>
        </replace>
    </modify>

    NOTE: ONLY USE `(?[SMI])` IF YOU NEED TO OVERRIDE POWERSHELL'S DEFAULT REGULAR EXPRESSION BEHAVIOR. `(?S)` MEANS THAT `.` MATCHES EVERY CHAR INCLUDING `\N` IN ORDER FOR THE REGULAR EXPRESSION TO SPAN MULTIPLE LINES.
    CONTENT ELEMENT: NEWMODULEMANIFEST
    This element allows you to create a module manifest using the data that has
    been input to Plaster through Plaster parameters.
    Available attributes for this content element: - `destination`          -
    Specifies the relative path, under the destination folder, to where the file
    will be copied.
    - `author`               - Specifies the value of the Author property.
    - `companyName`          - Specifies the value of the CompanyName property.
    - `description`          - Specifies the value of the Description property.
    Note: this field is required for module submissions to the PowerShell Gallery.
    - `moduleVersion`        - Specifies the value of the ModuleVersion property.
    - `rootModule`           - Specifies the value of the RootModule property.
    - `encoding`             - Specifies the encoding of the file, see `Content
    Element: Common` for possible values. If you do not specify an encoding, the
    current file encoding will be used.
    - `condition`            - Used to determine whether a directive is
    executed. If the condition (a PowerShell expression) evaluates to true, it
    will execute.
    - `openInEditor`         - Specifies whether the file should be opened in
    the editor (true) after scaffolding or not (false).  The PowerShell
    extension for Visual Studio Code honors this setting.
    - `powerShellVersion`    - Specifies the minimum PowerShell version required
    to load the module.
    - `nestedModules`        - Specifies a nested Module.
    - `dscResourcesToExport` - Specifies a DSC resource to export.
    Here is an example of the `newModuleManifest` element:

    <newModuleManifest destination='src\${PLASTER_PARAM_ModuleName}.psd1'
                       moduleVersion='$PLASTER_PARAM_Version'
                       rootModule='${PLASTER_PARAM_ModuleName}.psm1'
                       author='$PLASTER_PARAM_FullName'
                       description='$PLASTER_PARAM_ModuleDesc'
                       encoding='UTF8-NoBOM'
                       powerShellVersion='5.1'/>

    CONTENT ELEMENT: REQUIREMODULE
    The `requireModule` element specifies modules that are required under
    certain conditions resulting from the choices input by the user.
    Available attributes for this content element: - `name`            -
    Specifies the name of the required module.
    - `minimumVersion`  - The required module's minimum version.
    - `maximumVersion`  - The required module's maximum version.
    - `requiredVersion` - Specifies a specific version of the module. This
    attribute cannot be used with either the `minimumVersion` or
    `maximumVersion` attributes. Use this attribute rarely as any update to the
    module that changes its version will result in this check failing.
    - `message`         - Specifies a custom message to display after the
    standard Plaster message when the specified module's is not available on the
    target machine. This message should be used to tell the user what
    functionality will not work without the specified module.
    - `condition`       - Used to determine whether a directive is executed. If
    the condition (a PowerShell expression) evaluates to true, it will execute.

    NOTE: ALL VERSIONS IN THIS ELEMENT SHOULD BE SPECIFIED IN THE THREE PART MAJOR.MINOR.PATCH (SEMVER) FORMAT.
    <requireModule name="Pester" condition='$PLASTER_PARAM_Options -contains "Pester"'
                   minimumVersion="3.4.0"
                   message="Without Pester, you will not be able to run the provided Pester test to validate your module manifest file.`nWithout version 3.4.0, VS Code will not display Pester warnings and errors in the Problems panel."/>

Attribute and Condition Evaluation
    Many of the XML attributes in a manifest can contain variables or
    expressions that evaluate to a string. The attribute's value is passed into
    a constrained runspace in the context of a double quoted string for
    interpolation and the resulting string is used for template processing. Note
    the XML attributes on the `<parameter>` directive, except for the `default`
    and `prompt` attributes, are not processed this way because they are
    processed during `dynamicparam` execution. At that time, the constrained
    runspace in which these expressions are evaluated has not been created.
    Many of the directives in a Plaster manifest have a `condition` attribute.
    The contents of this condition attribute are evaluated in the same
    constrained runspace that attribute values are evaluated in. However, in the
    case of a `condition` attribute the resulting value is coerced to a [bool]
    which determines whether the associated directive is executed.

TemplateFile processing
    A file that is processed with the `<templateFile>` directive will be
    processed, replacing script expression delimiters and script block
    delimiters with PowerShell generated text. The script expression delimiter
    is `<%= %>`.  The contents of a script expression delimiter are executed and
    the results cast to a string and inserted into the file.
    The script block delimiter is:

    <%
        if ($PLASTER_PARAM_Ensure -eq 'Yes') {
    "        # Ensure the presence/absene of the resource."
    "        [ValidateSet('Present','Absent')]"
    "        [string]"
    "        `$Ensure = 'Present'"
        }
    %>

    Where both starting and closing delimiter must appear in column zero. The
    contents of a script block delimiter are executed and the results cast to a
    string array and inserted into the file.

PowerShell constrained runspace
    The manifest parameters you define are available via variables named
    `PLASTER_PARAM_<parameter-name>`. In addition to these variables, Plaster
    defines a set of built-in variables:
    - PLASTER_TemplatePath    - The absolute path to the template directory.
    - PLASTER_DestinationPath - The absolute path to the destination directory.
    - PLASTER_DestinationName - The name of the destinaion directory.
    - PLASTER_FileContent     - The contents of a file be modified via the
    `<modify>` directive.
    - PLASTER_DirSepChar      - The directory separator char for the platform.
    - PLASTER_HostName        - The PowerShell host name e.g. $Host.Name
    - PLASTER_Version         - The version of the Plaster module invoking the template.
    - PLASTER_Guid1           - Randomly generated GUID value
    - PLASTER_Guid2           - Randomly generated GUID value
    - PLASTER_Guid3           - Randomly generated GUID value
    - PLASTER_Guid4           - Randomly generated GUID value
    - PLASTER_Guid5           - Randomly generated GUID value
    - PLASTER_Date            - Date in short date string format e.g. 10/31/2016
    - PLASTER_Time            - Time in short time string format e.g. 5:11 PM
    - PLASTER_Year            - The four digit year
    In this constrained runspace, you have access to the Environment and
    FileSystem providers. In addition, the following commands are available:
    - Get-Content
    - Get-Date
    - Get-ChildItem
    - Get-Item
    - Get-ItemProperty
    - Get-Module
    - Get-Variable
    - Test-Path
    - Compare-Object

EXAMPLES
    You can create a base Plaster manifest by running the New-PlasterManifest command.
    See the included `NewModule` or `NewDscResourceScript` `plasterManifest.xml`
    for more examples.

NOTE
    You can find additional information about Plaster at the
    GitHub page

TROUBLESHOOTING NOTE
    This topic is not yet complete.

SEE ALSO
    - https://github.com/PowerShell/Plaster

KEYWORDS
    - Plaster Manifest

tools\Plaster\en-US\Plaster-help.xml
<?xml version="1.0" encoding="utf-8"?>
<helpItems schema="maml" xmlns="http://msh">
  <command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10" xmlns:MSHelp="http://msdn.microsoft.com/mshelp">
    <command:details>
      <command:name>Get-PlasterTemplate</command:name>
      <command:verb>Get</command:verb>
      <command:noun>PlasterTemplate</command:noun>
      <maml:description>
        <maml:para>Retrieves a list of available Plaster templates that can be used with the Invoke-Plaster cmdlet.</maml:para>
      </maml:description>
    </command:details>
    <maml:description>
      <maml:para>Retrieves a list of available Plaster templates from the specified path or from the set of templates that are shipped with Plaster.  Specifying no arguments will cause only the built-in Plaster templates to be returned.  Using the -IncludeInstalledModules switch will also search the PSModulePath for PowerShell modules that advertise Plaster templates that they include. By default, this retrieves the latest version available for each module. Using the -ListAvailable parameter will return all templates from all module versions installed on this computer. Using the -Name parameter limits the results based on name. Using the -Tag parameter limits the results based on the template tags.</maml:para>
      <maml:para></maml:para>
      <maml:para>The objects returned from this cmdlet will provide details about each individual template that was retrieved.  Use the TemplatePath property of a template object as the input to Invoke-Plaster's -TemplatePath parameter.</maml:para>
    </maml:description>
    <command:syntax>
      <command:syntaxItem>
        <maml:name>Get-PlasterTemplate</maml:name>
        <command:parameter required="true" variableLength="true" globbing="false" pipelineInput="False" position="0" aliases="IncludeModules">
          <maml:name>IncludeInstalledModules</maml:name>
          <maml:description>
            <maml:para>Initiates a search for Plaster templates inside of installed modules.</maml:para>
          </maml:description>
          <dev:type>
            <maml:name>SwitchParameter</maml:name>
            <maml:uri />
          </dev:type>
          <dev:defaultValue>False</dev:defaultValue>
        </command:parameter>
        <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="1" aliases="none">
          <maml:name>Name</maml:name>
          <maml:description>
            <maml:para>Limits the templates returned to those that match the template name. Wildcard characters are permitted.</maml:para>
          </maml:description>
          <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
          <dev:type>
            <maml:name>String</maml:name>
            <maml:uri />
          </dev:type>
          <dev:defaultValue>*</dev:defaultValue>
        </command:parameter>
        <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
          <maml:name>ListAvailable</maml:name>
          <maml:description>
            <maml:para>If specified, searches for Plaster templates inside of all installed module versions.</maml:para>
          </maml:description>
          <dev:type>
            <maml:name>SwitchParameter</maml:name>
            <maml:uri />
          </dev:type>
          <dev:defaultValue>False</dev:defaultValue>
        </command:parameter>
        <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
          <maml:name>Tag</maml:name>
          <maml:description>
            <maml:para>Limits the templates returned to those that match the template tags. Wildcard characters are permitted.</maml:para>
          </maml:description>
          <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
          <dev:type>
            <maml:name>String</maml:name>
            <maml:uri />
          </dev:type>
          <dev:defaultValue>*</dev:defaultValue>
        </command:parameter>
      </command:syntaxItem>
      <command:syntaxItem>
        <maml:name>Get-PlasterTemplate</maml:name>
        <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="0" aliases="none">
          <maml:name>Path</maml:name>
          <maml:description>
            <maml:para>Specifies a path to a folder containing a Plaster template or multiple template folders. Can also be a path to plasterManifest.xml.</maml:para>
          </maml:description>
          <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
          <dev:type>
            <maml:name>String</maml:name>
            <maml:uri />
          </dev:type>
          <dev:defaultValue>None</dev:defaultValue>
        </command:parameter>
        <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="1" aliases="none">
          <maml:name>Name</maml:name>
          <maml:description>
            <maml:para>Limits the templates returned to those that match the template name. Wildcard characters are permitted.</maml:para>
          </maml:description>
          <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
          <dev:type>
            <maml:name>String</maml:name>
            <maml:uri />
          </dev:type>
          <dev:defaultValue>*</dev:defaultValue>
        </command:parameter>
        <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
          <maml:name>Recurse</maml:name>
          <maml:description>
            <maml:para>Indicates that this cmdlet gets the items in the specified locations and in all child items of the locations.</maml:para>
          </maml:description>
          <dev:type>
            <maml:name>SwitchParameter</maml:name>
            <maml:uri />
          </dev:type>
          <dev:defaultValue>False</dev:defaultValue>
        </command:parameter>
        <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
          <maml:name>Tag</maml:name>
          <maml:description>
            <maml:para>Limits the templates returned to those that match the template tags. Wildcard characters are permitted.</maml:para>
          </maml:description>
          <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
          <dev:type>
            <maml:name>String</maml:name>
            <maml:uri />
          </dev:type>
          <dev:defaultValue>*</dev:defaultValue>
        </command:parameter>
      </command:syntaxItem>
    </command:syntax>
    <command:parameters>
      <command:parameter required="true" variableLength="true" globbing="false" pipelineInput="False" position="0" aliases="IncludeModules">
        <maml:name>IncludeInstalledModules</maml:name>
        <maml:description>
          <maml:para>Initiates a search for Plaster templates inside of installed modules.</maml:para>
        </maml:description>
        <command:parameterValue required="false" variableLength="false">SwitchParameter</command:parameterValue>
        <dev:type>
          <maml:name>SwitchParameter</maml:name>
          <maml:uri />
        </dev:type>
        <dev:defaultValue>False</dev:defaultValue>
      </command:parameter>
      <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
        <maml:name>ListAvailable</maml:name>
        <maml:description>
          <maml:para>If specified, searches for Plaster templates inside of all installed module versions.</maml:para>
        </maml:description>
        <command:parameterValue required="false" variableLength="false">SwitchParameter</command:parameterValue>
        <dev:type>
          <maml:name>SwitchParameter</maml:name>
          <maml:uri />
        </dev:type>
        <dev:defaultValue>False</dev:defaultValue>
      </command:parameter>
      <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="1" aliases="none">
        <maml:name>Name</maml:name>
        <maml:description>
          <maml:para>Limits the templates returned to those that match the template name. Wildcard characters are permitted.</maml:para>
        </maml:description>
        <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
        <dev:type>
          <maml:name>String</maml:name>
          <maml:uri />
        </dev:type>
        <dev:defaultValue>*</dev:defaultValue>
      </command:parameter>
      <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="0" aliases="none">
        <maml:name>Path</maml:name>
        <maml:description>
          <maml:para>Specifies a path to a folder containing a Plaster template or multiple template folders. Can also be a path to plasterManifest.xml.</maml:para>
        </maml:description>
        <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
        <dev:type>
          <maml:name>String</maml:name>
          <maml:uri />
        </dev:type>
        <dev:defaultValue>None</dev:defaultValue>
      </command:parameter>
      <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
        <maml:name>Recurse</maml:name>
        <maml:description>
          <maml:para>Indicates that this cmdlet gets the items in the specified locations and in all child items of the locations.</maml:para>
        </maml:description>
        <command:parameterValue required="false" variableLength="false">SwitchParameter</command:parameterValue>
        <dev:type>
          <maml:name>SwitchParameter</maml:name>
          <maml:uri />
        </dev:type>
        <dev:defaultValue>False</dev:defaultValue>
      </command:parameter>
      <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
        <maml:name>Tag</maml:name>
        <maml:description>
          <maml:para>Limits the templates returned to those that match the template tags. Wildcard characters are permitted.</maml:para>
        </maml:description>
        <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
        <dev:type>
          <maml:name>String</maml:name>
          <maml:uri />
        </dev:type>
        <dev:defaultValue>*</dev:defaultValue>
      </command:parameter>
    </command:parameters>
    <command:inputTypes>
      <command:inputType>
        <dev:type>
          <maml:name>System.String</maml:name>
        </dev:type>
        <maml:description>
          <maml:para>The first positional parameter is a filesystem path under which templates might be found.  The -Recurse switch will cause this path to be searched recursively.</maml:para>
        </maml:description>
      </command:inputType>
    </command:inputTypes>
    <command:returnValues>
      <command:returnValue>
        <dev:type>
          <maml:name>System.Object</maml:name>
        </dev:type>
        <maml:description>
          <maml:para>This output object provides the following properties:</maml:para>
          <maml:para>- Name: The name of the template</maml:para>
          <maml:para>- Title: The title of the template</maml:para>
          <maml:para>- Author: The author of the template</maml:para>
          <maml:para>- Version: The version of the template</maml:para>
          <maml:para>- Description: Text describing the template and what it creates</maml:para>
          <maml:para>- Tags: A list of tag strings which categorize the template</maml:para>
          <maml:para>- TemplatePath: The template's folder path in the filesystem</maml:para>
          <maml:para></maml:para>
          <maml:para>This output object provides the following methods:</maml:para>
          <maml:para>- InvokePlaster(): Runs Invoke-Plaster against the template</maml:para>
        </maml:description>
      </command:returnValue>
    </command:returnValues>
    <maml:alertSet>
      <maml:alert>
        <maml:para></maml:para>
      </maml:alert>
    </maml:alertSet>
    <command:examples>
      <command:example>
        <maml:title>-------------------------- Example 1 --------------------------</maml:title>
        <dev:code>PS C:\&gt; $templates = Get-PlasterTemplate

PS C:\&gt; Invoke-Plaster -TemplatePath $templates[0].TemplatePath -DestinationPath ~\GitHub\NewModule</dev:code>
        <dev:remarks>
          <maml:para>This will get the list of built-in Plaster templates.  The first template returned is then used to create a new module at the specifed path.</maml:para>
        </dev:remarks>
      </command:example>
      <command:example>
        <maml:title>-------------------------- Example 2 --------------------------</maml:title>
        <dev:code>PS C:\&gt; $templates = Get-PlasterTemplate -IncludeInstalledModules

PS C:\&gt; Invoke-Plaster -TemplatePath $templates[0].TemplatePath -DestinationPath ~\GitHub\NewModule</dev:code>
        <dev:remarks>
          <maml:para>This will get a list of Plaster templates, both built-in and included with installed modules.  The first template returned is then used to create a new module at the specifed path.</maml:para>
        </dev:remarks>
      </command:example>
      <command:example>
        <maml:title>-------------------------- Example 3 --------------------------</maml:title>
        <dev:code>PS C:\&gt; $templates = Get-PlasterTemplate -Path c:\MyPlasterTemplates -Recurse

PS C:\&gt; Invoke-Plaster -TemplatePath $templates[0].TemplatePath -DestinationPath ~\GitHub\NewModule</dev:code>
        <dev:remarks>
          <maml:para>This will get a list of Plaster templates found recursively under c:\MyPlasterTemplates The first template returned is then used to create a new module at the specifed path.</maml:para>
        </dev:remarks>
      </command:example>
      <command:example>
        <maml:title>-------------------------- Example 4 --------------------------</maml:title>
        <dev:code>PS C:\&gt; $template = Get-PlasterTemplate -Name NewPowerShellScriptModule

PS C:\&gt; Invoke-Plaster -TemplatePath $template.TemplatePath -DestinationPath ~\GitHub\NewModule</dev:code>
        <dev:remarks>
          <maml:para>This will get the built-in Plaster template with the name NewPowerShellScriptModule. It will then use that template to create a new module at the specified path.</maml:para>
        </dev:remarks>
      </command:example>
      <command:example>
        <maml:title>-------------------------- Example 5 --------------------------</maml:title>
        <dev:code>PS C:\&gt; $templates = Get-PlasterTemplate -IncludeInstalledModules -Name new*

PS C:\&gt; Invoke-Plaster -TemplatePath $templates[0].TemplatePath -DestinationPath ~\GitHub\NewModule</dev:code>
        <dev:remarks>
          <maml:para>This will get a list of Plaster templates, both built-in and included with installed modules, where the name matches 'new*'. It will then use the first template found to create a new module at the specified path.</maml:para>
        </dev:remarks>
      </command:example>
      <command:example>
        <maml:title>-------------------------- Example 6 --------------------------</maml:title>
        <dev:code>PS C:\&gt; $templates = Get-PlasterTemplate -IncludeInstalledModules -tag module*

PS C:\&gt; $templates[0].InvokePlaster()</dev:code>
        <dev:remarks>
          <maml:para>This will get a list of Plaster templates, both built-in and included with installed modules, where the name matches 'module*'. It will then use the first template found to create a new module at the specified path using the InvokePlaster script method that is available on the returned object.</maml:para>
        </dev:remarks>
      </command:example>
    </command:examples>
    <command:relatedLinks>
      <maml:navigationLink>
        <maml:linkText>Online Version:</maml:linkText>
        <maml:uri>https://github.com/PowerShell/Plaster/blob/master/docs/en-US/Get-PlasterTemplate.md</maml:uri>
      </maml:navigationLink>
      <maml:navigationLink>
        <maml:linkText>Invoke-Plaster</maml:linkText>
        <maml:uri>https://github.com/PowerShell/Plaster/blob/master/docs/en-US/Invoke-Plaster.md</maml:uri>
      </maml:navigationLink>
    </command:relatedLinks>
  </command:command>
  <command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10" xmlns:MSHelp="http://msdn.microsoft.com/mshelp">
    <command:details>
      <command:name>Invoke-Plaster</command:name>
      <command:verb>Invoke</command:verb>
      <command:noun>Plaster</command:noun>
      <maml:description>
        <maml:para>Invokes the specified Plaster template which will scaffold out a file or a set of files and directories.</maml:para>
      </maml:description>
    </command:details>
    <maml:description>
      <maml:para>Invokes the specified Plaster template which will scaffold out a file or a set of files and directories.</maml:para>
    </maml:description>
    <command:syntax>
      <command:syntaxItem>
        <maml:name>Invoke-Plaster</maml:name>
        <command:parameter required="true" variableLength="true" globbing="false" pipelineInput="False" position="0" aliases="none">
          <maml:name>TemplatePath</maml:name>
          <maml:description>
            <maml:para>Specifies the path to the template directory.</maml:para>
          </maml:description>
          <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
          <dev:type>
            <maml:name>String</maml:name>
            <maml:uri />
          </dev:type>
          <dev:defaultValue>None</dev:defaultValue>
        </command:parameter>
        <command:parameter required="true" variableLength="true" globbing="false" pipelineInput="False" position="1" aliases="none">
          <maml:name>DestinationPath</maml:name>
          <maml:description>
            <maml:para>Specifies the path to directory in which the template will use as a root directory when generating files. If the directory does not exist, it will be created.</maml:para>
          </maml:description>
          <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
          <dev:type>
            <maml:name>String</maml:name>
            <maml:uri />
          </dev:type>
          <dev:defaultValue>None</dev:defaultValue>
        </command:parameter>
        <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="cf">
          <maml:name>Confirm</maml:name>
          <maml:description>
            <maml:para>Prompts you for confirmation before running the cmdlet.</maml:para>
          </maml:description>
          <dev:type>
            <maml:name>SwitchParameter</maml:name>
            <maml:uri />
          </dev:type>
          <dev:defaultValue>False</dev:defaultValue>
        </command:parameter>
        <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
          <maml:name>Force</maml:name>
          <maml:description>
            <maml:para>Specify Force to override user prompts for conflicting handling. This will override the confirmation prompt and allow the template to overwrite existing files.</maml:para>
          </maml:description>
          <dev:type>
            <maml:name>SwitchParameter</maml:name>
            <maml:uri />
          </dev:type>
          <dev:defaultValue>False</dev:defaultValue>
        </command:parameter>
        <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
          <maml:name>NoLogo</maml:name>
          <maml:description>
            <maml:para>Suppresses the display of the Plaster logo.</maml:para>
          </maml:description>
          <dev:type>
            <maml:name>SwitchParameter</maml:name>
            <maml:uri />
          </dev:type>
          <dev:defaultValue>False</dev:defaultValue>
        </command:parameter>
        <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
          <maml:name>PassThru</maml:name>
          <maml:description>
            <maml:para>Returns an InvokePlasterInfo object with the following fields:</maml:para>
            <maml:para>* TemplatePath</maml:para>
            <maml:para>* DestinationPath</maml:para>
            <maml:para>* Success</maml:para>
            <maml:para>* CreatedFiles</maml:para>
            <maml:para>* UpdatedFiles</maml:para>
            <maml:para>* MissingModules</maml:para>
          </maml:description>
          <dev:type>
            <maml:name>SwitchParameter</maml:name>
            <maml:uri />
          </dev:type>
          <dev:defaultValue>False</dev:defaultValue>
        </command:parameter>
        <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="wi">
          <maml:name>WhatIf</maml:name>
          <maml:description>
            <maml:para>Shows what would happen if the cmdlet runs. The cmdlet is not run.</maml:para>
          </maml:description>
          <dev:type>
            <maml:name>SwitchParameter</maml:name>
            <maml:uri />
          </dev:type>
          <dev:defaultValue>False</dev:defaultValue>
        </command:parameter>
      </command:syntaxItem>
    </command:syntax>
    <command:parameters>
      <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="cf">
        <maml:name>Confirm</maml:name>
        <maml:description>
          <maml:para>Prompts you for confirmation before running the cmdlet.</maml:para>
        </maml:description>
        <command:parameterValue required="false" variableLength="false">SwitchParameter</command:parameterValue>
        <dev:type>
          <maml:name>SwitchParameter</maml:name>
          <maml:uri />
        </dev:type>
        <dev:defaultValue>False</dev:defaultValue>
      </command:parameter>
      <command:parameter required="true" variableLength="true" globbing="false" pipelineInput="False" position="1" aliases="none">
        <maml:name>DestinationPath</maml:name>
        <maml:description>
          <maml:para>Specifies the path to directory in which the template will use as a root directory when generating files. If the directory does not exist, it will be created.</maml:para>
        </maml:description>
        <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
        <dev:type>
          <maml:name>String</maml:name>
          <maml:uri />
        </dev:type>
        <dev:defaultValue>None</dev:defaultValue>
      </command:parameter>
      <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
        <maml:name>Force</maml:name>
        <maml:description>
          <maml:para>Specify Force to override user prompts for conflicting handling. This will override the confirmation prompt and allow the template to overwrite existing files.</maml:para>
        </maml:description>
        <command:parameterValue required="false" variableLength="false">SwitchParameter</command:parameterValue>
        <dev:type>
          <maml:name>SwitchParameter</maml:name>
          <maml:uri />
        </dev:type>
        <dev:defaultValue>False</dev:defaultValue>
      </command:parameter>
      <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
        <maml:name>NoLogo</maml:name>
        <maml:description>
          <maml:para>Suppresses the display of the Plaster logo.</maml:para>
        </maml:description>
        <command:parameterValue required="false" variableLength="false">SwitchParameter</command:parameterValue>
        <dev:type>
          <maml:name>SwitchParameter</maml:name>
          <maml:uri />
        </dev:type>
        <dev:defaultValue>False</dev:defaultValue>
      </command:parameter>
      <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
        <maml:name>PassThru</maml:name>
        <maml:description>
          <maml:para>Returns an InvokePlasterInfo object with the following fields:</maml:para>
          <maml:para>* TemplatePath</maml:para>
          <maml:para>* DestinationPath</maml:para>
          <maml:para>* Success</maml:para>
          <maml:para>* CreatedFiles</maml:para>
          <maml:para>* UpdatedFiles</maml:para>
          <maml:para>* MissingModules</maml:para>
        </maml:description>
        <command:parameterValue required="false" variableLength="false">SwitchParameter</command:parameterValue>
        <dev:type>
          <maml:name>SwitchParameter</maml:name>
          <maml:uri />
        </dev:type>
        <dev:defaultValue>False</dev:defaultValue>
      </command:parameter>
      <command:parameter required="true" variableLength="true" globbing="false" pipelineInput="False" position="0" aliases="none">
        <maml:name>TemplatePath</maml:name>
        <maml:description>
          <maml:para>Specifies the path to the template directory.</maml:para>
        </maml:description>
        <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
        <dev:type>
          <maml:name>String</maml:name>
          <maml:uri />
        </dev:type>
        <dev:defaultValue>None</dev:defaultValue>
      </command:parameter>
      <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="wi">
        <maml:name>WhatIf</maml:name>
        <maml:description>
          <maml:para>Shows what would happen if the cmdlet runs. The cmdlet is not run.</maml:para>
        </maml:description>
        <command:parameterValue required="false" variableLength="false">SwitchParameter</command:parameterValue>
        <dev:type>
          <maml:name>SwitchParameter</maml:name>
          <maml:uri />
        </dev:type>
        <dev:defaultValue>False</dev:defaultValue>
      </command:parameter>
    </command:parameters>
    <command:inputTypes />
    <command:returnValues />
    <maml:alertSet>
      <maml:alert>
        <maml:para></maml:para>
      </maml:alert>
    </maml:alertSet>
    <command:examples>
      <command:example>
        <maml:title>-------------------------- EXAMPLE 1 --------------------------</maml:title>
        <dev:code>Invoke-Plaster -TemplatePath . -Destination ~\GitHub\NewModule</dev:code>
        <dev:remarks>
          <maml:para>This will invoke the Plaster template in the current directory. The template will generate any files and directories in the ~\GitHub\NewModule directory.</maml:para>
        </dev:remarks>
      </command:example>
      <command:example>
        <maml:title>-------------------------- EXAMPLE 2 --------------------------</maml:title>
        <dev:code>Invoke-Plaster -TemplatePath . -Destination ~\GitHub\NewModule -ModuleName Foo -Version 1.0.0</dev:code>
        <dev:remarks>
          <maml:para>This will invoke the Plaster template in the current directory using dynamic parameters ModuleName and Version extracted from the parameters section of the manifest file. The template will generate any files and directories in the ~\GitHub\NewModule directory.</maml:para>
          <maml:para>Note: The parameters -ModuleName and -Version are dynamically added from the plaster manifest file in the current directory. If you run this command it may fail if the manifest file you are testing with does not contain these parameters.</maml:para>
        </dev:remarks>
      </command:example>
    </command:examples>
    <command:relatedLinks>
      <maml:navigationLink>
        <maml:linkText>Online Version:</maml:linkText>
        <maml:uri>https://github.com/PowerShell/Plaster/blob/master/docs/en-US/Invoke-Plaster.md</maml:uri>
      </maml:navigationLink>
      <maml:navigationLink>
        <maml:linkText>New-PlasterManifest</maml:linkText>
        <maml:uri>https://github.com/PowerShell/Plaster/blob/master/docs/en-US/New-PlasterManifest.md</maml:uri>
      </maml:navigationLink>
      <maml:navigationLink>
        <maml:linkText>Test-PlasterManifest</maml:linkText>
        <maml:uri>https://github.com/PowerShell/Plaster/blob/master/docs/en-US/Test-PlasterManifest.md</maml:uri>
      </maml:navigationLink>
    </command:relatedLinks>
  </command:command>
  <command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10" xmlns:MSHelp="http://msdn.microsoft.com/mshelp">
    <command:details>
      <command:name>New-PlasterManifest</command:name>
      <command:verb>New</command:verb>
      <command:noun>PlasterManifest</command:noun>
      <maml:description>
        <maml:para>Creates a new Plaster template manifest file.</maml:para>
      </maml:description>
    </command:details>
    <maml:description>
      <maml:para>The New-PlasterManifest command creates a new Plaster manifest file, populates its values, and saves the manifest file in the specified path.</maml:para>
      <maml:para>Template authors can use this command to create a manifest for their template. A template manifest is a file named plasterManifest.xml or plasterManifest_&lt;culture-name&gt;.xml. The information stored in the manifest is used to scaffold files and folders.</maml:para>
      <maml:para>The metadata section of the manifest is used to supply information about the template e.g. a unique id, name, version, title, author and tags.</maml:para>
      <maml:para>The parameters section of the manifest is used to describe choices the template user can choose from. Those choices are then used to conditionally create files and folders and modify existing files under the specified destination path.</maml:para>
      <maml:para>The content section is used to specify what actions the template will perform under the user's chosen destination directory. This includes copying files to the destination, copy &amp; expanding template files, modifying files, verifying required modules are installed and displaying messages to the user.</maml:para>
      <maml:para>See the help topic about_Plaster_CreatingAManifest for more details on authoring a Plaster manifest file.</maml:para>
    </maml:description>
    <command:syntax>
      <command:syntaxItem>
        <maml:name>New-PlasterManifest</maml:name>
        <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="0" aliases="none">
          <maml:name>Path</maml:name>
          <maml:description>
            <maml:para>Specifies the path and file name of the new Plaster manifest. Enter a path and file name with a .xml extension, such as $pshome\Modules\MyPlasterTemplate\plasterManifest.xml. NOTE: Plaster requires the manifest file be named either plasterManifest.xml OR plasterManifest_&lt;culture-name&gt;.xml e.g. plasterManifest_fr-FR.xml. The default, if no value is provided is to create plasterManifest.xml in the current directory.</maml:para>
          </maml:description>
          <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
          <dev:type>
            <maml:name>String</maml:name>
            <maml:uri />
          </dev:type>
          <dev:defaultValue>"$pwd\plasterManifest.xml"</dev:defaultValue>
        </command:parameter>
        <command:parameter required="true" variableLength="true" globbing="false" pipelineInput="False" position="1" aliases="none">
          <maml:name>TemplateName</maml:name>
          <maml:description>
            <maml:para>Specifies the name of the template. A template name is required. For localized manifests, this value should not be localized. The name is limited to the characters: aA-zZ0-9_-.</maml:para>
          </maml:description>
          <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
          <dev:type>
            <maml:name>String</maml:name>
            <maml:uri />
          </dev:type>
          <dev:defaultValue>None</dev:defaultValue>
        </command:parameter>
        <command:parameter required="true" variableLength="true" globbing="false" pipelineInput="False" position="2" aliases="none">
          <maml:name>TemplateType</maml:name>
          <maml:description>
            <maml:para>Defines the type of template.  Valid values are: Project and Item.  This value is used by editor extensions like the PowerShell extension for Visual Studio Code to determine if the template expects to create a whole new project in an empty workspace or if it adds an item to an existing workspace.</maml:para>
          </maml:description>
          <command:parameterValueGroup>
            <command:parameterValue required="false" command:variableLength="false">Item</command:parameterValue>
            <command:parameterValue required="false" command:variableLength="false">Project</command:parameterValue>
          </command:parameterValueGroup>
          <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
          <dev:type>
            <maml:name>String</maml:name>
            <maml:uri />
          </dev:type>
          <dev:defaultValue>None</dev:defaultValue>
        </command:parameter>
        <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="3" aliases="none">
          <maml:name>Id</maml:name>
          <maml:description>
            <maml:para>Unique identifier for all versions of this template. The id is a GUID. Use the same id for each version of your template. This will prevent editor environments from listing multiple, installed versions of your template. When you keep your template id the same, the editor will list only the latest version of your template.</maml:para>
          </maml:description>
          <command:parameterValue required="true" variableLength="false">Guid</command:parameterValue>
          <dev:type>
            <maml:name>Guid</maml:name>
            <maml:uri />
          </dev:type>
          <dev:defaultValue>[guid]::NewGuid()</dev:defaultValue>
        </command:parameter>
        <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="4" aliases="none">
          <maml:name>TemplateVersion</maml:name>
          <maml:description>
            <maml:para>Specifies the version of the template.</maml:para>
          </maml:description>
          <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
          <dev:type>
            <maml:name>String</maml:name>
            <maml:uri />
          </dev:type>
          <dev:defaultValue>1.0.0</dev:defaultValue>
        </command:parameter>
        <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="5" aliases="none">
          <maml:name>Title</maml:name>
          <maml:description>
            <maml:para>Title of the Plaster template. This string is typically used in an editor like VSCode when displaying a list of Plaster templates. A typical title might be "New DSC Resource" or "New PowerShell Module".</maml:para>
          </maml:description>
          <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
          <dev:type>
            <maml:name>String</maml:name>
            <maml:uri />
          </dev:type>
          <dev:defaultValue>$Name</dev:defaultValue>
        </command:parameter>
        <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="6" aliases="none">
          <maml:name>Description</maml:name>
          <maml:description>
            <maml:para>Description of the Plaster template. This describes what the the template is for. It is typically used in an editor like VSCode when displaying additional information about a Plaster template. A typical title might be "Creates files required for a PowerShell module with optional support for Pester tests, building with psake and publishing to the PowerShell Gallery."</maml:para>
          </maml:description>
          <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
          <dev:type>
            <maml:name>String</maml:name>
            <maml:uri />
          </dev:type>
          <dev:defaultValue>None</dev:defaultValue>
        </command:parameter>
        <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="7" aliases="none">
          <maml:name>Tags</maml:name>
          <maml:description>
            <maml:para>Specifies an array of tags for the template. Users can search for templates based on these tags.</maml:para>
          </maml:description>
          <command:parameterValue required="true" variableLength="false">String[]</command:parameterValue>
          <dev:type>
            <maml:name>String[]</maml:name>
            <maml:uri />
          </dev:type>
          <dev:defaultValue>None</dev:defaultValue>
        </command:parameter>
        <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="8" aliases="none">
          <maml:name>Author</maml:name>
          <maml:description>
            <maml:para>Specifies the author of the template.</maml:para>
          </maml:description>
          <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
          <dev:type>
            <maml:name>String</maml:name>
            <maml:uri />
          </dev:type>
          <dev:defaultValue>None</dev:defaultValue>
        </command:parameter>
        <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
          <maml:name>AddContent</maml:name>
          <maml:description>
            <maml:para>If specified, the contents of the directory the manifest is being created in will be added to the manifest's content element.</maml:para>
          </maml:description>
          <dev:type>
            <maml:name>SwitchParameter</maml:name>
            <maml:uri />
          </dev:type>
          <dev:defaultValue>False</dev:defaultValue>
        </command:parameter>
        <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="cf">
          <maml:name>Confirm</maml:name>
          <maml:description>
            <maml:para>Prompts you for confirmation before running the cmdlet.</maml:para>
          </maml:description>
          <dev:type>
            <maml:name>SwitchParameter</maml:name>
            <maml:uri />
          </dev:type>
          <dev:defaultValue>False</dev:defaultValue>
        </command:parameter>
        <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="wi">
          <maml:name>WhatIf</maml:name>
          <maml:description>
            <maml:para>Shows what would happen if the cmdlet runs. The cmdlet is not run.</maml:para>
          </maml:description>
          <dev:type>
            <maml:name>SwitchParameter</maml:name>
            <maml:uri />
          </dev:type>
          <dev:defaultValue>False</dev:defaultValue>
        </command:parameter>
      </command:syntaxItem>
    </command:syntax>
    <command:parameters>
      <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
        <maml:name>AddContent</maml:name>
        <maml:description>
          <maml:para>If specified, the contents of the directory the manifest is being created in will be added to the manifest's content element.</maml:para>
        </maml:description>
        <command:parameterValue required="false" variableLength="false">SwitchParameter</command:parameterValue>
        <dev:type>
          <maml:name>SwitchParameter</maml:name>
          <maml:uri />
        </dev:type>
        <dev:defaultValue>False</dev:defaultValue>
      </command:parameter>
      <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="8" aliases="none">
        <maml:name>Author</maml:name>
        <maml:description>
          <maml:para>Specifies the author of the template.</maml:para>
        </maml:description>
        <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
        <dev:type>
          <maml:name>String</maml:name>
          <maml:uri />
        </dev:type>
        <dev:defaultValue>None</dev:defaultValue>
      </command:parameter>
      <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="cf">
        <maml:name>Confirm</maml:name>
        <maml:description>
          <maml:para>Prompts you for confirmation before running the cmdlet.</maml:para>
        </maml:description>
        <command:parameterValue required="false" variableLength="false">SwitchParameter</command:parameterValue>
        <dev:type>
          <maml:name>SwitchParameter</maml:name>
          <maml:uri />
        </dev:type>
        <dev:defaultValue>False</dev:defaultValue>
      </command:parameter>
      <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="6" aliases="none">
        <maml:name>Description</maml:name>
        <maml:description>
          <maml:para>Description of the Plaster template. This describes what the the template is for. It is typically used in an editor like VSCode when displaying additional information about a Plaster template. A typical title might be "Creates files required for a PowerShell module with optional support for Pester tests, building with psake and publishing to the PowerShell Gallery."</maml:para>
        </maml:description>
        <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
        <dev:type>
          <maml:name>String</maml:name>
          <maml:uri />
        </dev:type>
        <dev:defaultValue>None</dev:defaultValue>
      </command:parameter>
      <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="3" aliases="none">
        <maml:name>Id</maml:name>
        <maml:description>
          <maml:para>Unique identifier for all versions of this template. The id is a GUID. Use the same id for each version of your template. This will prevent editor environments from listing multiple, installed versions of your template. When you keep your template id the same, the editor will list only the latest version of your template.</maml:para>
        </maml:description>
        <command:parameterValue required="true" variableLength="false">Guid</command:parameterValue>
        <dev:type>
          <maml:name>Guid</maml:name>
          <maml:uri />
        </dev:type>
        <dev:defaultValue>[guid]::NewGuid()</dev:defaultValue>
      </command:parameter>
      <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="0" aliases="none">
        <maml:name>Path</maml:name>
        <maml:description>
          <maml:para>Specifies the path and file name of the new Plaster manifest. Enter a path and file name with a .xml extension, such as $pshome\Modules\MyPlasterTemplate\plasterManifest.xml. NOTE: Plaster requires the manifest file be named either plasterManifest.xml OR plasterManifest_&lt;culture-name&gt;.xml e.g. plasterManifest_fr-FR.xml. The default, if no value is provided is to create plasterManifest.xml in the current directory.</maml:para>
        </maml:description>
        <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
        <dev:type>
          <maml:name>String</maml:name>
          <maml:uri />
        </dev:type>
        <dev:defaultValue>"$pwd\plasterManifest.xml"</dev:defaultValue>
      </command:parameter>
      <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="7" aliases="none">
        <maml:name>Tags</maml:name>
        <maml:description>
          <maml:para>Specifies an array of tags for the template. Users can search for templates based on these tags.</maml:para>
        </maml:description>
        <command:parameterValue required="true" variableLength="false">String[]</command:parameterValue>
        <dev:type>
          <maml:name>String[]</maml:name>
          <maml:uri />
        </dev:type>
        <dev:defaultValue>None</dev:defaultValue>
      </command:parameter>
      <command:parameter required="true" variableLength="true" globbing="false" pipelineInput="False" position="1" aliases="none">
        <maml:name>TemplateName</maml:name>
        <maml:description>
          <maml:para>Specifies the name of the template. A template name is required. For localized manifests, this value should not be localized. The name is limited to the characters: aA-zZ0-9_-.</maml:para>
        </maml:description>
        <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
        <dev:type>
          <maml:name>String</maml:name>
          <maml:uri />
        </dev:type>
        <dev:defaultValue>None</dev:defaultValue>
      </command:parameter>
      <command:parameter required="true" variableLength="true" globbing="false" pipelineInput="False" position="2" aliases="none">
        <maml:name>TemplateType</maml:name>
        <maml:description>
          <maml:para>Defines the type of template.  Valid values are: Project and Item.  This value is used by editor extensions like the PowerShell extension for Visual Studio Code to determine if the template expects to create a whole new project in an empty workspace or if it adds an item to an existing workspace.</maml:para>
        </maml:description>
        <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
        <dev:type>
          <maml:name>String</maml:name>
          <maml:uri />
        </dev:type>
        <dev:defaultValue>None</dev:defaultValue>
      </command:parameter>
      <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="4" aliases="none">
        <maml:name>TemplateVersion</maml:name>
        <maml:description>
          <maml:para>Specifies the version of the template.</maml:para>
        </maml:description>
        <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
        <dev:type>
          <maml:name>String</maml:name>
          <maml:uri />
        </dev:type>
        <dev:defaultValue>1.0.0</dev:defaultValue>
      </command:parameter>
      <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="5" aliases="none">
        <maml:name>Title</maml:name>
        <maml:description>
          <maml:para>Title of the Plaster template. This string is typically used in an editor like VSCode when displaying a list of Plaster templates. A typical title might be "New DSC Resource" or "New PowerShell Module".</maml:para>
        </maml:description>
        <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
        <dev:type>
          <maml:name>String</maml:name>
          <maml:uri />
        </dev:type>
        <dev:defaultValue>$Name</dev:defaultValue>
      </command:parameter>
      <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="wi">
        <maml:name>WhatIf</maml:name>
        <maml:description>
          <maml:para>Shows what would happen if the cmdlet runs. The cmdlet is not run.</maml:para>
        </maml:description>
        <command:parameterValue required="false" variableLength="false">SwitchParameter</command:parameterValue>
        <dev:type>
          <maml:name>SwitchParameter</maml:name>
          <maml:uri />
        </dev:type>
        <dev:defaultValue>False</dev:defaultValue>
      </command:parameter>
    </command:parameters>
    <command:inputTypes>
      <command:inputType>
        <dev:type>
          <maml:name>None</maml:name>
        </dev:type>
        <maml:description>
          <maml:para>You cannot pipe input to this cmdlet.</maml:para>
        </maml:description>
      </command:inputType>
    </command:inputTypes>
    <command:returnValues>
      <command:returnValue>
        <dev:type>
          <maml:name>None</maml:name>
        </dev:type>
        <maml:description>
          <maml:para></maml:para>
        </maml:description>
      </command:returnValue>
    </command:returnValues>
    <maml:alertSet>
      <maml:alert>
        <maml:para></maml:para>
      </maml:alert>
    </maml:alertSet>
    <command:examples>
      <command:example>
        <maml:title>-------------------------- EXAMPLE 1 --------------------------</maml:title>
        <dev:code>New-PlasterManifest -TemplateName NewPowerShellItem -TemplateType Item</dev:code>
        <dev:remarks>
          <maml:para>Creates a basic plasterManifest.xml file in the current directory.</maml:para>
        </dev:remarks>
      </command:example>
      <command:example>
        <maml:title>-------------------------- EXAMPLE 2 --------------------------</maml:title>
        <dev:code>New-PlasterManifest -TemplateName NewPowerShellItem -TemplateType Item -TemplateVersion 0.1.0 -Description "Some description." -Tags Module, Publish,Build</dev:code>
        <dev:remarks>
          <maml:para>Creates a plasterManifest.xml file in the current directory with the version set to 0.1.0 and with the Description and Tags elements populated.</maml:para>
        </dev:remarks>
      </command:example>
      <command:example>
        <maml:title>-------------------------- EXAMPLE 3 --------------------------</maml:title>
        <dev:code>New-PlasterManifest -TemplateName NewPowerShellItem -TemplateType Item -AddContent</dev:code>
        <dev:remarks>
          <maml:para>Creates a plasterManifest.xml file in the current directory with the content element filled in with all the files (except for any plasterManifest files) in and below the specified directory which defaults to the current directory.</maml:para>
        </dev:remarks>
      </command:example>
    </command:examples>
    <command:relatedLinks>
      <maml:navigationLink>
        <maml:linkText>Online Version:</maml:linkText>
        <maml:uri>https://github.com/PowerShell/Plaster/blob/master/docs/en-US/New-PlasterManifest.md</maml:uri>
      </maml:navigationLink>
      <maml:navigationLink>
        <maml:linkText>Invoke-Plaster</maml:linkText>
        <maml:uri>https://github.com/PowerShell/Plaster/blob/master/docs/en-US/Invoke-Plaster.md</maml:uri>
      </maml:navigationLink>
      <maml:navigationLink>
        <maml:linkText>Test-PlasterManifest</maml:linkText>
        <maml:uri>https://github.com/PowerShell/Plaster/blob/master/docs/en-US/Test-PlasterManifest.md</maml:uri>
      </maml:navigationLink>
    </command:relatedLinks>
  </command:command>
  <command:command xmlns:maml="http://schemas.microsoft.com/maml/2004/10" xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10" xmlns:MSHelp="http://msdn.microsoft.com/mshelp">
    <command:details>
      <command:name>Test-PlasterManifest</command:name>
      <command:verb>Test</command:verb>
      <command:noun>PlasterManifest</command:noun>
      <maml:description>
        <maml:para>Verifies that a plaster manifest file is a valid.</maml:para>
      </maml:description>
    </command:details>
    <maml:description>
      <maml:para>Verifies that a plaster manifest file is a valid. If there are any errors, the details of the errors can be viewed by using the Verbose parameter.</maml:para>
    </maml:description>
    <command:syntax>
      <command:syntaxItem>
        <maml:name>Test-PlasterManifest</maml:name>
        <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="True (ByPropertyName, ByValue)" position="0" aliases="PSPath">
          <maml:name>Path</maml:name>
          <maml:description>
            <maml:para>Specifies a path to a plasterManifest.xml file.</maml:para>
          </maml:description>
          <command:parameterValue required="true" variableLength="false">String[]</command:parameterValue>
          <dev:type>
            <maml:name>String[]</maml:name>
            <maml:uri />
          </dev:type>
          <dev:defaultValue>"$pwd\plasterManifest.xml"</dev:defaultValue>
        </command:parameter>
      </command:syntaxItem>
    </command:syntax>
    <command:parameters>
      <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="True (ByPropertyName, ByValue)" position="0" aliases="PSPath">
        <maml:name>Path</maml:name>
        <maml:description>
          <maml:para>Specifies a path to a plasterManifest.xml file.</maml:para>
        </maml:description>
        <command:parameterValue required="true" variableLength="false">String[]</command:parameterValue>
        <dev:type>
          <maml:name>String[]</maml:name>
          <maml:uri />
        </dev:type>
        <dev:defaultValue>"$pwd\plasterManifest.xml"</dev:defaultValue>
      </command:parameter>
    </command:parameters>
    <command:inputTypes>
      <command:inputType>
        <dev:type>
          <maml:name>System.String</maml:name>
        </dev:type>
        <maml:description>
          <maml:para>You can pipe the path to a plaster manifest to Test-PlasterManifest.</maml:para>
        </maml:description>
      </command:inputType>
    </command:inputTypes>
    <command:returnValues>
      <command:returnValue>
        <dev:type>
          <maml:name>System.Xml.XmlDocument</maml:name>
        </dev:type>
        <maml:description>
          <maml:para>Test-PlasterManifest returns a System.Xml.XmlDocument if the manifest is valid. Otherwise it returns $null.</maml:para>
        </maml:description>
      </command:returnValue>
    </command:returnValues>
    <maml:alertSet>
      <maml:alert>
        <maml:para></maml:para>
      </maml:alert>
    </maml:alertSet>
    <command:examples>
      <command:example>
        <maml:title>-------------------------- EXAMPLE 1 --------------------------</maml:title>
        <dev:code>Test-PlasterManifest MyTemplate\plasterManifest.xml</dev:code>
        <dev:remarks>
          <maml:para>Verifies that the plasterManifest.xml file in the MyTemplate sub-directory is valid.</maml:para>
        </dev:remarks>
      </command:example>
      <command:example>
        <maml:title>-------------------------- EXAMPLE 2 --------------------------</maml:title>
        <dev:code>Test-PlasterManifest plasterManifest.xml -Verbose</dev:code>
        <dev:remarks>
          <maml:para>Verifies that the plasterManifest.xml file in the current directory is valid. If there are any validation errors, using -Verbose will display the details of those errors.</maml:para>
        </dev:remarks>
      </command:example>
    </command:examples>
    <command:relatedLinks>
      <maml:navigationLink>
        <maml:linkText>Online Version:</maml:linkText>
        <maml:uri>https://github.com/PowerShell/Plaster/blob/master/docs/en-US/Test-PlasterManifest.md</maml:uri>
      </maml:navigationLink>
      <maml:navigationLink>
        <maml:linkText>Invoke-Plaster</maml:linkText>
        <maml:uri>https://github.com/PowerShell/Plaster/blob/master/docs/en-US/Invoke-Plaster.md</maml:uri>
      </maml:navigationLink>
      <maml:navigationLink>
        <maml:linkText>New-PlasterManifest</maml:linkText>
        <maml:uri>https://github.com/PowerShell/Plaster/blob/master/docs/en-US/New-PlasterManifest.md</maml:uri>
      </maml:navigationLink>
    </command:relatedLinks>
  </command:command>
</helpItems>
tools\Plaster\en-US\Plaster.Resources.psd1
# Localized PlasterResources.psd1

ConvertFrom-StringData @'
###PSLOC
DestPath_F1=Destination path: {0}
ErrorFailedToLoadStoreFile_F1=Failed to load the default value store file: '{0}'.
ErrorProcessingDynamicParams_F1=Failed to create dynamic parameters from the template's manifest file.  Template-based dynamic parameters will not be available until the error is corrected.  The error was: {0}
ErrorTemplatePathIsInvalid_F1=The TemplatePath parameter value must refer to an existing directory. The specified path '{0}' does not.
ErrorUnencryptingSecureString_F1=Failed to unencrypt value for parameter '{0}'.
ErrorPathDoesNotExist_F1=Cannot find path '{0}' because it does not exist.
ErrorPathMustBeRelativePath_F2=The path '{0}' specified in the {1} directive in the template manifest cannot be an absolute path.  Change the path to a relative path.
ErrorPathMustBeUnderDestPath_F2=The path '{0}' must be under the specified DestinationPath '{1}'.
ExpressionInvalid_F2=The expression '{0}' is invalid or threw an exception. Error: {1}
ExpressionNonTermErrors_F2=The expression '{0}' generated error output - {1}
ExpressionExecError_F2=PowerShell expression failed execution. Location: {0}. Error: {1}
ExpressionErrorLocationFile_F2=<{0}> attribute '{1}'
ExpressionErrorLocationModify_F1=<modify> attribute '{0}'
ExpressionErrorLocationNewModManifest_F1=<newModuleManifest> attribute '{0}'
ExpressionErrorLocationParameter_F2=<parameter> name='{0}', attribute '{1}'
ExpressionErrorLocationRequireModule_F2=<requireModule> name='{0}', attribute '{1}'
ExpressionInvalidCondition_F3=The Plaster manifest condition '{0}' failed. Location: {1}. Error: {2}
InterpolationError_F3=The Plaster manifest attribute value '{0}' failed string interpolation. Location: {1}. Error: {2}
FileConflict=Plaster file conflict
ManifestFileMissing_F1=The Plaster manifest file '{0}' was not found.
ManifestMissingDocElement_F2=The Plaster manifest file '{0}' is missing the document element. It should be specified as <plasterManifest xmlns="{1}"></plasterManifest>.
ManifestMissingDocTargetNamespace_F2=The Plaster manifest file '{0}' is missing or has an invalid target namespace on the document element. It should be specified as <plasterManifest xmlns="{1}"></plasterManifest>.
ManifestPlasterVersionNotSupported_F2=The template file '{0}' specifies a plasterVersion of {1} which is greater than the installed version of Plaster. Update the Plaster module and try again.
ManifestSchemaInvalidAttrValue_F5=Invalid '{0}' attribute value '{1}' on '{2}' element in file '{3}'. Error: {4}
ManifestSchemaInvalidCondition_F3=Invalid condition '{0}' in file '{1}'. Error: {2}
ManifestSchemaInvalidChoiceDefault_F3=Invalid default attribute value '{0}' for parameter '{1}' in file '{2}'. The default value must specify a zero-based integer index that corresponds to the default choice.
ManifestSchemaInvalidMultichoiceDefault_F3=Invalid default attribute value '{0}' for parameter '{1}' in file '{2}'. The default value must specify one or more zero-based integer indexes in a comma separated list that correspond to the default choices.
ManifestSchemaInvalidRequireModuleAttrs_F2=The requireModule attribute 'requiredVersion' for module '{0}' in file '{1}' cannot be used together with either the 'minimumVersion' or 'maximumVersion' attribute.
ManifestSchemaValidationError_F2=Plaster manifest schema error in file '{0}'. Error: {1}
ManifestSchemaVersionNotSupported_F2=The template's manifest schema version ({0}) in file '{1}' requires a newer version of Plaster. Update the Plaster module and try again.
ManifestErrorReading_F1=Error reading Plaster manifest: {0}
ManifestNotValid_F1=The Plaster manifest '{0}' is not valid.
ManifestNotValidVerbose_F1=The Plaster manifest '{0}' is not valid. Specify -Verbose to see the specific schema errors.
ManifestNotWellFormedXml_F2=The Plaster manifest '{0}' is not a well-formed XML file. {1}
ManifestWrongFilename_F1=The Plaster manifest filename '{0}' is not valid. The value of the Path argument must refer to a file named 'plasterManifest.xml' or 'plasterManifest_<culture>.xml'. Change the Plaster manifest filename and then try again.
MissingParameterPrompt_F1=<Missing prompt value for parameter '{0}'>
NewModManifest_CreatingDir_F1=Creating destination directory for module manifest: {0}
OpConflict=Conflict
OpCreate=Create
OpForce=Force
OpIdentical=Identical
OpMissing=Missing
OpModify=Modify
OpUpdate=Update
OpVerify=Verify
OverwriteFile_F1=Overwrite {0}
ParameterTypeChoiceMultipleDefault_F1=Parameter name {0} is of type='choice' and can only have one default value.
RequireModuleVerified_F2=The required module {0}{1} is already installed.
RequireModuleMissing_F2=The required module {0}{1} was not found.
RequireModuleMinVersion_F1=minimum version: {0}
RequireModuleMaxVersion_F1=maximum version: {0}
RequireModuleRequiredVersion_F1=required version: {0}
ShouldCreateNewPlasterManifest=Create Plaster manifest
ShouldProcessCreateDir=Create directory
ShouldProcessExpandTemplate=Expand template file
ShouldProcessNewModuleManifest=Create new module manifest
TempFileOperation_F1={0} into temp file before copying to destination
TempFileTarget_F1=temp file for '{0}'
TestPlasterNoXmlSchemaValidationWarning=The version of .NET Core that PowerShell is running on does not support XML schema-based validation. Test-PlasterManifest will operate in "limited validation" mode primarily verifying the specified manifest file is well-formed XML. For full, XML schema-based validation, run this command on Windows PowerShell.
UnrecognizedParametersElement_F1=Unrecognized manifest parameters child element: {0}.
UnrecognizedParameterType_F2=Unrecognized parameter type '{0}' on parameter name '{1}'.
UnrecognizedContentElement_F1=Unrecognized manifest content child element: {0}.
###PSLOC
'@
tools\Plaster\Schema\PlasterManifest-v1.xsd
 
tools\Plaster\Templates\AddPSScriptAnalyzerSettings\plasterManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<plasterManifest schemaVersion="0.4"
                 templateType="Item"
                 xmlns="http://www.microsoft.com/schemas/PowerShell/Plaster/v1">
  <metadata>
    <name>AddPSScriptAnalyzerSettings</name>
    <id>f58338ce-b153-4b70-80b8-c7dc63074f7d</id>
    <version>1.0.0</version>
    <title>AddPSScriptAnalyzerSettings</title>
    <description>Add a PowerShell Script Analyzer settings file to the root of your workspace.</description>
    <author>Plaster project</author>
    <tags>PSScriptAnalyzer, settings</tags>
  </metadata>
  <parameters>
    <parameter name='FileName' type='text' default='PSScriptAnalyzerSettings.psd1'
               prompt='Enter the name of the Script Analyzer settings file'/>
    <parameter name='Editor'
               type='choice'
               prompt='Select an editor for editor integration (or None):'
               default='0'
               store='text' >
       <choice label='&amp;None'
               help="No editor specified."
               value="None"/>
       <choice label='Visual Studio &amp;Code'
               help="Your editor is Visual Studio Code."
               value="VSCode"/>
    </parameter>
  </parameters>
  <content>
    <file source="PSScriptAnalyzerSettings.psd1"
          destination="$PLASTER_PARAM_FileName"
          openInEditor="true" />

    <modify path='.vscode\settings.json' encoding='UTF8'
            condition='($PLASTER_PARAM_Editor -eq "VSCode") -and (Test-Path -LiteralPath $PLASTER_DestinationPath\.vscode\settings.json)'>
        <replace condition='$PLASTER_FileContent -notmatch "`"$PLASTER_PARAM_FileName`""'>
            <original><![CDATA[(?si)(?<=^[^{]*\{)(\s*)]]></original>
            <substitute expand="true"><![CDATA[`$1// Use a custom PowerShell Script Analyzer settings file for this workspace.
    // Relative paths for this setting are always relative to the workspace root dir.
    `"powershell.scriptAnalysis.settingsPath`": `"$PLASTER_PARAM_FileName`",
    `$1]]></substitute>
        </replace>
    </modify>

    <templateFile condition="($PLASTER_PARAM_Editor -eq 'VSCode') -and !(Test-Path -LiteralPath $PLASTER_DestinationPath\.vscode\settings.json)"
                  source=".vscode\settings.json"
                  destination="" />

    <message>

$PLASTER_PARAM_FileName has been added to the folder '$PLASTER_DestinationPath'.
Edit this file to configure your PSScriptAnalyzer settings.

    </message>

    <message condition="$PLASTER_PARAM_Editor -eq 'VSCode'">
The PowerShell extension for Visual Studio Code has been configured to use $PLASTER_PARAM_FileName
via the settings.json file in '$PLASTER_DestinationPath\.vscode'.

    </message>
  </content>
</plasterManifest>
tools\Plaster\Templates\AddPSScriptAnalyzerSettings\PSScriptAnalyzerSettings.psd1
# Use the PowerShell extension setting `powershell.scriptAnalysis.settingsPath` to get the current workspace
# to use this PSScriptAnalyzerSettings.psd1 file to configure code analysis in Visual Studio Code.
# This setting is configured in the workspace's `.vscode\settings.json`.
#
# For more information on PSScriptAnalyzer settings see:
# https://github.com/PowerShell/PSScriptAnalyzer/blob/master/README.md#settings-support-in-scriptanalyzer
#
# You can see the predefined PSScriptAnalyzer settings here:
# https://github.com/PowerShell/PSScriptAnalyzer/tree/master/Engine/Settings
@{
    # Only diagnostic records of the specified severity will be generated.
    # Uncomment the following line if you only want Errors and Warnings but
    # not Information diagnostic records.
    #Severity = @('Error','Warning')

    # Analyze **only** the following rules. Use IncludeRules when you want
    # to invoke only a small subset of the default rules.
    IncludeRules = @('PSAvoidDefaultValueSwitchParameter',
                     'PSMisleadingBacktick',
                     'PSMissingModuleManifestField',
                     'PSReservedCmdletChar',
                     'PSReservedParams',
                     'PSShouldProcess',
                     'PSUseApprovedVerbs',
                     'PSAvoidUsingCmdletAliases',
                     'PSUseDeclaredVarsMoreThanAssignments')

    # Do not analyze the following rules. Use ExcludeRules when you have
    # commented out the IncludeRules settings above and want to include all
    # the default rules except for those you exclude below.
    # Note: if a rule is in both IncludeRules and ExcludeRules, the rule
    # will be excluded.
    #ExcludeRules = @('PSAvoidUsingWriteHost')

    # You can use rule configuration to configure rules that support it:
    #Rules = @{
    #    PSAvoidUsingCmdletAliases = @{
    #        Whitelist = @("cd")
    #    }
    #}
}
tools\Plaster\Templates\AddPSScriptAnalyzerSettings\.vscode\settings.json
{
    // Use a custom PowerShell Script Analyzer settings file for this workspace.
    // Relative paths for this setting are always relative to the workspace root dir.
    "powershell.scriptAnalysis.settingsPath": "<%=$PLASTER_PARAM_FileName%>"
}
tools\Plaster\Templates\NewPowerShellScriptModule\Module.psm1
# Implement your module commands in this script.


# Export only the functions using PowerShell standard verb-noun naming.
# Be sure to list each exported functions in the FunctionsToExport field of the module manifest file.
# This improves performance of command discovery in PowerShell.
Export-ModuleMember -Function *-*
tools\Plaster\Templates\NewPowerShellScriptModule\plasterManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<plasterManifest schemaVersion="1.1"
                 templateType="Project"
                 xmlns="http://www.microsoft.com/schemas/PowerShell/Plaster/v1">
  <metadata>
    <name>NewPowerShellScriptModule</name>
    <id>2a7c3f58-4a45-4d55-9b07-753ac7608114</id>
    <version>1.1.0</version>
    <title>New PowerShell Manifest Module</title>
    <description>Creates files for a simple, non-shared PowerShell script module.</description>
    <author>Plaster</author>
    <tags>Module, ScriptModule, ModuleManifest</tags>
  </metadata>
  <parameters>
        <parameter name='ModuleName'
                   type='text'
                   prompt='Enter the name of the module'/>

        <parameter name='Version'
                   type='text'
                   prompt='Enter the version number of the module'
                   default='0.0.1'/>

        <parameter name='Editor'
                   type='choice'
                   prompt='Select an editor for editor integration (or None):'
                   default='1'
                   store='text' >
            <choice label='&amp;None'
                    help="No editor specified."
                    value="None"/>
            <choice label='Visual Studio &amp;Code'
                    help="Your editor is Visual Studio Code."
                    value="VSCode"/>
        </parameter>
  </parameters>

  <content>
        <message>&#10;&#10;Scaffolding your PowerShell Module...&#10;&#10;&#10;</message>

        <newModuleManifest destination='${PLASTER_PARAM_ModuleName}.psd1'
                           moduleVersion='$PLASTER_PARAM_Version'
                           rootModule='${PLASTER_PARAM_ModuleName}.psm1'
                           encoding='UTF8-NoBOM'
                           openInEditor="true"/>

        <file source='Module.psm1'
              destination='${PLASTER_PARAM_ModuleName}.psm1'
              openInEditor="true"/>

        <templateFile source='test\Module.T.ps1'
                      destination='test\${PLASTER_PARAM_ModuleName}.Tests.ps1' />

        <file condition="$PLASTER_PARAM_Editor -eq 'VSCode'"
              source='editor\VSCode\settings.json'
              destination='.vscode\settings.json' />

        <file condition="$PLASTER_PARAM_Editor -eq 'VSCode'"
              source='editor\VSCode\tasks.json'
              destination='.vscode\tasks.json' />

        <requireModule name="Pester" minimumVersion="4.0.3"
            message="Without Pester, you will not be able to run the provided Pester test to validate your module manifest file.`nWithout version 4.0.3, VS Code will not display Pester warnings and errors in the Problems panel."/>

        <message>

Your new PowerShell module project '$PLASTER_PARAM_ModuleName' has been created.

        </message>

        <message>
A Pester test has been created to validate the module's manifest file.  Add additional tests to the test directory.
You can run the Pester tests in your project by executing the 'test' task.  Press Ctrl+P, then type 'task test'.

        </message>
  </content>
</plasterManifest>
tools\Plaster\Templates\NewPowerShellScriptModule\editor\VSCode\settings.json
{
    // When enabled, will trim trailing whitespace when you save a file.
    "files.trimTrailingWhitespace": true
}
tools\Plaster\Templates\NewPowerShellScriptModule\editor\VSCode\tasks.json
{
    // See https://go.microsoft.com/fwlink/?LinkId=733558
    // for the documentation about the tasks.json format
    "version": "2.0.0",
    "windows": {
        "options": {
            "shell": {
                "executable": "powershell.exe",
                "args": [ "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command" ]
            }
        }
    },
    "linux": {
        "options": {
            "shell": {
                "executable": "/usr/bin/pwsh",
                "args": [ "-NoProfile", "-Command" ]
            }
        }
    },
    "osx": {
        "options": {
            "shell": {
                "executable": "/usr/local/bin/pwsh",
                "args": [ "-NoProfile", "-Command" ]
            }
        }
    },
    "tasks": [
        {
            "label": "Run tests",
            "type": "shell",
            "group": "test",
            "command": "Invoke-Pester -Script test -PesterOption @{IncludeVSCodeMarker=$true}",
            "problemMatcher": [ "$pester" ]
        }
    ]
}
tools\Plaster\Templates\NewPowerShellScriptModule\test\Module.T.ps1
$ModuleName = '<%=$PLASTER_PARAM_ModuleName%>'
$ModuleManifestName = "$ModuleName.psd1"
$ModuleManifestPath = "$PSScriptRoot\..\$ModuleManifestName"

Describe 'Module Manifest Tests' {
    It 'Passes Test-ModuleManifest' {
        Test-ModuleManifest -Path $ModuleManifestPath | Should Not BeNullOrEmpty
        $? | Should Be $true
    }
}

Log in or click on link to see number of positives.

In cases where actual malware is found, the packages are subject to removal. Software sometimes has false positives. Moderators do not necessarily validate the safety of the underlying software, only that a package retrieves software from the official distribution point and/or validate embedded software against official distribution point (where distribution rights allow redistribution).

Chocolatey Pro provides runtime protection from possible malware.

Add to Builder Version Downloads Last Updated Status
Plaster (PowerShell Module) 1.1.3 798 Saturday, May 12, 2018 Approved

This package has no dependencies.

Discussion for the Plaster (PowerShell Module) Package

Ground Rules:

  • This discussion is only about Plaster (PowerShell Module) and the Plaster (PowerShell Module) package. If you have feedback for Chocolatey, please contact the Google Group.
  • This discussion will carry over multiple versions. If you have a comment about a particular version, please note that in your comments.
  • The maintainers of this Chocolatey Package will be notified about new comments that are posted to this Disqus thread, however, it is NOT a guarantee that you will get a response. If you do not hear back from the maintainers after posting a message below, please follow up by using the link on the left side of this page or follow this link to contact maintainers. If you still hear nothing back, please follow the package triage process.
  • Tell us what you love about the package or Plaster (PowerShell Module), or tell us what needs improvement.
  • Share your experiences with the package, or extra configuration or gotchas that you've found.
  • If you use a url, the comment will be flagged for moderation until you've been whitelisted. Disqus moderated comments are approved on a weekly schedule if not sooner. It could take between 1-5 days for your comment to show up.
comments powered by Disqus