How to prevent Invalid cast from System.Boolean? - powershell

This is the content of the my_script.ps1 file:
function InitDotnet {
param (
[Parameter(Mandatory)]
[string]
$ProjectName,
[Parameter(Mandatory)]
[ValidateSet("cli", "lib")]
[string]
$ProjectType,
[Parameter]
[bool]
$Xunit = $false,
[Parameter]
[bool]
$Src = $false
)
Write-Host "Name:" $ProjectName
Write-Host "Type:" $ProjectType
Write-Host "Create a xunit project:" $Xunit
Write-Host "Create a src folder:" $Src
}
InitDotnet $args[0] $args[1] $args[2] $args[3]
First, I don't know why the default bool values don't work.
.\my_script.ps1 Test lib
Name: Test
Type: lib
Needs xUnit:
Create a src folder:
And I can't pass Boolean arguments to my .ps1 script without casting errors.
.\my_script.ps1 Test lib $true
InitDotnet : Cannot process argument transformation on parameter 'Xunit'.
Cannot convert value "True" to type "System.Management.Automation.ParameterAttribute".
Error: "Invalid cast from 'System.Boolean' to 'System.Management.Automation.ParameterAttribute'."
What am I doing wrong?

Related

Conditional Mandatory in PowerShell

I'm trying to make a parameter mandatory, but only if another parameter uses certain ValidateSet values. It seems that using a code block on Mandatory doesn't work as expected.
function Test-Me {
[CmdletBinding()]
Param (
[Parameter()]
[ValidateSet("NameRequired", "AlsoRequired")]
[string]
$Type = "NoNameRequired",
[Parameter(Mandatory = {-not ($Type -eq "NoNameRequired")})]
[string]
$Name
)
Process {
Write-Host "I ran the process block."
Write-Host "Type = '$Type'"
Write-Host "Name = '$Name'"
Write-Host "Name Parameter Mandatory? = '$(-not ($Type -eq "NoNameRequired"))'"
}
}
Set-StrictMode -Version Latest
function Test-Me {
[CmdletBinding(DefaultParameterSetName = "Gorgonzola")]
Param (
[Parameter(Mandatory)]
[int]
$Number,
[Parameter(Mandatory, ParameterSetName = "NameNeeded")]
[ValidateSet("NameRequired", "AlsoRequired")]
[string]
$Type = "NoNameRequired",
[Parameter(Mandatory, ParameterSetName = "NameNeeded")]
[string]
$Name
)
Process {
Write-Host "I ran the process block."
Write-Host "Number = '$Number'"
Write-Host "Type = '$Type'"
Write-Host "Name = '$Name'"
Write-Host "Name Parameter Mandatory = '$(-not ($Type -eq "NoNameRequired"))'"
}
}
Parameter sets seem to help simulate conditional mandatory parameters.
I can make it to where if either the Type or Name parameter is given, then they are both required. This can happen regardless of other parameters in the function, such as the sibling Number parameter above.
I set the default parameter set name to something random; I usually specify "None". That parameter set name doesn't need to actually exist, again indicated by the Number parameter.
All of this works regardless of your strict mode setting.

Why does passing $null to a parameter with AllowNull() result in an error?

Consider the following code:
function Test
{
[CmdletBinding()]
param
(
[parameter(Mandatory=$true)]
[AllowNull()]
[String]
$ComputerName
)
process{}
}
Test -ComputerName $null
Based on the official documentation for AllowNull I was expecting that $ComputerName could either be [string] or $null. However, running the above code results in the following error:
[14,24: Test] Cannot bind argument to parameter 'ComputerName' because it is an empty
string.
Why doesn't passing $null for $ComputerName work in this case?
$null, when converted to [string], return empty string not $null:
[string]$null -eq $null # False
[string]$null -eq [string]::Empty # True
If you want to pass $null for [string] parameter you should use [NullString]::Value:
[string][NullString]::Value -eq $null # True
Test -ComputerName ([NullString]::Value)
You also need to add the [AllowEmptyString()] attribute if you plan on allowing nulls and empty strings.
function Test
{
[CmdletBinding()]
param
(
[parameter(Mandatory=$true)]
[AllowNull()]
[AllowEmptyString()]
[String]
$ComputerName
)
process{}
}
Test -ComputerName $null
As noted, when cast to a [string], $null becomes an empty string (""). To avoid this, one option is to type the param as an object - then in the body test first for $null, then check that $MyParam.GetType().Name is "String".

DynamicParam not working when prompt for input

I'm trying to get my powershell script to prompt for input, but still use a dynamicparam. If I pass the parameters through the command line (Example: DeployBuild "DEV02" "ClientPortal" "a" "b") , then the below code works fine, and I get prompted for the dynamic param. If I chose not to pass the parameters via command line (Example: DeployBuild), and instead let the script prompt for input, the dynamic parameter quits working. Does anyone have an idea why this is not working?
Function DeployBuild {
[CmdletBinding()]
Param
(
[ValidateSet("DEV01","DEV02","PEDEV01","QA01","QA02","UAT","PERF01","PROD")]
[Parameter(Mandatory=$true, Position=1)]
[String]$environment,
[Parameter(Mandatory=$true, Position=2)]
[ValidateSet("AuthenticationService","ClientPortal")]
[String]$application,
[Parameter(Mandatory=$true, Position=3)]
[String]$buildName,
[Parameter(Mandatory=$true, Position=4)]
[String]$buildNumber
)
DynamicParam{
if ($environment -eq "DEV02"){
#create a new ParameterAttribute Object
$buildVersionAttribute = New-Object System.Management.Automation.ParameterAttribute
$buildVersionAttribute.Position = 5
$buildVersionAttribute.Mandatory = $true
#create an attributecollection object for the attribute just created.
$attributeCollection = new-object System.Collections.ObjectModel.Collection[System.Attribute]
#add our custom attribute
$attributeCollection.Add($buildVersionAttribute)
#add our paramater specifying the attribute collection
$buildVersionParam = New-Object System.Management.Automation.RuntimeDefinedParameter('buildVersion', [double], $attributeCollection)
#expose the name of our parameter
$paramDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
$paramDictionary.Add('buildVersion', $buildVersionParam)
return $paramDictionary
}
}
End{
Write-Host $environment
Write-Host $application
Write-Host $buildName
Write-Host $buildNumber
Write-Host $PSBoundParameters.buildVersion
}
}
Well this is really old so you probably don't need it anymore, but I'm pretty sure the reason this is happening is because of the conditional in the DynamicParam block that looks to see what the value of $environment is.
When the DynamicParam block is evaluated, $environment is blank, so the parameter is never added.
You can demonstrate this for yourself like this:
DynamicParam {
Write-Verbose "DynamicParam here, environment is: '$environment'" -Verbose
# ... rest of code here
}
Now run DeployBuild with no parameters and you'll see exactly when the block is being evaluated.

Identify which parameter is passed when running a script

I have a simple code that accepts two parameters. The parameters are optional. Below is the code.
[CmdletBinding()]
Param(
[Parameter(Mandatory=$False)]
[string]$pA,
[Parameter(Mandatory=$False)]
[string]$pB
)
when running the script I want to know which parameter is passed. pA or pB.
$MyInvocation.BoundParameters
return a ps custom dictionary pair (key/value) with all passed parameter.
this is the content of a.ps1 file:
[CmdletBinding()]
Param(
[Parameter(Mandatory=$False)]
[string]$pA,
[Parameter(Mandatory=$False)]
[string]$pB
)
$MyInvocation.BoundParameters
running this script gives:
PS C:\ps> a -pA pAparam
Key Value
--- -----
pA pAparam
then you can check what key is present:
[bool]($MyInvocation.BoundParameters.Keys -match 'pa') # or -match 'pb' belong your needs
As you cas $Pa and $Pb you can test if they are empty:
You can test with this function :
function func
{
[CmdletBinding()]
Param([Parameter(Mandatory=$False)]
[string]$pA,
[Parameter(Mandatory=$False)]
[string]$pB
)
if ($pA -eq [string]::Empty -and $pA -eq [string]::Empty)
{
Write-Host "Both are empty"
}
elseif ($pA -ne [string]::Empty)
{
Write-Host "Pa is not empty"
}
elseif ($pB -ne [string]::Empty)
{
Write-Host "Pb is not empty"
}
}
Clear-Host
func
Remains the problem that func -Pa "" will give same results as func But if you just want to test the presence of a parameter you can use the switch attribute.
You can find more about PowerShell scripts and function parameters with these links:
about_Functions
about_Functions_Advanced
about_Functions_Advanced_Parameters

How to get Write-Host to output $true

I'm writing a powershell script which uses write-host to output another powershell script. How can I write out a boolean parameter with the value $true (or $false) including the dollar sign:
param
(
[switch] $myParam = $true
)
Write-Host My Param is $myParam
I need this to output exactly
My Param is $True
But it outputs
My Param is True
You can escape a $ with a `:
Write-Host My Param is `$$myParam
This would achieve what I think you want:
Write-Host My Param is `$$myParam