Sandwichnick
Sandwichnick

Reputation: 1466

Powershell: use values from json as parameter defaults

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?

Upvotes: 3

Views: 2487

Answers (4)

mklement0
mklement0

Reputation: 437100

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:

Upvotes: 2

jbsmith
jbsmith

Reputation: 1666

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.

Upvotes: 3

iRon
iRon

Reputation: 23623

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

Upvotes: 2

Manuel Batsching
Manuel Batsching

Reputation: 3596

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

Upvotes: 3

Related Questions