I'm currently writing a helper module and despite my efforts, some private variable have to be accessed read/write from outside the module.

my first attempt was using Export-ModuleMember -Variable but exported variables can not be prefixed, nor accessed using modulename\... like functions and so, there is a great risk of name collision with variable outside the module.

so I tried to put all exported variables in a hashtable and export only the hashtable, but this make variable usage within the module a bit painful as now I have to prefix each usage by the hashtable name.

$myModule = @{ value = 0 }
    
function doCheck {
    write-host $myModule.value
    $script:myModule.value += 1
}

Export-ModuleMember -Function 'doCheck' -Variable 'myModule'
[CmdletBinding()]
    param ( )
    
begin   { 

    import-module -Name '.\myModule.psm1' -Force
}

process { }

end { 

    write-host $myModule.value

    $myModule.value += 1
    myModule\doCheck

    write-host $myModule.value
}

to get around this, I then did use the variables in a simple way in the module and export a PSCustomObject shadowing them. this simplify the variable usage within the module but I now have to create a getter and a setter for each mirrored variable.

$value = 0
    
function getValue { $value }
function setValue ( $value ) { $script:value = $value }
    
$myModule = [PSCustomObject] @{ }
$myModule | Add-Member -MemberType ScriptProperty -Name value `
    -Value $function:getValue -SecondValue $function:setValue
    
function doCheck {
    write-host $value
    $script:value += 1
}

Export-ModuleMember -Function 'doCheck' -Variable 'myModule'
[CmdletBinding()]
    param ( )
    
begin   { 

    import-module -Name '.\myModule.psm1' -Force    
}
process { }

end     { 

    write-host $myModule.value

    $myModule.value += 1
    myModule\doCheck

    write-host $myModule.value
}

as a third option, I tried to use classes but now I have to prefix variable access by [myModule]:: in the module and classes are a bit harder to write (for me at least) and to debug (for everyone as using module... won't reload the class.

class myModule {
    
    static $value = 0
    
    static [void] reset () {

        [myModule]::value = 0
    }

    static [void] doCheck () {

        write-host ([myModule]::value)
        [myModule]::value += 1
    }
}
using module .\myModule.psm1

[CmdletBinding()]
    param ( )

[myModule]::reset()
    
write-host ([myModule]::value)

[myModule]::value += 1
[myModule]::doCheck()

write-host ([myModule]::value)

I'm completely happy with none of these options. are they the only options I have to choose from or did I miss something?

context

Name                           Value
----                           -----
PSVersion                      5.1.19041.6456
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.19041.6456
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1

4 Replies 4

Simplest answer, invoke a script block using the module scope to update your variable: . (Get-Module MyModule) { $myVar++ }; example:

$module = New-Module { function foo { $foo }; $foo = 0 }
foo; . $module { $foo++ }; foo # 0, 1

If the value is a reference type, e.g.: an array, you can use &:

$module = New-Module { function foo { $foo }; $foo = @(0) }
foo; & $module { $foo[0]++ }; foo # 0, 1

Why not just export functions that facilitate this exchange?

function Get-ModuleOption {
  param([string]$Name)

  return $script:moduleData[$name]
}

function Set-ModuleOption {
  param([string]$Name,$Value)

  $script:moduleData[$Name] = $Value
}

Export-ModuleMember -Function Get-ModuleOption,Set-ModuleOption

Using @Santiago Squadron answer I now have a forth option. Executing script in the module context is a powerful trick. I have to think about it. Life would be a lot easier if it was possible to have name space, or getter/setter for regular variables (<edit> as C# has getter/setter for class members).

$value = 0
    
function doCheck {
    
    write-host $value
    $script:value += 1
}

Export-ModuleMember -Function 'doCheck'
[CmdletBinding()]
    param ( )
    
begin   { 

    import-module -Name '.\myModule.psm1' -Force
    $myModule = Get-Module myModule
}
process { }

end { 

    write-host (. $myModule { $value })

    . $myModule { $value += 1 }
    myModule\doCheck

    write-host (. $myModule { $value })

This new format for Q&As sucks.... answering the:

Life would be a lot easier if it was possible to have name space, or getter/setter for regular variables.

It is easier if you write your modules in C#. The concept of "module variables" is all replaced with static fields or properties...

Your Reply

By clicking “Post Your Reply”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.