2

I'm having an issue here while creating a function with multiple parameters in PowerShell. I have 4 parameters, and their value can and will either be passed via pipeline or via direct input in a PowerShell window.

I can't seem to figure out how to make sure the 4 parameters can be used in the process section.

Here's what I have (the Write-Host section is only for testing, of course):

function Set-NewDevEnv {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$true, ValuefromPipelineByPropertyName=$true)]
        [ValidateSet('IIS', 'SQL', 'SOLR', 'Vanilla')]
        [string[]]$VMType,
        [Parameter(Mandatory=$true, ValuefromPipelineByPropertyName=$true)]
        [string[]]$StaticIP,
        [Parameter(Mandatory=$true, ValuefromPipelineByPropertyName=$true)]
        [string[]]$VMName,
        [Parameter(Mandatory=$true, ValuefromPipelineByPropertyName=$true)]
        [string[]]$VMDNSName
    )

    Begin {}

    Process {
        Write-Host "VMType is $VMType ; StaticIP is $StaticIP ; VMName is $VMName; and VMDNSName is $VMDNSName"
    }

If I use this, it works fine with Pipeline input, but not with direct command line input, i.e.:

Set-NewDevEnd -VMType "SQL","IIS" -StaticIp "192.168.0.1","192.168.0.2" -VMName "Testing1","Testing2" -VMDNSName "Testing1","Testing2"

I'm getting

VMType is SQL IIS ; StaticIP is 192.168.0.1 192.168.0.2 ; VMName is Testing1 Testing2 ; and VMDNSName is Testing1 Testing2

I know that usually, to support both direct input and pipeline input, you need to add a foreach loop in the process section, but how would I go about using a foreach loop with multiple parameters?

2 Answers 2

3

Parameters don't work that way. When you're passing the input via the pipeline each parameter gets one value at a time (for each object that's processed). In your commandline call you're passing all values at the same time, but the indexes of the individual parameter arrays have no relation to each other unless you implement that in your function. Otherwise, how should PowerShell handle a function with (for instance) two parameters that expect a string array and a simple string? Process the array and the simple value at the same time? Each array index along with a copy of the simple string? The first index with the simple string and the rest with null values? Something else?

What you can do is build a list of hashtables and splat them in a loop:

$data = @{
    'VMType'    = 'SQL'
    'StaticIP'  = '192.168.0.1'
    'VMName'    = 'Testing1'
    'VMDNSName' = 'Testing1'
},
@{
    'VMType'    = 'IIS'
    'StaticIP'  = '192.168.0.2'
    'VMName'    = 'Testing2'
    'VMDNSName' = 'Testing2'
}

$data | ForEach-Object { Set-NewDevEnv @_ }

although I'm not sure that would gain you any advantage over building custom objects and pipelining them.

Sign up to request clarification or add additional context in comments.

Comments

1

ValuefromPipelineByPropertyName means you need to have properties with same name in the object which you pass to input. Also the type of parameters don't need to be array. You can change the function this way:

Function Set-NewDevEnv {
    [cmdletbinding()]
    param (
        [Parameter(Mandatory=$true,ValuefromPipelineByPropertyName=$true)]
        [ValidateSet('IIS','SQL','SOLR','Vanilla')]
        [string]$VMType,
        [Parameter(Mandatory=$true,ValuefromPipelineByPropertyName=$true)]
        [string]$StaticIP,
        [Parameter(Mandatory=$true,ValuefromPipelineByPropertyName=$true)]
        [string]$VMName,
        [Parameter(Mandatory=$true,ValuefromPipelineByPropertyName=$true)]
        [string]$VMDNSName
    )

    begin {"Test Begin"}
    process {
        Write-Host "VMType is $VMType ; StaticIP is $StaticIP ;" + `
        "VMName is $VMName; and VMDNSName is $VMDNSName"
    }
    end {"Test End"}
}

And to test:

@([pscustomobject]@{
    VMType = "SQL";
    StaticIP = "192.168.0.1";
    VMName = "Testing1";
    VMDNSName= "Testing1"; }, 
  [pscustomobject]@{
    VMType = "IIS";
    StaticIP = "192.168.0.2";
    VMName = "Testing2";
    VMDNSName= "Testing2";}) | Set-NewDevEnv

This would be the result:

Test Begin
VMType is SQL ; StaticIP is 192.168.0.1 ; VMName is Testing1; and VMDNSName is Testing1
VMType is IIS ; StaticIP is 192.168.0.2 ; VMName is Testing2; and VMDNSName is Testing2
Test End

1 Comment

Thanks! Both Ansgar and you had pretty much the same answer hehe But yours, by using custom objects, helps me a lot more further down the line, as I'll name to call on those values specifically to build new VMs in VMware. Thanks for your help! :)

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.