PowerShell Script Module Design: Public/Private versus Functions/Internal folders for Functions

There’s been a lot of debate about script module design as of lately and instead of tweeting something out asking for responses, I thought I would post it here via a blog article.

Back when I first started creating PowerShell script modules, I placed all of my functions in the PSM1 file and later started placing each function in a separate PS1 file that was dot-sourced from the PSM1 file.

I would simply place the PS1 files in the root folder of the script module. Later, when I started writing functions that I didn’t want to make public, I created a folder named “Private” and placed the private functions in that folder.

I’ve recently updated my Plaster template for creating script modules and decided to place the PS1 files for the public functions in a sub-folder instead of in the module’s root folder. The dilemma is whether those should be called “Public” and “Private” or “Functions” and “Internal”. I see a lot of both options from others in the PowerShell community.

At this point, I’m leaning towards using “Functions” and “Internal” for the folder names because to me it’s more declarative for someone who may not understand what’s going on and when you start adding things like “Classes” into their own sub-folder, it only seems to make more sense.

This blog article isn’t some rant about whether or not you should place your functions into separate PS1 files or in the PSM1 file. Moving forward, I plan to have functions in separate files for development which is what you’ll find in my GitHub repositories. I plan to use a build process to merge all of the functions from the separate PS1 files back into the PSM1 file for production and that’s what you’ll find me publishing on the PowerShell Gallery. This module creation process will give me the best of both worlds. Separate functions while in development to avoid things like merge conflicts and if someone wants just one of my functions and not the entire module, they can simply download it from GitHub. The module will be packaged up for use in a production environment and if you install it from the PowerShell Gallery, that’s what you’ll get. This will eliminate things like slowness when the module is loaded due to the functions being in separate PS1 files. It will also be a lot simpler to sign fewer files before I publish them to the PowerShell gallery.

Did you know that one of the best practices for publishing your modules to the PowerShell Gallery is to sign them?

I’ve created a list on Twitter of the community members that I’ve seen building and promoting PowerShell script modules using similar designs. If I’ve missed someone, please post a link to their script module design content as a reply to this blog article.

I’d love to hear your thoughts or suggestions on the public/private versus functions/internal naming scheme debate. Please post them as a reply to this blog article.


Update based on Twitter responses.

What I’m hearing so far based on the responses on Twitter is Public/Private seems to be more widely accepted by the community. I’ll probably end up using those since that’s the whole point of this blog article (standardization).

µ

9 Comments

  1. skatterbrainzz

    This is a very good article and a good point of discussion! I’m still pretty far behind where you, and many of your peers, are with regards to PowerShell, but I’ve been using the public/private folder scheme for allocating separate .ps1 files in my modules. I agree that functions/internal makes sense, but does it really provide a lot of benefit when the target audience is often more technically skilled than not? I don’t have a formal build process that merges them either, so if you can point me to a good article (one of yours?) to help I would greatly appreciate it. Thank you!

    Reply
  2. rene leblanc

    I need to make a function out of this, but here is a working script:

    Clear-Host
    $CommandPath = $PSCommandPath
    $CommandPath
    $AllCommandsPath = split-path $CommandPath
    $AllCommandsPath
    $AllCommandsFileName = $AllCommandsPath + ‘\AllFunctions.ps1’
    $AllCommandsFileName
    $FunctionDirectory =
    $AllCommandsPath + ‘\Functions’
    $Functions = Get-ChildItem -Filter *.ps1 $FunctionDirectory -Recurse

    $CompiledFunctions = ”
    foreach ($Function in $Functions) {
    $CompiledFunctions =
    $CompiledFunctions +
    [environment]::newline + [environment]::newline + [string]::join([environment]::newline, (get-content -Path $Function.fullname))
    }

    Set-Content -Path $AllCommandsFileName -Value $CompiledFunctions -Force

    Reply
  3. jaykul

    It’s worth saying that when we use “Public” vs “Private” what we really mean is “Functions to Export” and “Other (Internal/Private) helper Functions” …

    I think if you use “Functions” vs “Internal” you’re going to cause some confusion because Internal means Internal … functions.

    I believe we settled on Public and Private for two reasons:

    1. Although “FunctionsToExport” is obvious, there’s no consensus on the name for “InternalFunctions”
    2. Public and Private are terms developers have been using _across languages_ for decades, and are intuitively grasped by all but newbies (who would do well to learn them, anyway).

    It’s also worth pointing out that the advent of “Classes” (and/or “Enums”) came purely from the fact that PowerShell wants those defined before they’re referenced, so they need to all be at the top of the module file.

    We could easily put them in the “Public” (or “Private”) folders, and use some other mechanism for pulling them to the top at build time (e.g. name them Example.class.ps1) … and that would be very compelling if there was an easy way to make classes private.

    Reply
  4. jaykul

    P.S. We are just about ready to release the first public release of “ModuleBuilder” and the “Build-Module” command for stitching these all together and updating your FunctionsToExport in your psd1, etc.

    https://github.com/PoshCode/ModuleBuilder

    Reply
  5. SQLDBAwithTheBeard

    I use this as the starting place

    https://github.com/SQLDBAWithABeard/PlasterTemplate

    dbachecks uses the same – We use functions and internal

    dbatools is roughly the same with some added extras and that build process takes all of the separate ps1 files and places them in the psm1

    Reply
  6. Craig

    As always the answer depends on what your doing, the intended audience and project size. Since most modules are just building blocks making the consumption of something easier for the developer, I would think the developer(s) should be able to read the code and make sense of it regardless of the structure chosen. I say let the KISS principle and common sense rule.

    For code signing what a wart on a hog in PowerShell. If there is a need for me to do this, I’ll MD5 checksum the code myself and have the code verify the checksum prior to executing. Obviously, if the checksum isn’t correct the code aborts. This is not iron clad, but keeps the honest people honest. It is also possible to compile your PowerShell code into an executable, if your needing to hide the code from general consumption.

    Reply
  7. Nick

    Public/Private here… To me it’s clearer what is exported and what isn’t within that scheme as “Functions” could be either private functions or exported functions.

    Reply

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: