Don’t use Default Manifest Settings when Dot-Sourcing Functions in PS1 Files from a PowerShell Script Module

I briefly mentioned and demonstrated something similar to this at the end of one of my sessions at the PowerShell and DevOps Global Summit 2016. Since then, I've tested more which has led to a better solution.

We've all been taught that it's best practice to use a Requires statement in our functions that specifies the required PowerShell version along with any module dependencies, but following this best practice has one unexpected side effect when dot-sourcing functions in PS1 files from a PSM1 script module.

1Get-ChildItem -Path .\GitHub\AWS\MrAWS\

requires-modules1a.jpg

Simple enough, right? My MrAWS module folder contains two functions that are dot-sourced from the PSM1 script module file. The PSM1 file contains the following code:

1Get-ChildItem -Path $PSScriptRoot\*.ps1 -Exclude *.tests.ps1, *profile.ps1 |
2ForEach-Object {
3    . $_.FullName
4}

When the module is imported, the correct functions show up as being exported:

1Get-Module -Name MrAWS
2Get-Command -Module MrAWS

requires-modules2a.jpg

Add a dependency for the AWSPowerShell module via a Requires statement in one or more of the PS1 files:

1#Requires -Modules AWSPowerShell

Re-import the module and now all of the cmdlets that are part of the AWSPowerShell module show as being part of the MrAWS module:

1Get-Module -Name MrAWS
2Get-Command -Module MrAWS

requires-modules3a.jpg

Although this isn't technically hurting anything, it's not desirable to have all of them show up as being part of my module. This problem also isn't limited to the modules shown in this blog article as it will occur with any module where the default module manifest settings are used, functions in PS1 files are dot-sourced from the module's PSM1 file, and a Requires statement is specified with module dependencies.

Initially, I determined that instead of setting the module dependency using a Requires statement in the PS1 file, it could be set using the RequiredModules section in the module manifest:

1# Modules that must be imported into the global environment prior to importing this module
2RequiredModules = 'AWSPowerShell'

I placed a comment in the PS1 file but that still wasn't the optimum solution because it meant someone would have to modify the PS1 file(s) if they only downloaded a function or two from my GitHub repository instead of the entire module otherwise the dependencies wouldn't be set for them.

What I discovered is that by default an asterisk (*) is set in several sections of a module manifest when it's created with the New-ModuleManifest cmdlet:

1# Cmdlets to export from this module
2CmdletsToExport = '*'

My scenario is limited to the CmdletsToExport section, but you may encounter this problem with functions, variables, and/or aliases if any exists in the modules you're specifying with the Requires statement.

I also noticed that if these lines were commented out or removed from the manifest altogether, all of the cmdlets were still exported so the default when not specified seems to export all. The only way I've found to resolve this problem in my scenario while meeting all of my objectives and using best practices such as setting module dependencies with a Requires statement is to simply remove the asterisk so no cmdlets are exported:

1# Cmdlets to export from this module
2CmdletsToExport = ''

This allows a Requires statement in the PS1 file to be set with the proper module dependencies while not exporting any of the cmdlets from the specified modules. If you had cmdlets to export or for some reason wanted to export some of the cmdlets from the required modules, you could specify them individually. That may sound difficult to maintain but not if you write a tool similar to my Get-MrFunctionsToExport function as shown in my Keeping Track of PowerShell Functions in Script Modules when Dot-Sourcing PS1 Files blog article. I also recommend writing tests to automate the process of making sure your modules only export what's expected. You can find an example in my Write Dynamic Unit Tests for your PowerShell Code with Pester blog article.

µ