I am trying to find the way to create values for parameters that can be auto completed or show all available options with tab, like the value "allsigned" in the cmdlet
Set-ExecutionPolicy -ExecutionPolicy AllSigned
¿Any ideas about how this is called in programation or how can I achieve this ?
yes its called validateset
Param
(
[parameter(Mandatory=$true)]
[ValidateSet("Low", "Average", "High")]
[String[]]
$Detail
)
advanced functions/cmdlets or just parameter validation, parameter sets, etc
snippet from the ISE:
function Verb-Noun
{
[CmdletBinding(DefaultParameterSetName='Parameter Set 1',
SupportsShouldProcess=$true,
PositionalBinding=$false,
HelpUri = 'http://www.microsoft.com/',
ConfirmImpact='Medium')]
[Alias()]
[OutputType([String])]
Param
(
# Param1 help description
[Parameter(Mandatory=$true,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true,
ValueFromRemainingArguments=$false,
Position=0,
ParameterSetName='Parameter Set 1')]
[ValidateNotNull()]
[ValidateNotNullOrEmpty()]
[ValidateCount(0,5)]
[ValidateSet("sun", "moon", "earth")]
[Alias("p1")]
$Param1,
# Param2 help description
[Parameter(ParameterSetName='Parameter Set 1')]
[AllowNull()]
[AllowEmptyCollection()]
[AllowEmptyString()]
[ValidateScript({$true})]
[ValidateRange(0,5)]
[int]
$Param2,
# Param3 help description
[Parameter(ParameterSetName='Another Parameter Set')]
[ValidatePattern("[a-z]*")]
[ValidateLength(0,15)]
[String]
$Param3
)
Begin
{
}
Process
{
if ($pscmdlet.ShouldProcess("Target", "Operation"))
{
}
}
End
{
}
}
Related
I am implementing a function in Powershell which will perform REST calls. One of the parameters may differ in contents, depending on given scenarios. For instance, the body of the REST call may be a string or a hash table. How do you implement this within the CmdletBinding() declaration?
For instance
Function doRESTcall(){
[CmdletBinding()]
param (
[Parameter(Mandatory=$true)]
[Hashtable]$headers
[Parameter(Mandatory=$true)]
[???????]$body # what type here??
)
.
.
.
}
To declare parameters where any type is allowed you can either not type-constrain the parameter at all or use type constraint [object] (System.Object), by doing so, no type conversion will be needed, since all objects in PowerShell inherit from this type.
It's worth mentioning that unconstrained parameters will allow $null as argument, to avoid this, [ValidateNotNull()] and / or [parameter(Mandatory)] can be used.
function Test-Type {
param(
[parameter(ValueFromPipeline, Mandatory)]
[object]$Value
)
process
{
[pscustomobject]#{
Type = $Value.GetType().FullName
IsObject = $Value -is [object]
}
}
}
PS /> 1, 'foo', (Get-Date) | Test-Type
Type IsObject
---- --------
System.Int32 True
System.String True
System.DateTime True
The correct way to tackel this is to create a ParameterSet:
Function doRESTcall(){
[CmdletBinding()]
param (
[Parameter(Mandatory=$true, ParameterSetName = 'StringBody', Position = 0)]
[Parameter(Mandatory=$true, ParameterSetName = 'HashBody', Position = 0)]
[Hashtable]$headers,
[Parameter(Mandatory=$true, ParameterSetName = 'StringBody', Position = 1)]
[String]$Stringbody,
[Parameter(Mandatory=$true, ParameterSetName = 'HashBody', Position = 1)]
[Hashtable]$Hashbody
)
Write-Host 'Parameter set:' $PSCmdlet.ParameterSetName
Write-Host 'StringBody:' $StringBody
Write-Host 'HashBody:' $HashBody
}
doRESTcall -?
NAME
doRESTcall
SYNTAX
doRESTcall [-headers] <hashtable> [-Hashbody] <hashtable> [<CommonParameters>]
doRESTcall [-headers] <hashtable> [-Stringbody] <string> [<CommonParameters>]
ALIASES
None
REMARKS
None
doRESTcall #{a = 1} 'Test'
Parameter set: StringBody
StringBody: Test
HashBody:
Note: to accept a larger variety of dictionaries (like [Ordered]), I would use a [System.Collections.Specialized.OrderedDictionary] (rather than [Hashtable]) type for the concerned parameters.
I am curious if it's possible to pass arguments to a powershell module using object notation. Look at the following definition of a function:
function Get-Something
{
Param
(
[Parameter(Mandatory=$true, Position=0)]
[string] $Name,
[Parameter(Mandatory=$true, Position=1)]
[int] $Id
)
}
can I call this function in the following notation:
Get-Something(Name "Vance Refrigeration", Id "9")
instead of doing:
Get-Something -Name "Vance Refrigeration" -Id 9
As far as I know, the only other way than to type every variable with '-Name "Vance Refrigeration" -Id 9' is to do the following:
function Get-Something
{
Param
(
[Parameter(Mandatory=$true, Position=0)]
[string] $Name,
[Parameter(Mandatory=$true, Position=1)]
[int] $Id
)
Write-Host $name en $id
}
$object = #{Name = "Vance Refrigeration"; Id = "9"}
Get-Something #object
Or with the prettier styling for the object:
$object = #{
Name = "Vance Refrigeration"
Id = "9"
}
Get-Something #object
# cmdlet MyCmdlet.ps1
Param(
[Parameter(Mandatory=$true)]
[string]$Variable1,
[string]$Variable2
)
Begin {
Function Function-Name {
Param (
[Parameter(Mandatory=$true)]
[string]$Variable1,
[Parameter(Mandatory=$false)]
[string]$Variable2,
[Parameter(Mandatory=$false)]
[ValidateScript({[string]::IsNullOrWhiteSpace($Variable2)})]
[switch]$Choice
)
# function-body
}
}
Process {
Function-name -Variable1 "SomeString" -Choice
}
This cmdlet was called like below:
.\MyCmdlet.ps1 -variable1 "string1" -variable2 "string2"
It returns:
Cannot validate argument on parameter 'choice'. The "[string]::IsNullOrWhiteSpace($Variable2) " validation script for the argument with value "True" did not return a result of True.
It seems like the value of -Variable2 of cmdlet was implicitly passed to the function because of same variable name even without specically mentioning it during the function call inside the cmdlet.
Note: I need the variables to have same name so I can see their similar function in the future. And I use Begin, Process, End in cmdlet just so I can convert it into function and put in inside other scripts.
How can I deal with this?
By the time the ValidationScript runs, the local variable $Variable2 has not yet been created, so you get the value from $Variable2 in the parent scope.
Use $PSBoundParameters instead:
Function Function-Name {
Param (
[Parameter(Mandatory=$true)]
[string]$Variable1,
[Parameter(Mandatory=$false)]
[string]$Variable2,
[Parameter(Mandatory=$false)]
[ValidateScript({[string]::IsNullOrWhiteSpace($PSBoundParameters['Variable2'])})]
[switch]$Choice
)
}
If your intention is to create a script that could be both invoked directly (as a script) and dot-sourced (so you can call the function in it from other PowerShell scripts), you could do something like this:
# Invoke-Something.ps1
[CmdletBinding()]
Param(
[Parameter(Mandatory=$false)]
[string]$Variable1,
[Parameter(Mandatory=$false, ParameterSetName='bar')]
[string]$Variable2,
[Parameter(Mandatory=$true, ParameterSetName='foo')]
[switch]$Choice
)
function Invoke-Something {
[CmdletBinding()]
Param (
[Parameter(Mandatory=$true)]
[string]$Variable1,
[Parameter(Mandatory=$false, ParameterSetName='bar')]
[string]$Variable2,
[Parameter(Mandatory=$true, ParameterSetName='foo')]
[switch]$Choice
)
Write-Host 'invocation: function'
}
if ($MyInvocation.Line.Split()[0] -ne '.') {
Write-Host 'invocation: directly'
Invoke-Something #PSBoundParameters
} else {
Write-Host 'invocation: dot-source'
}
Using the above approach you can invoke the script directly:
PS> Invoke-Something.ps1 -Variable1 'some' -Variable2 'other'
invocation: directly
invocation: function
or dot-source it and invoke the function:
PS> . Invoke-Something.ps1
invocation: dot-source
PS> Invoke-Something -Variable1 'some' -Variable2 'other'
invocation: function
For this to work you must make all parameters of the script optional, though.
A simplified version of this would not define parameters on the script level and pass #args to the invoked function:
# Invoke-Something.ps1
function Invoke-Something {
[CmdletBinding()]
Param (
[Parameter(Mandatory=$true)]
[string]$Variable1,
[Parameter(Mandatory=$false, ParameterSetName='bar')]
[string]$Variable2,
[Parameter(Mandatory=$true, ParameterSetName='foo')]
[switch]$Choice
)
Write-Host 'invocation: function'
}
if ($MyInvocation.Line.Split()[0] -ne '.') {
Write-Host 'invocation: directly'
Invoke-Something #args
} else {
Write-Host 'invocation: dot-source'
}
So I have this script that creates a snap mirror on our dr server based on its location. Below is just a small part of a the script. I need to write an if statement so if location='uk' then to not run the below function otherwise if location='us' then create snap-mirror.
function Create-SnapMirror {
[CmdletBinding(PositionalBinding=$false,
HelpUri='http://www.microsoft.com/',
ConfirmImpact='Medium')]
[OutputType([Boolean])]
Param(
[Parameter(Mandatory=$true,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true,
ValueFromRemainingArguments=$false,
Position=0)]
[ValidateNotNull()]
[ValidateNotNullOrEmpty()]
[String]$SourcePath,
[Parameter(Mandatory=$true,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true,
ValueFromRemainingArguments=$false,
Position=1)]
[ValidateNotNull()]
[ValidateNotNullOrEmpty()]
[String]$DestinationPath,
[Parameter(Mandatory=$true,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true,
ValueFromRemainingArguments=$false,
Position=2)]
[ValidateNotNull()]
[ValidateNotNullOrEmpty()]
[String]$LogName
)
$success = $null
$error.Clear()
}
Assuming that the logic is required outside of the function you can achieve this by simply encapsulating the function call within the if statement as follows:
if($Location -eq 'us') { Create-SnapMirror -SourcePath $MySourcePath -DestinationPath $MyDestinationPath -LogName $MyLogName }
If however you want to check the location within the function you will first need to receive the location either from the input parameters or some other method. Assuming that you have the location in a variable named $Location you can simply add the following within your function before any other action:
if($Location -ne 'us') { return }
This will exit the function; you can add other actions, for instance logging within the parenthesis.
How can I make PowerShell script to accept 0 or 3 params? All or nothing.
[CmdletBinding()]
Param(
# Name of VPN connection
[Parameter(Mandatory=$False,ValueFromPipeline=$True,Position=1)]
[string]$vpn_name,
# Server URL or IP
[Parameter(Mandatory=$False,ValueFromPipeline=$True,Position=2)]
[string]$vpn_server,
# 192.168.72.0/24
[Parameter(Mandatory=$False,ValueFromPipeline=$True,Position=3)]
[string[]]$target_nets
)
try this
Param(
[Parameter(Mandatory=$True, Position=0, ParameterSet="FirstSet")]
[string]FirstRequiredParam,
[Parameter(Mandatory=$False, Position=0, ParameterSet="SecondSet")]
[string]FirstNotRequiredParam
)
Here is simplified snippet
[CmdletBinding()]
Param(
# Name of VPN connection
[Parameter(Mandatory=$True, ParameterSetName="WithParams")]
[string]$name,
# Server URL or IP
[Parameter(Mandatory=$True, ParameterSetName="WithParams"))]
[string]$vpn_server,
# No params
[Parameter(Mandatory=$False,ValueFromPipeline=$True,Position=0, ParameterSetName="NoParams")]
[string]$none
)
# Then check what parameters set was passed
switch ($PsCmdlet.ParameterSetName){
"WithParams" { Write-Host $name; break}
"NoParams" { Write-Host "No params passed."; break }
}