Steve B
Steve B

Reputation: 37700

Dynamic invoke command with different parameters

In a PowerShell script, I want to read a CSV file that contains something like this:

Type  Title      Param1        Param2       
----  -----      ------        ------       
Type1 Foo type 1 ValueForType1              
Type2 Foo type 2               ValueForType2

When type is Type1, I have to call a function named New-FooType1, when type is Type2, the funcation is named New-FooType2, and so on:

function New-FooType1{
    param(
        [Parameter(Mandatory=$true, ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
        [string]$Title,
        [Parameter(Mandatory=$true, ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
        [string]$Param1
    )

    Write-Host "New-FooType1 $Title with $Param1"
}

function New-FooType2{
    param(
        [Parameter(Mandatory=$true, ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
        [string]$Title,
        [Parameter(Mandatory=$true, ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
        [string]$Param2
    )

    Write-Host "New-FooType2 $Title with $Param2"
}

I'm trying to route the call to either of the functions, using a dynamic invocation:

$csv | % {
    $cmdName = "New-Foo$($_.Type)"
    Invoke-Command (gcm $cmdName) -InputObject $_

}

However, I always get an error:

Parameter set cannot be resolved using the specified named parameters

As you can see, different type mean different parameters set.

How can I solve this? I would like to avoid manipulating properties manually, because in my real life script, I have a dozen of different types, with up to 6 parameters.

Here is a complete repro sample of the issue:

$csvData = "Type;Title;Param1;Param2`nType1;Foo type 1;ValueForType1;;`nType2;Foo type 2;;ValueForType2"

$csv = ConvertFrom-csv $csvData -Delimiter ';'

$csv | ft -AutoSize

function New-FooType1{
    param(
        [Parameter(Mandatory=$true, ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
        [string]$Title,
        [Parameter(Mandatory=$true, ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
        [string]$Param1
    )

    Write-Host "New-FooType1 $Title with $Param1"
}

function New-FooType2{
    param(
        [Parameter(Mandatory=$true, ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
        [string]$Title,
        [Parameter(Mandatory=$true, ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
        [string]$Param2
    )

    Write-Host "New-FooType2 $Title with $Param2"
}


$csv | % {
    $cmdName = "New-Foo$($_.Type)"
    Invoke-Command (gcm $cmdName) -InputObject $_

}

The expected output of this script is:

New-FooType1 Foo type 1 with ValueForType1
New-FooType2 Foo type 2 with ValueForType2

Upvotes: 3

Views: 761

Answers (3)

user4003407
user4003407

Reputation: 22132

To invoke command by name you should use invoke operator &. Invoke-Command cmdlet support only ScriptBlock and file invocation, and file invocation only supported for remote calls.

For dynamic parameter binding you can use spatting, but in that case you have to convert PSCustomObjects, returned by ConvertFrom-Csv cmdlet, to Hashtable. You also have to strip any extra parameters from Hashtable because splatting will fail if you try to bind non-existing parameter.

Another approach for dynamic parameter binding would be to use binding from pipeline object. It looks like it is what you want to do, since you mark all your parameters with ValueFromPipelineByPropertyName option. And this approach will just ignore any extra property it can not bind to parameter. I recommend you to remove ValueFromPipeline option, because with this option in case of absence of property with parameter name PowerShell will just convert PSCustomObject to string (or to whatever type you use for parameter) and pass it as value for parameter.

So, all you need is to pass object by pipeline and use invoke operator for invocation of command with dynamic name:

$_ | & "New-Foo$($_.Type)"

Upvotes: 2

Mathias R. Jessen
Mathias R. Jessen

Reputation: 174700

Use the call operator &:

$CmdName = "New-FooType1"
$Arguments = "type1"

& $CmdName $Arguments

the call operator also supports splatting if you want the arguments bound to specific named parameters:

$Arguments = @{
    "title" = "type1"
}

& $CmdName @Arguments

Upvotes: 4

Chris Bakker
Chris Bakker

Reputation: 11

dont know exactly what your trying to do, but Invoke-Command (gcm $cmdName) ?

Try invoke-expression $cmdname

Upvotes: -2

Related Questions