Keeping Track of PowerShell Functions in Script Modules when Dot-Sourcing PS1 Files
I'm picking up where I left off in a previous blog article
Write Dynamic Unit Tests for your PowerShell Code with Pester.
I'm using the dynamic Test-MrFunctionsToExport
Pester test that I wrote about in that previous
blog article. Currently, the test is failing for my MrToolkit module that's part of
my PowerShell repository on GitHub:
1Test-MrFunctionsToExport -ManifestPath .\GitHub\PowerShell\MrToolkit\MrToolkit.psd1
Based on the previous results, I can easily determine that more functions exist in the module folder
than are specified in the FunctionsToExport
section of the module manifest. That may sound like a
simple fix but once you have tons of PS1 files in the module folder and a similar number specified
in the manifest, it becomes difficult to know what's missing so I've written a tool to generate a
list of the function names:
1function Get-MrFunctionsToExport {
2
3<#
4.SYNOPSIS
5 Returns a list of functions in the specified directory.
6
7.DESCRIPTION
8 Get-MrFunctionsToExport is an advanced function which returns a list of functions
9 that are each contained in single quotes and each separated by a comma unless the
10 simple parameter is specified in which case a simple list of the base file names
11 for the functions is returned.
12
13.PARAMETER Path
14 Path to the folder where the functions are located.
15
16.PARAMETER Exclude
17 Pattern to exclude. By default profile scripts and Pester tests are excluded.
18
19.PARAMETER Recurse
20 Return function names from subdirectories in addition to the specified directory.
21
22.PARAMETER Simple
23 Return a simple list instead of a quoted comma separated list.
24
25.EXAMPLE
26 Get-MrFunctionsToExport -Path .\MrToolkit
27
28.EXAMPLE
29 Get-MrFunctionsToExport -Path .\MrToolkit -Simple
30
31.INPUTS
32 None
33
34.OUTPUTS
35 String
36
37.NOTES
38 Author: Mike F Robbins
39 Website: http://mikefrobbins.com
40 Twitter: @mikefrobbins
41#>
42
43 [CmdletBinding()]
44 param (
45 [ValidateScript({
46 If (Test-Path -Path $_ -PathType Container) {
47 $True
48 }
49 else {
50 Throw "'$_' is not a valid directory."
51 }
52 })]
53 [string]$Path = (Get-Location),
54
55 [string[]]$Exclude = ('*profile.ps1', '*.tests.ps1'),
56
57 [switch]$Recurse,
58
59 [switch]$Simple
60 )
61
62 $Params = @{
63 Exclude = $Exclude
64 }
65
66 if ($PSBoundParameters.Recurse) {
67 $Params.Recurse = $true
68 }
69
70 $results = Get-ChildItem -Path "$Path\*.ps1" @Params |
71 Select-Object -ExpandProperty BaseName
72
73 if ((-not($PSBoundParameters.Simple)) -and $results) {
74 $results = $results -join "', '"
75 Write-Output "'$results'"
76 }
77 elseif ($results) {
78 Write-Output $results
79 }
80
81}
For those of you who didn't read the previous blog article that I referenced, I've moved to the
model of placing each of my functions into a separate PS1 file and the file base name is the same as
the function name. Each PS1 file is dot-sourced in the PSM1 file when the module is imported and a
list of the function names must exist in the FunctionsToExport
section of the module manifest (or
exported via Export-ModuleMember
in the PSM1 file).
I'll use this tool to generate a list of function names. If nothing is specified in the
FormatsToProcess
or TypesToProcess
section of the module manifest, it can be updated
programmatically using the Update-ModuleManifest
cmdlet. Unfortunately
a known error
is generated as shown in the following example if either or both of these are specified:
1Update-ModuleManifest -Path .\GitHub\PowerShell\MrToolkit\MrToolkit.psd1 -FunctionsToExport (Get-MrFunctionsToExport -Path .\GitHub\PowerShell\MrToolkit\ -Simple)
1Update-ModuleManifest : Cannot update the manifest file ‘.\GitHub\PowerShell\MrToolkit\MrToolkit.psd1’ because the
2manifest is not valid. Verify that the manifest file is valid, and then try again.’The member ‘FormatsToProcess’ in
3the module manifest is not valid: Cannot find path
4‘C:\Users\mikefrobbins\AppData\Local\Temp\1707008812\GitHub\PowerShell\MrToolkit\MrToolkit.ps1xml’ because it does not
5exist.. Verify that a valid value is specified for this field in the
6‘C:\Users\mikefrobbins\AppData\Local\Temp\1707008812\NewManifest.psd1′ file.’
7At line:1 char:1
8+ Update-ModuleManifest -Path .\GitHub\PowerShell\MrToolkit\MrToolkit.p …
9+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
10+ CategoryInfo : InvalidArgument: (System.Argument…rd errorRecord):ArgumentException) [Update-ModuleMan
11ifest], ArgumentException
12+ FullyQualifiedErrorId : UpdateManifestFileFail,Update-ModuleManifest
The sub-command used in the previous example creates a simple list of the function names:
1Get-MrFunctionsToExport -Path .\GitHub\PowerShell\MrToolkit\ -Simple
Luckily, I've written the Get-MrFunctionToExport
function so it generates a list in the proper
format that can be pasted into the module manifest if you happen to run into a scenario such as this
one where Update-ModuleManifest
can't be used.
1Get-MrFunctionsToExport -Path .\GitHub\PowerShell\MrToolkit\
The easiest way to copy the results of the previous command is to simply pipe it to clip.exe:
1Get-MrFunctionsToExport -Path .\GitHub\PowerShell\MrToolkit\ | clip.exe
After pasting the results into the FunctionsToExport
section of the module's manifest (PSD1 file),
the Pester test runs without issue:
1Test-MrFunctionsToExport -ManifestPath .\GitHub\PowerShell\MrToolkit\MrToolkit.psd1
Keeping track of functions and making sure they're all specified in the FunctionsToExport
section
of the module manifest can be a annoying to maintain but it's a necessary evil if you want module
auto-loading to work properly when placing the functions from your modules in separate PS1 files.
Why not make your life a little easier by creating tools to make building other tools easier?
µ