5

We are building a bigger project with PowerShell (a collection of scripts/functions that helps us setup some SharePoint environments/tenants).

A lot of the functions should reuse settings that are stored in a single, central place.

I couldn't find a "best practice" of how such a settings file/location should be best created and structured.

My idea is to store global settings in a separate file (a module file), for example Settings.psm1 with content like this:

# Set vars
$global:scriptEnvironment = "SP2016HOSTINGDEV"
$global:logFileName = "z_Migration_to_SP2016.log"
$global:languageMapping = @{
    "en-US" = 1;
    "de-DE" = 2;
}
$global:oldWsps = @(
    [WspFile]@{ Filename = "comapany.solution.wsp"; IsDeployable = $true; IsGloballyDeployable = $false; FullTrustBinDeployment = $false },
    [WspFile]@{ Filename = "company.solution2.server.wsp"; IsDeployable = $true; IsGloballyDeployable = $false; FullTrustBinDeployment = $false }
)
...

And in the other modules/scripts I then could always include those settings like this:

# Set vars
$scriptDirectory = Split-Path -parent $PSCommandPath

# Module import
Import-Module (Join-Path $scriptDirectory Settings.psm1) -Force -ErrorAction Stop
Import-Module (Join-Path $scriptDirectory Functions.psm1) -Force -ErrorAction Stop

# Functions
...

So this would make me able to use the global settings like this in the functions inside other script files:

Function WriteLog
{
    param
    (
        [System.String]$LogString = "",
        [System.String]$LogLevel = ""
    )
    WriteLogToPath -LogFileName $global:logFileName -LogString $LogString -LogLevel $LogLevel
}

Is this a good approach? Or shouldn't I use module files for this and if not what kind of files should I use instead?

Patric
  • 2,789
  • 9
  • 33
  • 60

3 Answers3

2

I would probably collect all your scripts/functions in a module and use the registry to store the global settings. Read the values from the registry when the module is loaded, and have variables with default values for each setting in your module so that you can write missing values to the registry.

Something like this:

$modulename = Split-Path $PSScriptRoot -Leaf

$default_foo = 'something'
$default_bar = 'or other'
...

function Get-RegistrySetting($name) {
    $path = "HKCU:\Software\${script:modulename}"

    if (-not (Test-Path -LiteralPath $path)) {
        New-Item -Path $path -Force | Out-Null
    }

    try {
        Get-ItemProperty $path -Name $name -ErrorAction Stop |
            Select-Object -Expand $name
    } catch {
        $val = Get-Variable -Scope script -Name "default_${name}" -ValueOnly -ErrorAction Stop
        Set-ItemProperty $path -Name $name -Value $val
        $val
    }
}

$global:foo = Get-RegistrySetting 'foo'
$global:bar = Get-RegistrySetting 'bar'
...

For variables you only use inside your module you may want to use the script scope instead of the global scope.

Ansgar Wiechers
  • 193,178
  • 25
  • 254
  • 328
  • Nice approach but we want it to be simple and quickly changeable - so we prefer text files for storing of settings. Changing a setting value in a text file is a bit quicker than going the registry route. – Patric Dec 13 '17 at 14:40
  • Whatever floats your boat. – Ansgar Wiechers Dec 13 '17 at 14:50
1

I would avoid using the registry personally. I agree about using modules though. My approach would be to use a manifest module (i.e. using a .psd1 file which essentially is a key-value hashtable containing metadata about a module), and specifying a 'root' module with the RootModule key.

The module scope is now set by this RootModule, and you can define your variables in there.

You could separate out your functions into 'nested' modules (another manifest file key), and these are loaded automatically by PowerShell into the rootmodule scope, so should have access to those variables.

You can even control what variables and functions are exported using keys in that manifest file as well.

Laurence
  • 419
  • 1
  • 5
  • 12
0

Check the Get-Configuration Powershell module. Concept is easy, module adds Environment variable in which (in JSON) definition of type and source is saved.

(dir env:PSGetConfiguration).Value
{
    "Mode":  "Xml",
    "XmlPath":  "C:\\Managers\\PSGetConfiguration.xml"
}

Configuration file is very simple and contains just key and value items

cat $(Get-ConfigurationSource).XmlPath
<Configuration>
  <conf key="ExampleKey" value="ExampleValue" />
  <conf key="key" value="Value123" />
  <conf key="remove" value="remove" />
</Configuration>

Module expose two main functions Get-Configuration and Set-Configuration

Set-Configuration -Key k1 -Value v1
Get-Configuration -Key k1
v1

XML Powershel Configuration

At start module saves XML in module directory it can be changed by manually changing the environment variable or using command Set-XmlConfigurationSource

SQL Configuration

By default module uses XML to store data, but second option is to store data in the SQL. Setting configuration is pretty easy:

Set-SqlConfigurationSource -SqlServerInstance ".\sql2017" -SqlServerDatabase "ConfigDatabase" -SqlServerSchema "config" -SqlServerTable "Configuration"

After this our config will be stored in SQL table.

Configuration in SQL

Code is also available in github.

Pawel Wujczyk
  • 422
  • 3
  • 12