Elomis
Elomis

Reputation: 532

How do I make a variable in a Powershell module accessible to other functions in that module?

I have a powershell module that defines some basic functions that write log events according to corporate standard, and another single function which creates the folder the log file should go into, and restarts the logging app service.

Each function that writes different severities of log event needs to use the $LogFileLocation variable (set in the function that creates the folder, restarts the service and generally gets the system ready to start logging) but the $LogFileLocation variable is only available inside the function that does setup.

How can I make it available to the other functions, including to any script functions from a script which imports the module? I've tried Global:$LogFileLocation instead of just $LogFileLocation but this doesn't seem to make it a global variable.

Upvotes: 1

Views: 2881

Answers (2)

JohnLBevan
JohnLBevan

Reputation: 24430

Define this function in your module:

Function Set-MyModuleLogPath {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$true)]
        [ValidateScript({Test-Path $_ -PathType Leaf})] #include this if you want to ensure the path exists before using (or add logic to create the path below)
        [String]$Path
    )
    process {
        (New-Variable -Name 'LogFileLocation' -Value (Resolve-Path $Path).Path -Option 'Constant' -Scope 1 -PassThru).Value
        #For more info on Scope see: https://msdn.microsoft.com/en-us/powershell/reference/5.1/microsoft.powershell.core/about/about_scopes
    }
}

This uses the New-Variable command to assign your path value to a variable, setting that variable as a constant (as presumably once set you don't want other functions to change this path at runtime; since that could lead to unpredictable results with a widely scoped variable).

The Scope parameter takes an argument of 1, meaning that this variable is scoped to the container of the function's definition; which in your case is the module script. i.e. Defining this function in the module then calling at runtime has the same effect as writing $LogFileLocation = 'c:\path\to\file.log' in the module's code; only you now have a way to avoid hard-coding that path in your module.

The ValidateScript option on path has the logic Test-Path $_ -PathType Leaf. i.e. we want to ensure that the path being referred to is valid / already exists; and that it's a file, not a directory. Of course, you may want to only validate the directory exists then create a new file at runtime; or maybe create anything that doesn't already exist at runtime... you can tweak this logic as you require.

The Resolve-Path is used in case someone passes in a relative path (i.e. '.\default.log'; as if the working directory changes as the script runs, the file which this refers to would also change. By resolving it to an absolute path when set this location is then locked down.

Rather than referring to the variable by name elsewhere in the module (i.e. $Script:LogFileLocation or $LogFileLocation), I'd recommend adding a Get method to allow logic to check that this variable was set, then using that. Of course, that may be an additional overhead that's not worth it (i.e. if performance is more important than robustness)... depending on your requirements.

Function Get-MyModuleLogPath {
    [CmdletBinding(DefaultParameterSetName='ErrorIfNotSet')]
    param (
        [Parameter(ParameterSetName='DefaultIfNotSet', Mandatory=$true)]
        [switch]$DefaultIfNotSet
        ,
        [Parameter(ParameterSetName='DefaultIfNotSet')]
        [string]$DefaultPath = '.\default.log'
    )
    process {
        try {
            $path = (Get-Variable -Name 'LogFileLocation' -Scope 1 -ValueOnly -ErrorAction Stop)
        } catch {
            if ($DefaultIfNotSet) {
                $path = Set-MyModuleLogPath $DefaultPath #once we've used the default we want it to become locked as the constant; so this log path won't change at runtime
            } else {
                throw "Please run 'Set-ModuleLogPath' to define a log path before calling 'Get-ModuleLogPath', or use the '-DefaultIfNotSet' switch to allow the default path to be used"
            }
        }
        $path
    }
}

Upvotes: 3

tommymaynard
tommymaynard

Reputation: 2152

Put the variable and its assignment ($LogFile = 'C:\path\to\file.log'), at the top of the .psm1 file, and it'll becomes a module-level variable, thus making it available to all the functions in the module.

Upvotes: 2

Related Questions