How to set PowerShell PSDefaultParameterValues conditionally based on other parameters

Viewed 102

Without going much into details on why am I even trying this out, is it possible to set PSDefaultParameterValues conditionally based on other parameter values?

Let's say I would like to set -Force if ItemType is Directory in New-Item call.

$PSDefaultParameterValues = @{ "New-Item:Force" = {
        # TODO: if Itemtype is Directory, return $true
        # else return default: false
        return $false
    }
}
New-Item -ItemType Directory

Problem is, that I can get the parameters used in $args but I do not have access to their values.

debugger screenshot

1 Answers

As you've observed, the argument passed to your script block via the automatic $args variable contains the names of the bound parameters in the New-Item call at hand, but lacks their values.

The following workaround isn't foolproof, but may suffice in practice:

$PSDefaultParameterValues = @{
  'New-Item:Force' = { 
    ($false, $true)[
      $args.BoundParameters.Contains('ItemType') -and 
      (Get-PSCallStack)[1].Position.Text -match '\bDirectory\b'
    ]
  }
}

You could tweak the regex to be stricter, but note that PowerShell's elastic syntax and parameter aliases make it hard to match a parameter name reliably; e.g., -Type Directory, -it Directory and -ty Directory are all acceptable variations of -ItemType Directory.

A caveat is that this won't work if you pass the Directory argument to -ItemType in New-Item calls via a variable; e.g., $type='Directory'; New-Item -ItemType $type ... would not be recognized by the script block. Handling that case would require substantially more work.


Note:

  • The parent call-stack entry, which you can obtain as the 2nd element of the call-stack array returned by Get-PSCallStack, contains the raw command text of the New-Item call at hand (in property .Position.Text), which the solution above examines.

    • However, since it is the raw command text, it doesn't include the expanded argument values that are ultimately seen by the command; that is, what variable references and expression evaluate to isn't directly available.
    • You could perform your own expansion, assuming you've reliably identified the variable reference / subexpression of interest, but note that, at least in principle, evaluating a subexpression can have side effects (and possibly also take a long time to execute), so effectively executing it twice may be undesirable.
Related