3

I want to write a script with parameters, where the default values for those parameters are stored in a json file. Unfortunately, Powershell does not recognise param() if it is not the first line of code within the script.

But of course if it is the first line of the script, it cannot load the config in advance and use its value as default value. So I am in anpickle.

The config.json:

{"defaultValue":"Default"}

The powershell script:

$configPath = "config.json"
$configPath = Join-Path $PSScriptRoot $configPath

# read config
$config = Get-Content $configPath | ConvertFrom-Json

write-host $config.defaultValue

param($myparam = $config.defaultValue)

write-host $myparam

I am new to powershell, is there a way to set default values of parameters later in the script? Or alternatively, read parameters later into the script?

4 Answers 4

3

Lets assume your config.json looks like this:

{
    "Name": "Earthling",
    "Color": "Pink"
}

Depending on your actual scenario you could pass the path to the config.json in as a parameter and make all the other parameters optional.

Within the script you load the config and provide default values for all parameters that have not been set in the script invocation:

[CmdletBinding()]
param (
    [Parameter()]
    [string]
    $Name,

    [Parameter()]
    [string]
    $Color,

    [Parameter()]
    [string]
    $ConfigPath
)

    if ($ConfigPath -in  $PSBoundParameters.Keys) {

        $config = Get-Content $ConfigPath | ConvertFrom-Json

        if ('Name' -notin  $PSBoundParameters.Keys) {
            $Name = $config.Name
        } 
        if ('Color' -notin  $PSBoundParameters.Keys) {
            $Color = $config.Color
        } 
    }

    Write-Host "$Name your favourite color is $Color"

You would call the script like this:

PS> ./script.ps1  -ConfigPath ./config.json -Color Blue

Earthling your favourite color is Blue

That has the downside, that you cannot declare parameters as mandatory. An alternative approach that allows declaring parameters as mandatory would be to pipe the configuration into your script:

[CmdletBinding()]
param (
    [Parameter(ValueFromPipelineByPropertyName,Mandatory)]
    [string]
    $Name,

    [Parameter(ValueFromPipelineByPropertyName,Mandatory)]
    [string]
    $Color
)

    Write-Host "$Name your favourite color is $Color"

The invocation would then be:

PS> Get-Content ./params.json | ConvertFrom-Json | ./script.ps1 -Color Red

Earthling your favourite color is Red
Sign up to request clarification or add additional context in comments.

Comments

3

I would recommend using PSDefaultParameterValues; so you could load the JSON file into this hashtable at the start of your script.

To get around param needing to be at the start of the script, and to give you a function name to use in the PSDefaultParameterValues hashtable, you can define the core logic of your script in a function definition inside the script. Then the script itself just does 3 things:

  • read the JSON file and sets PSDefaultParameterValues
  • defines the function
  • invokes the function

I think this approach is simpler than DynamicParams and encourages the good practive of encapsulating your code into re-usable functions, and scripts are really just wrappers that invoke said functions.

Comments

2

Parameter default values can be assigned via arbitrary commands, and as long as your default values are parameter-specific and do not rely on the values of other parameters (or variables defined later in the script), the use of self-contained commands to determine default values is a viable option:

param(
  $myparam = (ConvertFrom-Json (Get-Content -Raw $PSScriptRoot/config.json)).defaultValue
)

# Output the parameter value for diagnostic purposes.
"[$myparam]"

The only potential problem is that if you want to assign multiple parameters' default values this way, you'll have to duplicate (variations of) this command:

Comments

2

You might want to use the DynamicParam { } block and possibly the Set-Variable cmdlet for this.
The disadvantage is that you can't set the related variable (like $Name) but could use $PSBoundParameters[$Name] instead or assign the bare variable names within the Begin block.
Quick and dirty example (no check e.g. if the related parameter really exist):

Function Test {
    [CmdletBinding()] param (
        [Parameter()][string]$Name,
        [Parameter()][string]$Color
    )
    DynamicParam {
        $Defaults = ConvertFrom-Json -AsHashTable '{
            "Name": "Earthling",
            "Color": "Pink"
        }'
        ForEach ($Key in $Defaults.get_Keys()) { 
            if (!$PSBoundParameters.ContainsKey($Key)) { $PSBoundParameters[$Key] = $Defaults[$Key] }
        }
    }
    Begin {
        ForEach ($Key in $PSBoundParameters.get_Keys()) { Set-Variable $Key $PSBoundParameters[$Key] }
    }
    Process {
        Write-Host 'Name:' $Name
        Write-Host 'Color:' $Color
    }
}

Test -Name Jim
Name: Jim
Color: Pink

1 Comment

The check if the parameters were set is needed though, otherwise Test -Name Jim -Color Blue will still return the default-values "Earthling" and "Pink"..

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.