1

Background

I'd like all of my scripts and modules to be based on a template script which takes care of some common "housekeeping" tasks.

Some of my modules are interdependent, and so I might load a module within another module, even if temporarily.

However, because the code is templated, the basic initialization functions have the same name. In my mind, this shouldn't be an issue as long as I scope things properly, but it turns out it is an issue.

Demonstrating the issue

The demo below will have 1 script, which loads a module, which in turn loads another module. Each module has initialization code that runs when loading he module.

  • Update: updated the code to demonstrate a bit better why it's structured the way it is, although answer was already accepted.

module.level2.psm1

# module.level2.psm1
# list of modules loaded by template code
$script:LoadModule = $null

# module housekeeping code - called from template code
function ModuleSpecificInitCode {
    Write-Host "Level 2 Code"
}

function Level2ModuleFunction {
    Write-Host "This is why I imported Level 2 module"
}

# module housekeeping code - template code
function TemplateInitCode {
    Write-Host "Level 2 Init"
    $LoadModule | % { Import-Module ".\$_.psm1" }
}

TemplateInitCode 
try {
    ModuleSpecificInitCode 
} catch {
# Error handling
}

module.level1.psm1

# module.level1.psm1
# list of modules loaded by template code
$script:LoadModule = "module.level2"

# module housekeeping code - called from template code
function ModuleSpecificInitCode {
    Write-Host "Level 1 Code"
}

function Level1ModuleFunction {
    Write-Host "This is why I imported Level 1 module"
}

# module housekeeping code - template code
function TemplateInitCode {
    Write-Host "Level 1 Init"
    $LoadModule | % { Import-Module ".\$_.psm1" }
}

TemplateInitCode 
try {
    ModuleSpecificInitCode 
} catch {
# Error handling
}

test.ps1

# test.ps1
Remove-Module module.level*
Import-Module .\module.level1.psm1

When running test.ps1, the output I receive is:

PS>.\test.ps1
Level 1 Init
Level 2 Init
Level 2 Code
Level 2 Code

The Issue / My Question

The issue is the last line. Level 2 code is running instead of level 1 code.

I've tried local, private and script as the <scope>: but whatever I do the Level 1 Code never runs.

What am I missing here, why do all the modules seem to be running in the same namespace?

Lockszmith
  • 2,173
  • 1
  • 29
  • 43

1 Answers1

1

If you don't explicitly export anything from the module, it all gets exported.

If you want a function to be available only within the module (think of it as a private function, though it's not actually related to the Private: scope in PowerShell), then just export every function except the ones that are internal.

Export-ModuleMember is how you define what you want to be exported. It accepts wildcards, so if you can describe what's public with a pattern, you could do it in one call, but it's ok to call it as many times as you need.

Once the functions are no longer exported, they are not available to code outside the module.

Your examples are a little strange to me, in that you seem to want access to some of the module code outside the module, but want it to be the same name, but I'm not certain.

In that case, for example if Level1 init was supposed to call code in Level2 module, you might consider adding -Scope Local to the Import-Module call itself, but I'm not certain this would help your situation.

briantist
  • 45,546
  • 6
  • 82
  • 127
  • Both your answers were right. either Export-ModuleMember or using the -Scope Local worked. – Lockszmith May 14 '18 at 12:17
  • clarification about the code, the template code is what will run INSIDE the modules only during initialization. the rest will be exported out. I've updated the code in the question to reflect that better. – Lockszmith May 14 '18 at 15:10
  • Can you call an internal function from an exported function? – jschmitter Jan 21 '22 at 16:36
  • @jschmitter yes, the functions within a module all run within the module's session state, so they can access other variables and functions defined within the module, whether they are exported or not. – briantist Jan 21 '22 at 17:21