37

I'm trying to find a way to get all parameter information from a powershell script. Ex script:

function test()
{
    Param(
        [string]$foo,
        [string]$bar,
        [string]$baz = "baz"
    )

    foreach ($key in $MyInvocation.BoundParameters.keys)
    {
        write-host "Parameter: $($key) -> $($MyInvocation.BoundParameters[$key])"
    }
}
test -foo "foo!"

I'd like to get the values of $bar and $baz in a dynamic way without knowing the names of the parameters ahead of time.

I've looked through $MyInvocation properties and methods but I don't see anything besides parameters that are set/passed.

Update 1:

I'm close to getting it with:

function test()
{
    Param(
        [string]$foo,
        [string]$bar,
        [string]$baz = "baz"
    )
    foreach($var in (get-variable -scope private))
    {
        write-host "$($var.name) -> $($var.value)"
    }
}
test -foo "foo!"

If i could filter out the script parameters vs the default parameters I would be good to go.

Update 2: The final working solution looks like this:

function test {
    param (
          [string] $Bar = 'test'
        , [string] $Baz
        , [string] $Asdf
    )
    $ParameterList = (Get-Command -Name $MyInvocation.InvocationName).Parameters;
    foreach ($key in $ParameterList.keys)
    {
        $var = Get-Variable -Name $key -ErrorAction SilentlyContinue;
        if($var)
        {
            write-host "$($var.name) > $($var.value)"
        }
    }
}

test -asdf blah;
3
  • 2
    Your answer seems to work and... is different than the accepted answer. Is there a reason you didn't add it as an answer and accept it if it's the best answer? (And I would argue it may be, since there's no [CmdletBinding()] requirement.) Commented Dec 3, 2021 at 15:19
  • 1
    Aside: Get-Variable returns an object with Name and Value properties. In some use cases you may know the name in advance and just need to check if the value was supplied (e.g. optional ScriptBlock parameter, running in strict mode with Set-StrictMode -Version 3). Get-Variable has a switch -ValueOnly for returning just the value and -ErrorAction SilentlyContinue takes care of returning $null instead of throwing an exception if the parameter was not supplied. Commented Mar 2, 2022 at 9:27
  • 1
    Just in case anyone's confused like me, to make it explicit, $MyInvocation.MyCommand.Parameters contains all parameters, passed or not, default values or not, but ALSO includes all the CommonParameters: Verbose, Debug, ErrorAction, ErrorVariable, WarningAction, WarningVariable, OutBuffer, PipelineVariable, and OutVariable. But there is a variable for the explicit parameters but NOT for the CommonParameters, so Get-Variable errors for the CommonParameters (and the -ErrorAction SilentlyContinue makes it return null instead) and that's how you can tell them apart. Commented May 11, 2024 at 1:55

12 Answers 12

42

Check this solution out. This uses the CmdletBinding() attribute, which provides some additional metadata through the use of the $PSCmdlet built-in variable. You can:

  1. Dynamically retrieve the command's name, using $PSCmdlet
  2. Get a list of the parameter for the command, using Get-Command
  3. Examine the value of each parameter, using the Get-Variable cmdlet

Code:

function test {
    [CmdletBinding()]
    param (
          [string] $Bar = 'test'
        , [string] $Baz
        , [string] $Asdf
    )
    # Get the command name
    $CommandName = $PSCmdlet.MyInvocation.InvocationName;
    # Get the list of parameters for the command
    $ParameterList = (Get-Command -Name $CommandName).Parameters;

    # Grab each parameter value, using Get-Variable
    foreach ($Parameter in $ParameterList) {
        Get-Variable -Name $Parameter.Values.Name -ErrorAction SilentlyContinue;
        #Get-Variable -Name $ParameterList;
    }
}

test -asdf blah;

Output

The output from the command looks like this:

Name                           Value                                           
----                           -----                                           
Bar                            test                                            
Baz                                                                            
Asdf                           blah                                            
Sign up to request clarification or add additional context in comments.

6 Comments

Yes! This kind of approach is exactly what I was looking for. I ended up having to touch up the script a bit because I kept getting nulls, but this is definitely the way to go. Thanks!
@EricLongstreet Glad to hear that worked for you. :)
I think you can skip the $PSCmdlet and Get-Command and just use $MyInvocation.MyCommand.Parameters, and that you can just loop over Parameters.Keys to get the parameter names.
Also worth noting that $PSCmdlet only works if you have an explicit [CmdletBinding()], whereas $MyInvocation.MyCommand works for all functions.
Some technical notes for if you want to adapt this sample code for other uses: 1. $Parameter.Values.Name is a list of names, not a single name, so you may want to iterate through this list if you're passing it to something other than Get-Variable (which accepts lists without issue). 2. Get-Variable produces object output which is nice for table display, but requires extra effort to pass as a parameter. Accessing through the Variable: drive instead may help, e.g. Get-Content "Variable:$name".
|
18

To read the value dynamically use the get-variable function / cmdlet

write-host (get-variable "foo")

To print out all of the parameters do the following

foreach ($key in $MyInvocation.BoundParameters.keys)
{
    $value = (get-variable $key).Value 
    write-host "$key -> $value"
}

5 Comments

Thanks for that, wasn't aware of that commandlet. However, i'm looking to get the values dynamically without knowing the names of the variables.
@EricLongstreet my updated sample doesn't use any fixed names. It just goes straight through the bound parameter list
Sorry missed that, however it looks like $MyInvocation.BoundParameters.keys doesn't contain $bar and $baz, only actual bounded parameters. I'm really interested in getting $baz who might have a default value set already and is never passed
This actually worked for me in case of calling a .ps1 file using & .\MyScript.ps1 -param1 value 1. Using $MyInvocation.InvocationName would return &
confirmed: does not include parameters not explicitly passed, should not be upvoted. see this instead (same thread answer by Janos): stackoverflow.com/a/47290923/1279373
8

Hopefully, some may find this one-liner useful:

function test()
{
    Param(
        [string]$foo,
        [string]$bar,
        [string]$baz = "baz"
    )

    $MyInvocation.MyCommand.Parameters | Format-Table -AutoSize @{ Label = "Key"; Expression={$_.Key}; }, @{ Label = "Value"; Expression={(Get-Variable -Name $_.Key -EA SilentlyContinue).Value}; }
}
test -foo "foo!"

Result

Keys Value
---- -----
foo  foo! 
bar       
baz  baz  

1 Comment

upvoted bc this works, including values not explicitly passed and assigned by default. However, it also includes many other variables which i suppose are powershell global/overhead of some kind: Verbose Debug ErrorAction WarningAction InformationAction ErrorVariable WarningVariable InformationVariable OutVariable OutBuffer PipelineVariable
6

I found this most useful for PS4 (Windows 2012 R2) - it includes default values / optional parameters:

$cmdName = $MyInvocation.InvocationName
$paramList = (Get-Command -Name $cmdName).Parameters
foreach ( $key in $paramList.Keys ) {
    $value = (Get-Variable $key -ErrorAction SilentlyContinue).Value
    if ( $value -or $value -eq 0 ) {
        Write-Host "$key -> $value"
    }
}

1 Comment

this is perfect! it includes params not explicitly passed and it omitted the system/global variables like the following Verbose Debug ErrorAction WarningAction InformationAction ErrorVariable WarningVariable InformationVariable OutVariable OutBuffer PipelineVariable
5

For those of you who do not want to use cmdletbinding() here's a variation on the one liner I found above:

(Get-Command -Name $PSCommandPath).Parameters | Format-Table -AutoSize @{ Label = "Key"; Expression={$_.Key}; }, @{ Label = "Value"; Expression={(Get-Variable -Name $_.Key -EA SilentlyContinue).Value}; }

$PSCommandPath is always available

1 Comment

Wow thanks...saved my day. I had issues not getting any decent return from the function rated best above....switched over to "Invoke-Expression" to get a return value but that brought different issues for me...so I reverted to "&" for calling one ps1 script from another one and took your solution.
2

A streamlined solution that:

  • builds on your own approach now shown at the bottom of the question,

  • while also including an additional piece of information, namely whether each parameter was bound on invocation, i.e. whether an argument was passed or not, based on its presence in the automatic $PSBoundParameter variable, which is a dictionary containing the bound parameters and their values (arguments).

    • Note: $PSBoundParameters does not include parameters bound by a default value, only those parameters to which an argument was explicitly passed; for your use case, that is arguably desirable in order to distinguish between a default value and an explicit one, but in scenarios where arguments should be passed through it may be desirable to have default-value parameters included - see GitHub issue #3285.
function test {
  param (
    [string]$foo,
    [string]$bar,
    [string]$baz = "baz"
  )
  foreach ($paramName in $MyInvocation.MyCommand.Parameters.Keys) {
    $bound = $PSBoundParameters.ContainsKey($paramName)
    [pscustomobject] @{
      ParameterName = $paramName
      ParameterValue = if ($bound) { $PSBoundParameters[$paramName] }
                       else { Get-Variable -Scope Local -ErrorAction Ignore -ValueOnly $paramName }
      Bound = $bound
    }
  }
}

test -foo "foo!"

The above yields the following:

ParameterName ParameterValue Bound
------------- -------------- -----
foo           foo!            True
bar                          False
baz           baz            False

Note: This solution also handles dynamic parameters correctly:

  • Such parameters are reflected in $MyInvocation.MyCommand.Parameters if they situationally apply, but - unlike regular static parameters - are never reflected in scope-local variables. If they apply and are also bound, they and their values are reflected in $PSBoundParameters.
  • Thus - given that an applicable dynamic parameter may not be bound - the Get-Variable call to look for scope-local variables representing static parameters:
    • Must explicitly be limited to the current scope with -Scope Local so as not to accidentally pick up unrelated variables of the same name from ancestral scopes for unbound dynamic parameters.
    • Must ignore errors from the potentially resulting failure to find a variable in the current scope, using -ErrorAction Ignore.

Comments

1

I played with the 2 solutions i liked in this thread, they both work. however I needed to produce an error out on missing parameter for a build script

   $cmdName = $MyInvocation.InvocationName 
   $paramList = (Get-Command -Name $cmdName).Parameters
     foreach ( $key in $paramList.Keys ) {
     $value = (Get-Variable $key -ErrorAction Stop)
     #Write-Host $value.Value #remove comment for error checking
        if ([string]::IsNullOrEmpty($value.Value)){ 
        $(throw ("$key is a mandatory value please declare with -$key <Required value> " ))
        }
       }

1 Comment

Wouldn't you handle the missing parameter as an error by defining it using [Parameter( Mandatory )] and add [ValidateNotNullOrEmpty()]?
1

What if I don't know what arguments or how many will be passed? For example, the function could be called with:

test -foo "value1" -bar "value2" -baz "value3"

and someone else might call it with:

test -variable1 "somevalue" -variable2 "somevalue2" -variable3 "somevalue3" -variable4 "value4"

I looked at $MyInvocation and the arguments come across as UnboundArguments, so theoretically I could "pair up" argument 0 with argument 1, 2 with 3, etc and error out if there's an odd number of UnboundArguments.

1 Comment

My "Update 2" example should work for this case as well, GL.
1

Stumbled upon this trying to do something similar and figured out my preferred option.

Function ParamTest {
    Param (
        $t1 = '1234',
        $t2,
        [switch]$3
    )
    
    $MyInvocation |Add-Member -Name:'Param' -MemberType:'NoteProperty' -Value:(
        (Get-Variable -Scope:'Local' -Include:@($MyInvocation.MyCommand.Parameters.keys)|
            ForEach-Object -begin:{$h=@{}} -process:{$h.add($_.Name,$_.Value)} -end:{$h}
    ))

    $MyInvocation.Param
}

Result

Name                           Value                                                                                                                                                                                                                                                                                                                      
----                           -----                                                                                                                                                                                                                                                                                                                      
t1                             1234                                                                                                                                                                                                                                                                                                                       
3                              False                                                                                                                                                                                                                                                                                                                      
t2

                          

PSVersionTable

Name                           Value                                                                                                                                                                                                                                                                                                                      
----                           -----                                                                                                                                                                                                                                                                                                                      
PSVersion                      5.1.19041.1320                                                                                                                                                                                                                                                                                                             
PSEdition                      Desktop                                                                                                                                                                                                                                                                                                                    
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}                                                                                                                                                                                                                                                                                                    
BuildVersion                   10.0.19041.1320                                                                                                                                                                                                                                                                                                            
CLRVersion                     4.0.30319.42000                                                                                                                                                                                                                                                                                                            
WSManStackVersion              3.0                                                                                                                                                                                                                                                                                                                        
PSRemotingProtocolVersion      2.3                                                                                                                                                                                                                                                                                                                        
SerializationVersion           1.1.0.1

Comments

0

I wanted a compact string of parameter key/value pairs that I can write out when catching error. Use content of the other answers, I came up with this:

$parameters = (Get-Variable -Scope:'Local' -Include:@($MyInvocation.MyCommand.Parameters.keys) |
        Select-Object Name, Value | ForEach-Object { "$($_.Name) : $($_.Value)" }) -join ' | '

Sample output:

ComputerName : SomePC | Directory : C:\Tools\LogExpert | UserName : Hans Wurst | YesOrNo : False

Comments

0

Thanks to all other posters with great & helpful answers above! I'm cobbling together some of the above posts to try to meet my requirements. I'd like to show Params in a Powershell-language-compatible format, so that one could easily see & display these params, and then also copy+pasta back into either a script as Param() Defaults (ParamsFormat1) or into a commandline/function call (CommandFormat2). I struggled with this for a while, so I hope this saves someone some time.

Collection of various Scripts from above:

# Note: there are some subtle differences between these versions below
# Specifically in the first line for *which* object you're checking for Parameters, and subsequently what type of Parameters you're looking at.


# PSCmdlet version REQUIRES [CmdletBinding()] on your function!!!
# $($(Get-Command -Name $($PSCmdlet.MyInvocation.InvocationName)).Parameters) | `
#   %{ Get-Variable -Name $_.Values.Name -ErrorAction SilentlyContinue; } 

# -Scope:'Local' FILTERS out any global params
# (Get-Variable -Scope:'Local' -Include:@($MyInvocation.MyCommand.Parameters.keys)) | ft

# PSCommandPath supposedly available/usable "in all situations" - so this may be the most useful of all of them, BUT it shows global params [Debug, ErrorAction, etc...]
# (Get-Command -Name $PSCommandPath).Parameters  | `
#    %{ Get-Variable -Name $_.Values.Name -ErrorAction SilentlyContinue; } 

ParamsFormat1 / HYBRID VERSION: To output "Default Params" as you would see them in a function definition. Combines the -Scope:"Local" and "always available" versions to get BOTH param TYPES as well as Name, Value

Write-Host "Params("
# TBD Figure out how to expand @{} of System.Collections.Hashtable

# HYBRID VERSION: Combine the -Scope:"Local" and "always available" versions to get BOTH param TYPES as well as Name, Value

# If you remove LocalList Filter here, it will also show "Global" function properties like Debug, ErrorAction, etc...
$LocalList = $(Get-Variable -Scope:'Local' -Include:@($MyInvocation.MyCommand.Parameters.keys) | Select-Object -ExpandProperty Name) -join "|"
# VERSION: Wrapper script with DEFAULTS : normally DOES include Global vars [Debug, ErrorAction, etc]. but DOES preserve param order as-written.
((Get-Command -Name $PSCommandPath).Parameters | `
    Select -ExpandProperty Values | `
    Where-Object { $_.Name -Match $LocalList } | `
    Format-Table -HideTableHeaders -AutoSize `
    @{ Label="Type"; Expression={"[$($_.ParameterType )]"}; }, `
    @{ Label="Name"; Expression={"`t`$$($_.Name)"}; }, `
    @{ Label="Equals"; Expression={"="}; }, `
    @{ Label="Value"; Expression={ If( $_.ParameterType -Match "String" ) { "`"$((Get-Variable -Name $_.Name -EA SilentlyContinue).Value)`"" } Else{ $((Get-Variable -Name $_.Name -EA SilentlyContinue).Value)}; }; }, `
    @{ Label="RowEndComma"; Expression={ "," }; }
    #@{ Label="Value"; Expression={ $((Get-Variable -Name $_.Name -EA SilentlyContinue).Value) }; } # (OPTIONAL) Values only, no wrapping quotes
)
Write-Host ")";

CommandFormat2 / SIMPLER VERSION: Call with CommandLine Args (TYPES not needed). This filters out Global vars, does NOT preserve param ordering. (sorted alphabetically?)

# VERSION: Call with CommandLine Args (TYPES not available - TBD needs more work to display String, Array, and Hashmap/PsCustomObject ["", @() and {}] types better): filters out Global vars, does NOT preserve param ordering.
# If needed, cut+paste OPTIONAL lines down above Format-Table line.
# Where-Object { $_.Value } | `     # (Optional) remove any Null items.  
# Sort-Object -Property Name | `    # (Optional) sort output
    (Get-Variable -Scope:'Local' -Include:@($MyInvocation.MyCommand.Parameters.keys) | `    
        Format-Table -HideTableHeaders -AutoSize `
        @{ Label="Name"; Expression={"`t-$($_.Name)"}; }, `
        @{ Label="Equals"; Expression ={":"}; }, `
        @{ Label="Value"; Expression={ If( $_.ParameterType -NotMatch "String" ) { $_.Value; } Else {"`"$($_.Value)`"";} }; Alignment="left"; }
    )

Comments

0

If anyone is looking to find the default value of a command's parameter while in a DynamicParam block, I was able to leverage help documentation to achieve this.

Default values are not assigned to parameters until after dynamic parameters are evaluated, which means $MyInvocation isn't going to contain defaults when you observe it within a DynamicParam block, and parameters aren't even defined yet so you cannot use Get-Variable either.

(Get-Help -Name $MyInvocation.MyCommand.Name -Full).parameters.parameter[0].defaultValue

If you ever re-order your parameter listing, or change Position properties, change the array value accordingly. But, at least this keeps you from having to update hard-coded default values in multiple places.

If someone has a better way to find default parameter values inside a DynamicParam block of the same command without using help text, that would be great.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.