How to differentiate not set parameter from $false, 0, empty string? - powershell

I have function that updates object in WMI. I want user to be able to specify in parameters only values that he wants to update. How can I do it?
function UpdateObject ([bool] $b1, [bool] $b2, [int] $n1, [string] $s1)
{
$myObject = GetObjectFromWmi #(...)
#(...)
#This is bad. As it overrides all the properties.
$myObject.b1 = $b1
$myObject.b2 = $b2
$myObject.n1 = $n1
$myObject.s1 = $s1
#This is what I was thinking but don't kwow how to do
if(IsSet($b1)) { $myObject.b1 = $b1 }
if(IsSet($b2)) { $myObject.b2 = $b2 }
if(IsSet($n1)) { $myObject.n1 = $n1 }
if(IsSet($s1)) { $myObject.s1 = $s1 }
#(...) Store myObject in WMI.
}
I tried passing $null as as parameter but it get's automaticly converted to $false for bool, 0 for int and empty string for string
What are your suggestions?

Check $PSBoundParameters to see if it contains a key with the name of your parameter:
if($PSBoundParameters.ContainsKey('b1')) { $myObject.b1 = $b1 }
if($PSBoundParameters.ContainsKey('b2')) { $myObject.b2 = $b2 }
if($PSBoundParameters.ContainsKey('n1')) { $myObject.n1 = $n1 }
if($PSBoundParameters.ContainsKey('s1')) { $myObject.s1 = $s1 }
$PSBoundParameters acts like a hashtable, where the keys are the parameter names, and the values are the parameters' values, but it only contains bound parameters, which means parameters that are explicitly passed. It does not contain parameters filled in with a default value (except for those passed with $PSDefaultParameterValues).

Building on briantist's answer, if you know that all the parameters exist as properties on the target object you can simply loop through the $PSBoundParameters hashtable and add them one by one:
foreach($ParameterName in $PSBoundParameters.Keys){
$myObject.$ParameterName = $PSBoundParameters[$ParameterName]
}
If only some of the input parameters are to be passed as property values, you can still specify the list just once, with:
$PropertyNames = 'b1','b2','n1','s1'
foreach($ParameterName in $PSBoundParameters.Keys |Where-Object {$PropertyNames -contains $_}){
$myObject.$ParameterName = $PSBoundParameters[$ParameterName]
}

To save yourself having to create a parameter for each property you may want to change, consider using a hashtable or other object to pass this information to your function.
For example:
function UpdateObject ([hashtable]$properties){
$myObject = GetObjectFromWmi
foreach($property in $properties.Keys){
# without checking
$myObject.$property = $properties.$property
# with checking (assuming members of the wmiobject have MemberType Property.
if($property -in (($myObject | Get-Member | Where-Object {$_.MemberType -eq "Property"}).Name)){
Write-Output "Updating $property to $($properties.$property)"
$myObject.$property = $properties.$property
}else{
Write-Output "Property $property not recognised"
}
}
}
UpdateObject -properties {"b1" = $true; "b2" = $false}

If you want a [Boolean] parameter that you want the user to specify explicitly or omit (rather than a [Switch] parameter which can be present or not), you can use [Nullable[Boolean]]. Example:
function Test-Boolean {
param(
[Nullable[Boolean]] $Test
)
if ( $Test -ne $null ) {
if ( $Test ) {
"You specified -Test `$true"
}
else {
"You specified -Test `$false"
}
}
else {
"You did not specify -Test"
}
}
In this sample function the $Test variable will be $null (user did not specify the parameter), $true (user specified -Test $true), or $false (user specified -Test $false). If user specifies -Test without a parameter argument, PowerShell will throw an error.
In other words: This gives you a tri-state [Boolean] parameter (missing, explicitly true, or explicitly false). [Switch] only gives you two states (present or explicitly true, and absent or explicitly false).

Related

How to check if a PowerShell switch parameter is absent or false

I am building a PowerShell function that builds a hash table. I am looking for a way I can use a switch parameter to either be specified as absent, true or false. How can I determine this?
I can resolve this by using a [boolean] parameter, but I didn't find this an elegant solution. Alternatively I could also use two switch parameters.
function Invoke-API {
param(
[switch]$AddHash
)
$requestparams = #{'header'='yes'}
if ($AddHash) {
$requestparams.Code = $true
}
How would I get it to display false when false is specified and nothing when the switch parameter isn't specified?
To check whether a parameter was either passed in by the caller or not, inspect the $PSBoundParameters automatic variable:
if($PSBoundParameters.ContainsKey('AddHash')) {
# switch parameter was explicitly passed by the caller
# grab its value
$requestparams.Code = $AddHash.IsPresent
}
else {
# parameter was absent from the invocation, don't add it to the request
}
If you have multiple switch parameters that you want to pass through, iterate over the entries in $PSBoundParameters and test the type of each value:
param(
[switch]$AddHash,
[switch]$AddOtherStuff,
[switch]$Yolo
)
$requestParams = #{ header = 'value' }
$PSBoundParameters.GetEnumerator() |ForEach-Object {
$value = $_.Value
if($value -is [switch]){
$value = $value.IsPresent
}
$requestParams[$_.Key] = $value
}
You can use PSBoundParameter to check
PS C:\ > function test-switch {
param (
[switch]$there = $true
)
if ($PSBoundParameters.ContainsKey('there')) {
if ($there) {
'was passed in'
} else {
'set to false'
}
} else {
'Not passed in'
}
}
If you have a parameter that can be $true, $false or unspecified, then you might not want the [Switch] parameter type because it can only be $true or $false ($false is the same as unspecified). As an alternative, you can use a nullable boolean parameter. Example:
function Test-Boolean {
param(
[Nullable[Boolean]] $Test
)
if ( $Test -ne $null ) {
if ( $Test ) {
"You specified -Test `$true"
}
else {
"You specified -Test `$false"
}
}
else {
"You did not specify -Test"
}
}
even simpler:
function test{
[CmdletBinding()]
param(
[Parameter(Mandatory=$False,Position=1)][switch]$set
)
write-host "commence normal operation"
if(-not $set){"switch is not set, i execute this"}
else{"switch is set"}
}
output
enter image description here

Check if a string is not NULL or EMPTY

In below code, I need to check if version string is not empty then append its value to the request variable.
if ([string]::IsNullOrEmpty($version))
{
$request += "/" + $version
}
How to check not in if condition?
if (-not ([string]::IsNullOrEmpty($version)))
{
$request += "/" + $version
}
You can also use ! as an alternative to -not.
You don't necessarily have to use the [string]:: prefix. This works in the same way:
if ($version)
{
$request += "/" + $version
}
A variable that is null or empty string evaluates to false.
As in many other programming and scripting languages you can do so by adding ! in front of the condition
if (![string]::IsNullOrEmpty($version))
{
$request += "/" + $version
}
If the variable is a parameter then you could use advanced function parameter binding like below to validate not null or empty:
[CmdletBinding()]
Param (
[parameter(mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string]$Version
)
if (!$variablename)
{
Write-Host "variable is null"
}
I hope this simple answer will resolve the question.
Source
I would define $Version as a string to start with
[string]$Version
and if it's a param you can use the code posted by Samselvaprabu
or if you would rather not present your users with an error you can do something like
while (-not($version)){
$version = Read-Host "Enter the version ya fool!"
}
$request += "/" + $version
You can use the [string]::IsNullOrEmpty($version) method if it is a string.
But, I was looking for a universal way to check nulls (regardless of data type)
in Powershell. Checking for null (or not null) values in
PowerShell is tricky. Using ($value -eq $null) or ($value -ne
$null) does not always work. Neither does if($value). Using them
can even cause problems later on.
Just read this Microsoft article below (IN IT'S ENTIRETY) to get a grasp
of how tricky nulls can be in Powershell.
[https://learn.microsoft.com/en-us/powershell/scripting/learn/deep-dives/everything-about-null?view=powershell-7.1][1]
I wrote these two functions below for checking for null (or not null) values
in PowerShell. I "believe" they should work for any and all values
and data types.
I hope someone finds them helpful.
I am not sure why MS hasn't put something like this into PowerShell natively to
make handling nulls easier (and less dangerous) in PowerShell.
I hope this helps someone.
If anyone knows of an unseen "pitfall" or problem with this method,
please post a comment here so we can know that.
Thanks!
<#
*********************
FUNCTION: ValueIsNull
*********************
Use this function ValueIsNull below for checking for null values
rather using -eq $null or if($value) methods. Those may not work as expected.
See reference below for more details on $null values in PowerShell.
[https://learn.microsoft.com/en-us/powershell/scripting/learn/deep-dives/everything-about-null?view=powershell-7.1][1]
An if statement with a call to ValueIsNull can be written like this:
if (ValueIsNull($TheValue))
#>
function ValueIsNull {
param($valueToCheck)
# In Powershell when a parameter of a function does not have a data type defined,
# it will create the parameter as a PSObject. It will do this for
# an object, an array, and a base date type (int, string, DateTime, etc.)
# However if the value passed in is $null, then it will still be $null.
# So, using a function to check null gives us the ability to determine if the parameter
# is null or not by checking if the parameter is a PSObject or not.
# This function could be written more efficiently, but intentionally
# putting it in a more readable format.
# Special Note: This cannot tell the difference between a parameter
# that is a true $null and an undeclared variable passed in as the parameter.
# ie - If you type the variable name wrong and pass that in to this function it will see it as a null.
[bool]$returnValue = $True
[bool]$isItAnObject=$True
[string]$ObjectToString = ""
try { $ObjectToString = $valueToCheck.PSObject.ToString() } catch { $isItAnObject = $false }
if ($isItAnObject)
{
$returnValue=$False
}
return $returnValue
}
<#
************************
FUNCTION: ValueIsNotNull
************************
Use this function ValueIsNotNull below for checking values for
being "not-null" rather than using -ne $null or if($value) methods.
Both may not work as expected.
See notes on ValueIsNull function above for more info.
ValueIsNotNull just calls the ValueIsNull function and then reverses
the boolean result. However, having ValueIsNotNull available allows
you to avoid having to use -eq and\or -ne against ValueIsNull results.
You can disregard this function and just use !ValueIsNull($value).
But, it is my preference to have both for easier readability of code.
An if statement with a call to ValueIsNotNull can be written like this:
if (ValueIsNotNull($TheValue))
#>
function ValueIsNotNull {
param($valueToCheck)
[bool]$returnValue = !(ValueIsNull($valueToCheck))
return $returnValue
}
You can use the following list of calls to ValueIsNull to test it out.
$psObject = New-Object PSObject
Add-Member -InputObject $psObject -MemberType NoteProperty -Name customproperty -Value "TestObject"
$valIsNull = ValueIsNull($psObject)
$props = #{
Property1 = 'one'
Property2 = 'two'
Property3 = 'three'
}
$otherPSobject = new-object psobject -Property $props
$valIsNull = ValueIsNull($otherPSobject)
# Now null the object
$otherPSobject = $null
$valIsNull = ValueIsNull($otherPSobject)
# Now an explicit null
$testNullValue = $null
$valIsNull = ValueIsNull($testNullValue)
# Now a variable that is not defined (maybe a type error in variable name)
# This will return a true because the function can't tell the difference
# between a null and an undeclared variable.
$valIsNull = ValueIsNull($valueNotDefine)
[int32]$intValueTyped = 25
$valIsNull = ValueIsNull($intValueTyped)
$intValueLoose = 67
$valIsNull = ValueIsNull($intValueLoose)
$arrayOfIntLooseType = 4,2,6,9,1
$valIsNull = ValueIsNull($arrayOfIntLooseType)
[int32[]]$arrayOfIntStrongType = 1500,2230,3350,4000
$valIsNull = ValueIsNull($arrayOfIntStrongType)
#Now take the same int array variable and null it.
$arrayOfIntStrongType = $null
$valIsNull = ValueIsNull($arrayOfIntStrongType)
$stringValueLoose = "String Loose Type"
$valIsNull = ValueIsNull($stringValueLoose)
[string]$stringValueStrong = "String Strong Type"
$valIsNull = ValueIsNull($stringValueStrong)
$dateTimeArrayLooseValue = #("1/1/2017", "2/1/2017", "3/1/2017").ForEach([datetime])
$valIsNull = ValueIsNull($dateTimeArrayLooseValue)
# Note that this has a $null in the array values. Still returns false correctly.
$stringArrayLooseWithNull = #("String1", "String2", $null, "String3")
$valIsNull = ValueIsNull($stringArrayLooseWithNull)

Boolean NoteProperty becomes an Array

The title says it all single boolean value becomes an array when assigned to a NoteProperty using Add-Member or using splatting.
PSVersion: 5.0.1xx
I have what I consider a strange problem. I am creating a PSObject with one of the NoteProperty members as a boolean. The function loops through a list, calls a function to perform an evaluation, creates an object and then adds it to an array. This seems to only happen to the first object created but I have not tested this with 5 or more objects being created.
I have validated that the functions are actually returning bool and that the variable being assigned to the property is an bool.
My workaround seems solid but am curious as to why this is happening.
Here's part of the code:
$clientCertsRequired = Get-Is-Client-Cert-Required -configFile $configFile -siteName $siteName
$httpsStatus = "Https is not enabled"
$clientCertStatus = "Client certs are not required"
if ($httpsEnabled -eq $true) {
$httpsStatus = "Https is enabled"
}
if ($clientCertsRequired -eq $true){
$clientCertStatus = "Client certs are required"
}
$sc = New-Object PSObject -Property #{
SiteName = $siteName;
ConfigFilePath = $path;
HttpsEnabled = $httpsStatus;
ClientCertStatus =$clientCertStatus;
ClientCertRequired = $clientCertsRequired;
}
# clean up of some inexplicable problem where assignment to property
# produces array with actual value in the last element.
if ($sc.ClientCertRequired.GetType().Name -eq "Object[]"){
$sc.ClientCertRequired = $sc.ClientCertRequired[-1]
}
$si += $sc
Function Get-Is-Client-Cert-Required{
param(
[xml]$configFile,
[string]$siteName
)
$functionName = $MyInvocation.MyCommand.Name
$clientCertRequired = $false
try{
# then read locations section (this will often not have any pages
$locationPath = "//configuration/location[#path='$siteName']"
[system.xml.xmlelement]$location = $configFile.DocumentElement.SelectSingleNode($locationPath)
if($location -ne $null){
[system.xml.xmlelement]$accessNode = $location.SelectSingleNode("system.webServer/security/access")
[system.xml.xmlelement]$authenticationNode = $location.SelectSingleNode("system.webServer/security/authentication")
[system.xml.xmlelement]$clientCertMappingNode
[system.xml.xmlelement]$iisClientCertMappingNode
[int]$sslFlagMask = 0
if($accessNode -ne $null){
$sslFlags = $accessNode.Attributes.GetNamedItem("sslFlags")
# $sslFlags = $accessNode.Attributes["sslFlags"].Value
if($sslFlagMask -ne $null){
$sslFlagMask = Convert-Ssl-Flag-String-To-Int-Flag -sslFlag $sslFlags.Value
}
}
if($authenticationNode -ne $null){
[system.xml.xmlelement]$clientCertMappingNode = $authenticationNode.SelectSingleNode("clientCertificateMappingAuthentication[#enabled='true']")
[system.xml.xmlelement]$iisClientCertMappingNode = $authenticationNode.SelectSingleNode("iisClientCertificateMappingAuthentication[#enabled='true']")
}
$clientCertAccepted = ($sslFlagMask -band $certAccepted) -eq $certAccepted
$clientCertRequired = Check-IIS-Express-SSL-Config $sslFlagMask
if($clientCertRequired -eq $false){
if($clientCertAccepted -and ($clientCertMappingNode -ne $null -or $iisClientCertMappingNode -ne $null)){
$clientCertRequired = $true
}
}
}
}catch{
$exceptionMessage = Get-Formatted-Exception-String -exceptionObject $_
$message = "$functionName - Exception`: $exceptionMessage"
Add-Exception -exception $message
Log-Error -message $message
}
$clientCertRequired
}
In the body of the Get-Is-Client-Cert-Required function, you do:
[system.xml.xmlelement]$clientCertMappingNode
[system.xml.xmlelement]$iisClientCertMappingNode
This pattern:
[type]$nonExistingVariable
Is a terrible idea in PowerShell - unlike C#, PowerShell does not have the concept of bare variable declarations, and the above pattern simply casts $null to the specified type, emitting a new instance of said type if it succeeds - this is likely what causes the function to output an array.
If you really need to bind a variable to a specific type, cast on assignment:
[type]$Variable = Get-Stuff
Bonus tip: The PowerShell-idiomatic naming convention for functions and cmdlets is Noun-Verb, with only a single hyphen. A more appropriate name for the function would be:
Test-ClientCertRequirement

Add Custom Argument Completer for Cmdlet?

How do I add dynamic argument tab completion to a PowerShell Cmdlet?
When I type this and hit tab, I'd like for it to do tab completion.
PM> Paket-Add -NuGet FSharp.Co
These are the values I'd like to use in this example:
PM> Paket-FindPackages -SearchText FSharp.Co
FSharp.Core
FSharp.Core.3
FSharp.Configuration
FSharp.Core.Fluent-3.1
FSharp.Core.Fluent-4.0
FSharp.Compiler.Tools
FSharp.Compatibility.Scala
FSharp.Compatibility.OCaml
FSharp.Compiler.CodeDom
FSharp.Compiler.Service
FSharp.Control.Reactive
FSharp.Compatibility.Haskell
FSharp.Compatibility.OCaml.Numerics
FSharp.Compatibility.OCaml.Format
FSharp.Compatibility.OCaml.System
FSharp.Collections.ParallelSeq
FSharp.Compatibility.StandardML
FSharp.Compatibility.OCaml.LexYacc
FSharp.Control.AsyncSeq
I found this answer that gave a couple of helpful links and said I should run Get-Content function:TabExpansion2:
It looks like CommandCompletion.CompleteInput needs to implemented. I thought I read somewhere that there is a Hashtable of commands to functions. If so, where is it and how do I install custom ones? I'm using Chocolatey to distribute Paket.PowerShell. Here is the Cmdlet code.
UPDATE 2015-06-20:
I ended up getting it to work with the code here:
https://github.com/fsprojects/Paket/blob/76de1c44853ce09029ba157855525f435d951b85/src/Paket.PowerShell/ArgumentTabCompletion.ps1
# https://github.com/mariuszwojcik/RabbitMQTools/blob/master/TabExpansions.ps1
function createCompletionResult([string]$text, [string]$value, [string]$tooltip) {
if ([string]::IsNullOrEmpty($value)) { return }
if ([string]::IsNullOrEmpty($text)) { $text = $value }
if ([string]::IsNullOrEmpty($tooltip)) { $tooltip = $value }
$completionText = #{$true="'$value'"; $false=$value }[$value -match "\W"]
$completionText = $completionText -replace '\[', '``[' -replace '\]', '``]'
New-Object System.Management.Automation.CompletionResult $completionText, $text, 'ParameterValue', $tooltip | write
}
$findPackages = {
param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter)
Paket-FindPackages -SearchText $wordToComplete -Max 100 | % {
createCompletionResult $_ $_ $_ | write
}
}
$findPackageVersions = {
param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter)
if (-not $fakeBoundParameter.NuGet){ return }
Paket-FindPackageVersions -Name $fakeBoundParameter.NuGet -Max 100 | % {
createCompletionResult $_ $_ $_ | write
}
}
# create and add $global:options to the list of completers
# http://www.powertheshell.com/dynamicargumentcompletion/
if (-not $global:options) { $global:options = #{CustomArgumentCompleters = #{};NativeArgumentCompleters = #{}}}
$global:options['CustomArgumentCompleters']['Paket-Add:NuGet'] = $findPackages
$global:options['CustomArgumentCompleters']['Paket-Add:Version'] = $findPackageVersions
$function:tabexpansion2 = $function:tabexpansion2 -replace 'End\r\n{','End { if ($null -ne $options) { $options += $global:options} else {$options = $global:options}'
The completer param names are important. Renaming them will make it not work.
You may want to look at the TabExpansion++ module, which was designed to make extending tab completion easier.
I just played with it for few minutes, and I think you want something like this based on the example:
Import-Module TabExpansion++
function PaketAddNugetCompletion
{
[ArgumentCompleter(Parameter = 'Nuget', Command = 'Paket-Add')]
param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter)
Paket-FindPackages -SearchText $wordToComplete |
ForEach-Object {
# not quite sure what property to use off the result, but this might work.
New-CompletionResult -CompletionText $_
}
}
These are called Dynamic parameters and are described in about_Functions_Advanced_Parameters.
The following example shows a sample function with standard
parameters named Name and Path, and an optional dynamic parameter
named DP1.The DP1 parameter is in the PSet1 parameter set and has a
type of Int32. The DP1 parameter is available in the Sample function
only when the value of the Path parameter contains "HKLM:", indicating
that it is being used in the HKEY_LOCAL_MACHINE registry drive.
function Get-Sample {
[CmdletBinding()]
Param ([String]$Name, [String]$Path)
DynamicParam
{
if ($path -match ".*HKLM.*:")
{
$attributes = new-object System.Management.Automation.ParameterAttribute
$attributes.ParameterSetName = "__AllParameterSets"
$attributes.Mandatory = $false
$attributeCollection = new-object -Type System.Collections.ObjectModel.Collection[System.Attribute]
$attributeCollection.Add($attributes)
$dynParam1 = new-object -Type System.Management.Automation.RuntimeDefinedParameter("dp1", [Int32], $attributeCollection)
$paramDictionary = new-object -Type System.Management.Automation.RuntimeDefinedParameterDictionary
$paramDictionary.Add("dp1", $dynParam1)
return $paramDictionary
}
}
}
Here's another example that does validation sets dynamically.
I've re-read your question, and it looks like maybe you just want a static, pre-defined list of tab-completed values for a specific parameter. If that's the case, then you can simply use the [ValidateSet()] attribute:
function Get-Something {
[CmdletBinding()]
param(
[ValidateSet('One','Two','Three')]
[String]
$MyParam
)
}
But if the values need to be determined at runtime, then see the above section on dynamic parameters instead.

Pass an unspecified set of parameters into a function and thru to a cmdlet

Let's say I want to write a helper function that wraps Read-Host. This function will enhance Read-Host by changing the prompt color, calling Read-Host, then changing the color back (simple example for illustrative purposes - not actually trying to solve for this).
Since this is a wrapper around Read-Host, I don't want to repeat the all of the parameters of Read-Host (i.e. Prompt and AsSecureString) in the function header. Is there a way for a function to take an unspecified set of parameters and then pass those parameters directly into a cmdlet call within the function? I'm not sure if Powershell has such a facility.
for example...
function MyFunc( [string] $MyFuncParam1, [int] $MyFuncParam2 , Some Thing Here For Cmdlet Params that I want to pass to Cmdlet )
{
# ...Do some work...
Read-Host Passthru Parameters Here
# ...Do some work...
}
It sounds like you're interested in the 'ValueFromRemainingArguments' parameter attribute. To use it, you'll need to create an advanced function. See the about_Functions_Advanced and about_Functions_Advanced_Parameters help topics for more info.
When you use that attribute, any extra unbound parameters will be assigned to that parameter. I don't think they're usable as-is, though, so I made a little function that will parse them (see below). After parsing them, two variables are returned: one for any unnamed, positional parameters, and one for named parameters. Those two variables can then be splatted to the command you want to run. Here's the helper function that can parse the parameters:
function ParseExtraParameters {
[CmdletBinding()]
param(
[Parameter(ValueFromRemainingArguments=$true)]
$ExtraParameters
)
$ParamHashTable = #{}
$UnnamedParams = #()
$CurrentParamName = $null
$ExtraParameters | ForEach-Object -Process {
if ($_ -match "^-") {
# Parameter names start with '-'
if ($CurrentParamName) {
# Have a param name w/o a value; assume it's a switch
# If a value had been found, $CurrentParamName would have
# been nulled out again
$ParamHashTable.$CurrentParamName = $true
}
$CurrentParamName = $_ -replace "^-|:$"
}
else {
# Parameter value
if ($CurrentParamName) {
$ParamHashTable.$CurrentParamName += $_
$CurrentParamName = $null
}
else {
$UnnamedParams += $_
}
}
} -End {
if ($CurrentParamName) {
$ParamHashTable.$CurrentParamName = $true
}
}
,$UnnamedParams
$ParamHashTable
}
You could use it like this:
PS C:\> ParseExtraParameters -NamedParam1 1,2,3 -switchparam -switchparam2:$false UnnamedParam1
UnnamedParam1
Name Value
---- -----
switchparam True
switchparam2 False
NamedParam1 {1, 2, 3}
Here are two functions that can use the helper function (one is your example):
function MyFunc {
[CmdletBinding()]
param(
[string] $MyFuncParam1,
[int] $MyFuncParam2,
[Parameter(Position=0, ValueFromRemainingArguments=$true)]
$ExtraParameters
)
# ...Do some work...
$UnnamedParams, $NamedParams = ParseExtraParameters #ExtraParameters
Read-Host #UnnamedParams #NamedParams
# ...Do some work...
}
function Invoke-Something {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true, Position=0)]
[string] $CommandName,
[Parameter(ValueFromRemainingArguments=$true)]
$ExtraParameters
)
$UnnamedParameters, $NamedParameters = ParseExtraParameters #ExtraParameters
&$CommandName #UnnamedParameters #NamedParameters
}
After importing all three functions, try these commands:
MyFunc -MyFuncParam1 Param1Here "PromptText" -assecure
Invoke-Something -CommandName Write-Host -Fore Green "Some text" -Back Red
One word: splatting.
Few more words: you can use combination of $PSBoundParameters and splatting to pass parameters from external command, to internal command (assuming names match). You would need to remove any parameter that you don't want to use though from $PSBoundParameters first:
$PSBoundParameters.Remove('MyFuncParam1')
$PSBoundParameters.Remove('MyFuncParam2')
Read-Host #PSBoundParameters
EDIT
Sample function body:
function Read-Data {
param (
[string]$First,
[string]$Second,
[string]$Prompt,
[switch]$AsSecureString
)
$PSBoundParameters.Remove('First') | Out-Null
$PSBoundParameters.Remove('Second') | Out-Null
$Result = Read-Host #PSBoundParameters
"First: $First Second: $Second Result: $Result"
}
Read-Data -First Test -Prompt This-is-my-prompt-for-read-host