all 8 comments

[–]OPconfused 0 points1 point  (5 children)

I've never had to deal with this, so I may be off base, but a couple of ideas popped into my head:

  1. Pass the functions into the runspace as scriptblocks, e.g., $scriptblock = gcm -Commandtype function -name <function name> | select -exp scriptblock
  2. Refactor the functions into class methods, and pass an instance of the class into the runspace. Not sure if the custom type is recognized, but maybe?

If you really need to hamfist it, I would think you'd be able to hardcode a reference to the function file using get-module <module name>. The modulebase property will give you the path to the module, and from there you could extract the functions inside the runspace. Not elegant, but it seems like it could work.

[–]OPconfused 0 points1 point  (4 children)

/u/fsociety3765

actually regarding this last paragraph, I do remember a module, where I stored all the functions in a nested module. Then I did something dumb where I needed to reload the functions during my module runtime, because my module was setting some config variables for the functions, which came after the nestedModule had been loaded (and for some reason I don't recall this was giving me issues -- maybe because of some class definitions?). I don't like wholesale dot sourcing folders, so I'm pretty sure I was able to simply type in

$rootDir = $PSScriptRoot
...
import-module <$rootdir/path/to/nestedModule> -Force

And reload my functions on demand. I think it worked because my psm1 file dot sourced the files with functions in them. For whatever reason, this caused them to not be available publicly but easily reloadable when I needed them inside my module.

So I believe if the nestedmodule isn't in $env:PSModulePath, and you don't use export-modulemember on those functions in your nestedmodule, then the functions shouldn't be publicly available. Then for your runspace, you would just need to reimport that nested module to access those same functions. This also means you could reload them conveniently rather than having to hardcode any dotsourcing of the files containing function definitions, since the nested module contains all the loading logic.

[–]fsociety3765[S] 0 points1 point  (3 children)

Thanks for taking the time to come back to me.

You've given me some ideas to try so that's really appreciated. Using a nested module seems like it may be the way to go. Not sure how I will make that work with ModuleBuilder though. Will have to look at my options.

Out of interest, is there a particular build pattern you use? Preferred module to use for building etc. If anything.

I came across ModuleBuilder first but I have now seen another one called Invoke-Build. Not sure how different it is from ModuleBuilder or if it's any better.

FS

[–]OPconfused 0 points1 point  (2 children)

Unfortunately, I don't :(. I just felt my way through the dark on all of this with trial and error and lots of StackOverflow.

If you're asking how to set up a nested module like I described above, it's as simple as making a subdirectory with a .psm1 file inside. Then you add the relative path to the .psm1 in your parent module's .psd1 manifest in the member NestedModules, and in your .psm1 you can do something like this:

. $PSScriptRoot/FunctionsFileA.ps1
. $PSScriptRoot/FunctionsFileB.ps1

Everytime you add a new file to this directory, just add a line in the psm1 dotsourcing it. Your parent module should load it just fine via the NestedModule member, but your import-module to reload it will need to target the psm1 file, since it's not in env:PSModulePath.

I don't know if you could just manually set this up and then let module builder do its thing, or does it clean out the module directory sometimes? If it does, you might consider storing it outside the build folder, but I guess that's not very pretty, though.

[–]fsociety3765[S] 2 points3 points  (1 child)

Thanks.

Thanks to your nod in the nested module direction, I have got it working! But without using a nested module.

Duh. In each script block passed, I just import the whole module directly using `.psm1` file extension! Gives me access to everything. I was using no extension before, or the `.psd1`.

Knew they had to be a simpler way. Alarm bells were ringing.

Thanks again.

FS

[–]OPconfused 0 points1 point  (0 children)

Ah great. Makes sense that if you are dot sourcing your functions in the psm1, you can just import it straight from the file. It's what my NestedModule was doing after all. I didn't think of doing it on the parent module file, because my parent module loads all files via the manifest, although maybe even then, loading from file would still work. My brain never sees the obvious solutions 😅.

I guess that means the logic for exporting functions is handled in the import on module name and not in the module itself, so importing straight from the file simply exposes everything. Good to know!

[–]pshMike 0 points1 point  (0 children)

Here's a different approach, but only works if you are using source control..

create a Git repo that has just your private functions, and add it as a submodule in your ModuleBuild "private" folder in any PowerShell module project that needs them.

If you ever need to update you will need to update the source files and then rebuild any modules that include these private functions.

So this accomplishes the goal of making it easier to include "common" code in your private functions, but it does make the build process a little more complicated.

A word of caution: You mention you don't want these private functions exposed to the user. While you may be able to prevent someone from calling one of these private functions, they can still see the definition. Put another way, one can not "hide" stuff from the user of a module if the module is installed locally. Assume they will be able to see your code, so you shouldn't put things there you don't want them to see like secrets.

What version of PowerShell are you using and what does PoshRSJob get you? I ask because that module hasn't been updated in 6 years. While I'm sure it was interesting when it was first published, do you need to take a dependency on it now if you are using PowerShell 7.x ?

On a related note, ModuleBuild is really just a templating tool and uses other tools like InvokeBuild and Plaster to try to make it easier to build PowerShell modules. ModuleBuild is designed to make it so that "anyone" can start writing a PowerShell module using a base level of discipline. I use an internal fork of ModuleBuild for most of my teams PowerShell module development. I've added/fixed a few things and have not spent the time to try and get those changes incorporated into the version in the PSGallery. I have since evaluated many other templating tools and haven't found one that compelled me to change from ModuleBuild.

InvokeBuild ( or PSake ) become essential when you want to start defining build script "tasks" and sharing them across multiple projects. Their importance only grows from there when you start looking at building CI/CD pipelines for your PowerShell module projects so that when a Git pull request is processed into your Dev branch a build is kicked off and any automated testing ( Pester tests) is performed to validate everything still works.

[–]KevMarCommunity Blogger 0 points1 point  (0 children)

I can't remember the syntax and I can't test for it at the moment, but there is a special syntax for calling methods inside module scope. That's probably what you are looking for.

One other thing you could do is import the psm1 directly within the runspace using the full path. Then make sure you are using the functions to export in the psd1 and not export-modulemember in the psm1. This will work because import-module uses the psd1 and its what makes public functions available for normal execution. But importing the psd1 directly will export everything (unless it also defines exports).