Over the course of the last week or so, I've been sharing PowerShell functions and scripts for working with PowerShell functions and scripts. I showed PowerShell functions to export functions to a script file and code to convert scripts to functions It has all been very Inception-like. To wrap this all up I thought I'd share a PowerShell script I wrote to convert all of the code I've been sharing into a new module. I will eventually publish PSFunctionTools to the PowerShell Gallery and I see no reason why it won't work in Windows PowerShell or PowerShell 7. Even cross-platform.
ManageEngine ADManager Plus - Download Free Trial
Exclusive offer on ADManager Plus for US and UK regions. Claim now!
The script might even be the basis for an additional function. Or I might include it as a reference sample in the new module. What is mind-bending to me is that I am using the functions in the code on themselves to create something new. I don't expect you to be able to run this script since it is written for my environment and files. I've posted all of the code over the last week, but my files may be different than yours.
This script is based on the one I posted as a proof-of-concept for converting scripts into a PowerShell module. My script will also initialize the new project in git and generate initial help documentation using the Platyps module.
#requires -version 5.1
#requires -module Platyps
#Export functions from files and create a new module
[cmdletbinding(SupportsShouldProcess)]
Param(
[Parameter(position = 0,HelpMessage = "What is the name of the new module?")]
[ValidateNotNullOrEmpty()]
[string]$NewModuleName = "PSFunctionTools",
[Parameter(Position = 1,HelpMessage = "What is the parent path for the new module?")]
[ValidateNotNullOrEmpty()]
[ValidateScript({Test-Path $_})]
[string]$ParentPath = "C:\Scripts",
[Parameter(HelpMessage = "Enter an module description.")]
[string]$Description = "A set of PowerShell commands for working with PowerShell scripts and functions.",
[Parameter(HelpMessage ="PowerShell script files with functions to export.")]
[ValidateNotNullOrEmpty()]
[ValidateScript({Test-Path $_})]
[string[]]$Files = $(
'C:\scripts\Convert-FunctionToFile.ps1',
'C:\scripts\new-folderlayout.ps1',
'C:\scripts\dev-scripttofunction.ps1'
)
)
Write-Verbose "Starting $($MyInvocation.MyCommand)"
<#
dot source the conversion functions
C:\scripts\Convert-FunctionToFile.ps1
Test-FunctionName
Get-FunctionName
Get-FunctionAlias
Export-FunctionFromFile
C:\scripts\New-FolderLayout.ps1
Import-ModuleLayout
Export-ModuleLayout
#>
. C:\scripts\Convert-FunctionToFile.ps1
. C:\scripts\New-FolderLayout.ps1
#the new module location
$path = Join-path -Path $ParentPath -ChildPath $NewModuleName
$export = [System.Collections.Generic.list[object]]::New()
$aliases = @()
#the layout was created using Export-ModuleLayout
$layout = "C:\scripts\ModuleLayout.json"
Write-Verbose "Creating the module structure"
Import-ModuleLayout -Name $NewModuleName -ParentPath $ParentPath -Layout $layout
#I removed the parameter validation on the target path
$functionFiles = $files | ForEach-Object {
Write-Verbose "Processing $_"
Export-FunctionFromFile -Path $_ -OutputPath $Path\functions\public -All -Passthru
#get aliases
if ($pscmdlet.ShouldProcess($_,"Getting function aliases")) {
$aliases += Get-FunctionAlias -path $_ | Select-Object -ExpandProperty alias
}
}
if ($functionFiles) {
$export.AddRange($functionFiles.baseName)
}
#create the root module
$psm1 = @"
Get-Childitem `$psscriptroot\functions\*.ps1 -recurse |
Foreach-Object {
. `$_.FullName
}
"@
Write-Verbose "Creating root module $path\$newmodulename.psm1"
$psm1 | Out-File "$path\$newmodulename.psm1"
#create the module manifest
$splat = @{
Path = "$path\$newmodulename.psd1"
RootModule = "$newmodulename.psm1"
ModuleVersion = "0.1.0"
Author = "Jeff Hicks"
CompanyName = "JDH Information Technology Solutions, Inc."
Copyright = "(c) 2021 JDH Information Technology Solutions, Inc."
Description = $Description
CmdletsToExport = @()
VariablesToExport = @()
FunctionsToExport = $Export
AliasesToExport = $aliases
PowerShellVersion = "5.1"
CompatiblePSEditions = "Desktop","Core"
}
Write-Verbose "Creating module manifest $($splat.path)"
New-ModuleManifest @splat
#this requires the Platyps module
Write-Verbose "Creating module help files"
if ($PSCmdlet.ShouldProcess("docs","create markdown help files")) {
Import-Module $splat.path
New-MarkdownHelp -Module $NewModuleName -OutputFolder $path\docs
New-ExternalHelp -Path $path\docs -OutputPath $path\en-us
}
Write-Verbose "Initializing git"
if ($PSCmdlet.ShouldProcess($path, "git initialize")) {
Set-Location $path
git init
git add .
git commit -m "initial files"
git checkout -b $splat.ModuleVersion
}
if (-not $WhatIfPreference) {
Get-ChildItem $path -Recurse
Try {
[void](Get-Command -name code.cmd -ErrorAction stop)
Write-Verbose "Opening module in VSCode"
code $path
}
Catch {
Write-Warning "VS Code not found."
}
}
Write-Verbose "Ending $($MyInvocation.MyCommand)"
I've parameterized the script with the idea that I can turn this into a reusable tool. But for now, the parameter values are hard-coded to save me typing.
The script has several phases. First, it creates the module structure using the command I shared to export and import a model module directory structure. Second, the script takes the array of files and exports all of the functions to separate files. Third, it creates a root module, and the module manifest using information from the exported functions. Next, the script creates help documentation and finally initializes git. If I'm not running the script with -WhatIf, the new module will be opened in VSCode.
I've already run this script and started working on the module, but I wanted you to see the output so this is using a temporary folder and -Whatif.
Here you can see the results in VSCode. The entire process took about 3 seconds. I definitely will be developing this idea and using it myself. I often start with standalone script files that eventually I decide to turn into modules. It will be nice to have a set of tools to accelerate that process.
In the meantime, feel free to use any of the code I've shared to build your own PowerShell tooling for building PowerShell tooling.
1 thought on “Building a PowerShell Module Inception-Style”
Comments are closed.