Feeding switch parameter from pipeline input

Hello experts, I need to pass parameters to a script from pipeline input by importing the required values from CSV file. The original script has more than 15 parameters to be passed and input values are stored in a CSV file. I am posting a simple example to express the issue.

Below are the contents of CSV file (Input.csv)

ResourceGroupName,SetThrottling
TestRG1,1
TestRG2,0
TestRG3,1
TestRG4,0
TestRG5,0

Script file - Switch-FromPipelineTest.ps1

[CmdletBinding()]
Param
(
[Parameter(Mandatory=$True, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True)]
[String]$ResourceGroupName,

[Parameter(ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True)]
[Switch]$SetThrottling

)

Begin {}
Process
{
Function TestingValues
{
Param
(
$ResourceGroupName,
$SetThrottling
)

    Write-Host "$ResourceGroupName is set to $SetThrottling"
}

TestingValues -ResourceGroupName $ResourceGroupName -SetThrottling $SetThrottling

}
End {}

If I run the command Import-Csv .\Input.csv | .\Switch-FromPipelineTest.ps1 it gives an error as given below:

C:\Scripts\Switch-FromPipelineTest.ps1 : Cannot process argument transformation
on parameter ‘SetThrottling’. Cannot convert value “System.String” to type
“System.Management.Automation.SwitchParameter”. Boolean parameters accept only
Boolean values and numbers, such as $True, $False, 1 or 0.
At line:1 char:26

  • Import-Csv .\Input.csv | .\Switch-FromPipelineTest.ps1
  •                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    • CategoryInfo : InvalidData: (@{ResourceGroup…etThrottling=1}:stuck_out_tongue:
      SObject) [Swith-FromPipelineTest.ps1], ParameterBindingArgumentTransformati
      onException
    • FullyQualifiedErrorId : ParameterArgumentTransformationError,Switch-FromPi
      pelineTest.ps1

In order to make this work, I have to run below command:
Import-Csv -Path .\Input.csv -Verbose | Select-Object -Property ResourceGroupName,@{n=‘SetThrottling’;e={[bool][int]$_.SetThrottling}} | .\Switch-FromPipelineTest.ps1

Is there a way we can omit the type casting done by using custom property expression in the second command? as in the original script, I have several [switch] parameters and I need to do the same thing for each [switch] parameter.

remove ValueFromPipeline=$True from both parameters
and read some tips here
http://learn-powershell.net/2013/05/07/tips-on-implementing-pipeline-support/

todo: move function TestingValues to begin{} block

Hmm, I’ve never tried to do a switch parameter from the pipeline. However, when you’re using splatting, you can do switches with $true or $false, so maybe it’ll work in the pipeline if you make it a [bool] instead of [int].

@Max Kozlov: I went through that link earlier and also tried to remove ValueFromPipeline=$True from both parameters, it didn’t help. I still need to manually convert the value from CSV column to [Switch] / [bool] before passing it to the script.

@Dave Wyatt: I can execute the script if I use:
Import-Csv -Path .\Input.csv -Verbose | Select-Object -Property ResourceGroupName,@{n=‘SetThrottling’;e={[bool][int]$_.SetThrottling}} | .\Switch-FromPipelineTest.ps1

But I want to get rid of this extra layer of conversion using Select-Object, since my actual script has now around 30 parameters that I need to pass using CSV and there are many switch parameters that I have to convert like this if I don’t get a solution.

sorry, does not test solution before…
also you need to change or remove parameter types from outer proxy function and do type conversion inplace
this variant works well

[CmdletBinding()]
 Param
 (
 [Parameter(Mandatory=$True, ValueFromPipelineByPropertyName=$True)]
 [string]$ResourceGroupName,

[Parameter(ValueFromPipelineByPropertyName=$True)]
 [int]$SetThrottling
 )

Begin {
 Function TestingValues
 {
 Param
 (
 [string]$ResourceGroupName,
 [switch]$SetThrottling
 )

Write-Host "$ResourceGroupName is set to $SetThrottling"
 }
}
 Process
 {
	TestingValues -ResourceGroupName $ResourceGroupName -SetThrottling:([bool]$SetThrottling)
 }
 End {}

btw, import-csv afaik always import values as string thus you always need conversion as you do with select, as I suggest with inplace conversion or by using foreach-object:

Import-Csv -Path .\Input.csv -Verbose | Foreach-Object { $_.SetThrottling=[bool][int]$_.SetThrottling; $_ }  | .\Switch-FromPipelineTest.ps1

if you need to convert many parameters you can do this in cycle like

$toconvert = 'SetThrottling','param1','param2'

Import-Csv -Path .\Input.csv -Verbose | Foreach-Object {
foreach ($param in $toconvert) {
$_.$param = [bool][int]$_.$param 
}
$_
}  |
 .\Switch-FromPipelineTest.ps1

As Max said, the main problem here is that Import-Csv makes all values from the CSV file as String values, and powershell obviously does not like trying to convert the string values to bool.

I too would recommend just placing a Select-Object between your Import-Csv and your function with a calculated property to try to convert the property from the CSV file into a Boolean value. It may seem like unnecessary work but unfortunately it’s due to the way the Import-Csv cmdlet was designed (to this point at least).

PS Merry Christmas!!

Thank you all for your inputs. After looking at all possibilities I think I have to convert the switch parameter to a string parameter by sung a validate set of “Yes” and “No”. I will change the CSV format to use Yes and NO instead of 1 and 0.

In script I will check the value using If condition whether $SetThrottling = Yes or No.

That seems to be the only option for now as there is no other way we can pass Bool values through pipeline while importing CSV.

(Post update: Scratch that, I see the problem and the below does not help the issue)

@savindrasingh-shahoo use [System.Convert]::ToBoolean()

https://social.technet.microsoft.com/Forums/windowsserver/en-US/300b5602-b16a-4a10-96fd-3e2e6ba29ed6/importcsv-with-true-and-false-values?forum=winserverpowershell

[pscustomobject]@{
    Value1 = $true
    Value2 = $false
    Value3 = "string"
} | Export-Csv test.csv

$a = Import-Csv test.csv

[System.Convert]::ToBoolean($a.Value1)
[System.Convert]::ToBoolean($a.Value2)
[System.Convert]::ToBoolean($a.Value3)

($a.Value1).GetType()
($a.Value2).GetType()
([System.Convert]::ToBoolean($a.Value1)).GetType()
([System.Convert]::ToBoolean($a.Value2)).GetType()

Results:

True
False
Exception calling "ToBoolean" with "1" argument(s): "String was not recognized as a valid Boolean."
At line:11 char:1
+ [System.Convert]::ToBoolean($a.Value3)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : FormatException

IsPublic IsSerial Name                                     BaseType                                                                                                                  
-------- -------- ----                                     --------                                                                                                                  
True     True     String                                   System.Object                                                                                                             
True     True     String                                   System.Object                                                                                                             
True     True     Boolean                                  System.ValueType                                                                                                          
True     True     Boolean                                  System.ValueType