34

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;
Eric Longstreet
  • 783
  • 1
  • 9
  • 23
  • 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.) – ruffin Dec 03 '21 at 15:19
  • 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. – xorcus Mar 02 '22 at 09:27

11 Answers11

36

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                                            
  • 2
    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! – Eric Longstreet Feb 04 '14 at 19:28
  • @EricLongstreet Glad to hear that worked for you. :) –  Feb 05 '14 at 00:08
  • 5
    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. – jpmc26 Jun 30 '16 at 01:12
  • 3
    Also worth noting that `$PSCmdlet` only works if you have an explicit `[CmdletBinding()]`, whereas `$MyInvocation.MyCommand` works for all functions. – jpmc26 Mar 02 '17 at 21:21
  • 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"`. – Tydaeus Jun 23 '20 at 20:46
  • @Tydaeus - self-correction: Use the `Value` property of the Get-Variable output, or from `Get-Item "Variable:$name"`. `Get-Content "Variable:$name"` doesn't work properly with arrays. – Tydaeus Jun 26 '20 at 18:54
17

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"
}
JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
  • 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. – Eric Longstreet Feb 04 '14 at 17:54
  • 1
    @EricLongstreet my updated sample doesn't use any fixed names. It just goes straight through the bound parameter list – JaredPar Feb 04 '14 at 17:56
  • 5
    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 – Eric Longstreet Feb 04 '14 at 17:58
  • This actually worked for me in case of calling a .ps1 file using `& .\MyScript.ps1 -param1 value 1`. Using `$MyInvocation.InvocationName` would return `&` – Farrukh Waheed Mar 14 '22 at 14:18
  • confirmed: does not include parameters not explicitly passed, should not be upvoted. see this instead (same thread answer by Janos): https://stackoverflow.com/a/47290923/1279373 – spioter Jun 20 '23 at 20:41
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  
Janos
  • 101
  • 1
  • 6
  • 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``` – spioter Jun 20 '23 at 20:46
7

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"
    }
}
cslotty
  • 1,696
  • 20
  • 28
  • 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``` – spioter Jun 20 '23 at 20:43
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

  • 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. – Martin Guth Nov 22 '19 at 10:14
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> " ))
        }
       }
Kelly Davis
  • 354
  • 2
  • 6
  • Wouldn't you handle the missing parameter as an error by defining it using [Parameter( Mandatory )] and add [ValidateNotNullOrEmpty()]? – Jim Sep 09 '22 at 13:54
1

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.
Sean Saleh
  • 460
  • 5
  • 17
mklement0
  • 382,024
  • 64
  • 607
  • 775
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.

danielnelz
  • 3,794
  • 4
  • 25
  • 35
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
Nick W.
  • 1,536
  • 3
  • 24
  • 40
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
ToastedSoul
  • 1,296
  • 1
  • 17
  • 27
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"; }
    )
m1m1k
  • 1,375
  • 13
  • 14