PowerShell advanced functions provide some powerful magic. In the previous article we covered some “features” that can help us to write better code, and now we are going to focus in on the magic of those features that are to do with the parameters that we pass to advanced functions.
We already know that, when we put the [CmdletBinding()]
attribute in a function, it changes it into an advanced function, thereby allowing options that you don’t have in normal functions – see The PoSh DBA: Grown-Up PowerShell Functions. What I didn’t emphasize then is that all magic comes at a price. In this case, the price is that you must write code that uses the magic correctly; you must be more precise and cautious, just like any magician. To start with, the function’s parameters should be formally defined. We lose something by this: When we write a function without the [CmdletBinding()]
attribute, we can be less strict, and pass more parameters than we have defined in the function, because the $args
variable stores them.
1 2 3 4 5 6 7 |
function ILoveInformalParameters { param ($IamFirst,$IamSecond) Write-Host "I am The First Parameter : $($IamFirst)" Write-Host "I am The Second Parameter : $($IamSecond)" Write-Host "And I am The Rest : $($args)" } |
If we now call our informal function :
1 2 3 4 5 6 |
ILoveInformalParameters ObiWanKenoby Yoda DarthVader DarthSidious I am The First Parameter : ObiWanKenoby I am The Second Parameter : Yoda And I am The Rest : DarthVader DarthSidious |
But if, without due thought, we add just the [CmdletBinding()]
attribute to this without any option…
1 2 3 4 5 |
function ILoveInformalParameters { [cmdletbinding()] param ($IamFirst,$IamSecond) ....... } |
…And try to run it using more parameters than we’ve declared…
1 2 3 |
ILoveInformalParameters ObiWanKenoby Yoda DarthVader DarthSidious ILoveInformalParameters : A positional parameter cannot be found that accepts argument 'DarthVader'....... |
Yes. It is an error in the positional parameters. You’ve just dropped the rabbit from the hat.
The CmdletBinding arguments
There are 3 arguments that are concerned with the parameters that can be passed to PowerShell functions. These are :
Supports
ShouldProcess
- By setting this property to
$True
(the default is$False
), you are asking the function to be able to support theShouldProcess
method. By doing this, you allow the function to implement theWhatif
common parameter that displays what the function would do, but without actually performing the operation . TheConfirm
option is also enabled by this property.
- By setting this property to
ConfirmImpact
- In this argument, you will configure the impact level (“HIGH”, “MEDIUM” or “LOW”) at which the action that you are performing in your function should be preceded by a prompted confirmation request such as “Are you sure about this?”. This prompt would only be displayed if the impact level in the argument is equal to, or higher than, the value of the global
$
ConfirmPreference
shell variable (which defaults to ‘HIGH). The default value of the argument is ‘Medium’ and, of course, this option only make sense if you specifySupportsShouldProcess
option as well.
- In this argument, you will configure the impact level (“HIGH”, “MEDIUM” or “LOW”) at which the action that you are performing in your function should be preceded by a prompted confirmation request such as “Are you sure about this?”. This prompt would only be displayed if the impact level in the argument is equal to, or higher than, the value of the global
DefaultParameterSet
Name
- The
DefaultParameterSetName
argument specifies the name of the parameter set that Windows PowerShell will attempt to use when it cannot determine which parameter set to use. (for more information, useabout_Functions_CmdletBindingAttribute
)
- The
So how should we code these?
1 2 3 4 5 6 |
Function NowIamDoingRight { [cmdletbinding( SupportsShouldProcess=<Boolean $True or $False> ConfirmImpact=<String 'None' 'Low' 'Medium' 'High'> DefaultParameterSetName <String ParameterName> )] |
Both SupportsShouldProcess
and ConfirmImpact
are part of the advanced functions confirmation methods, or as I like to call them, the “License to Kill” methods.
The License to Kill Options – Performing Highly Dangerous Operations
SupportsShouldProcess Argument
First License to Kill parameter – “My Name is If… -WHATIF”
We know that there is no such thing as a small mistake in a DBA’s job. When it happens, it is always a mistake of HUGE proportions.
Fortunately we can use the -
whatif
common parameter to help reduce mistakes, if our functions support it. This does nothing more than to show you what it would do without actually running the command in the function you are executing. It is something like asking the function: “What am I about to do?” rather than asking “Holy Saints. Do I have a backup?” after the function does something unexpected.
To illustrate this, we’ll create two advanced functions, Get
-
MSSQLTable
and GET-
MSSQLProcedure
. The former outputs the SQL Server table objects that you specify, and the latter outputs stored procedure objects.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
function Get-MSSQLTable { [CmdletBinding()] param( [Parameter(Position=0, Mandatory=$true)] [String]$Server, [Parameter(Position=1, Mandatory=$true)] [String]$Database, [Parameter(Position=2, Mandatory=$false)] [String]$TableName ) begin { [reflection.assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo") | out-null $ServerName=New-Object "Microsoft.SqlServer.Management.Smo.Server" $server } Process { $ServerName.Databases | where {$_.name -eq "$Database" } | % { foreach ($table in $_.tables) { if ($tableName) { $tables = $table | where {$_.name -like "$tablename*"} } else { $tables = $table } Write-Output $tables } } } } function Get-MSSQLStoredProcedure { [CmdletBinding()] param( [Parameter(Position=0, Mandatory=$true)] [String]$Server, [Parameter(Position=1, Mandatory=$true)] [String]$Database, [Parameter(Position=2, Mandatory=$false)] [String]$ProcedureName ) Begin { [reflection.assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo") | out-null $ServerName=New-Object "Microsoft.SqlServer.Management.Smo.Server" $server } Process { $ServerName.Databases | where {$_.name -eq "$Database" } | % { foreach ($Proc in $_.storedprocedures) { if ($ProcedureName) { $procs = $Proc | where {$_.name -like "$ProcedureName*"} } else { $procs = $proc } Write-Output $procs } } } } |
There is not much that to say about them. They are simple functions.
Now, we will write a more dangerous function that drops the objects. Now, you’ll probably have noticed that this is not a pair of related functions; one to drop Tables and other to drop Procedures. This is a function to drop a database object no matter what it is, just as long as the object has the drop method, or will accept being dropped.
The first name that I thought of was Drop-
SQLObject,
but Drop it is not an approved PowerShell verb, so I replaced the ‘Drop’ verb with something similar and approved. Remove-
SQLObject
. (You can get a list of approved verbs by using the Get-Verb
cmdlet).
My first step towards using this function was, of course, to enable the -
whatif
and -
confirm
parameters by adding SupportsShouldProcess
=$true
in the CmdletBinding
attribute:
1 2 3 |
function Remove-SQLObject { [CmdletBinding(SupportsShouldProcess=$true)] |
And I created it so that it receives an SMO object as a parameter and drops the object inside a process block, but I encountered a problem with Foreach
enumerators in the Pipeline, and to retrieve the Database Objects (tables, stored procedures and so on). In SMO you need to use enumerators, as I’ve shown in the “foreach
$
proc
in $_.
StoredProcedures
” and “foreach
$table in $_tables
” in the functions above. This will cause problems if you drop an object in the enumeration. This script won’t work.
1 2 3 4 5 6 7 8 9 10 11 12 |
function Remove-SQLObject { [CmdletBinding(SupportsShouldProcess=$true)] param( [Parameter(Position=0, Mandatory=$true, ValueFromPipeline = $true)] [ValidateScript({$_.GetType().Namespace -like "Microsoft.SqlServer.Management.Smo*" -and ($_.gettype().getinterfaces()|select -expand name) -contains 'idroppable'})] $SmoObject ) Process { SmoObject.Drop() } } |
Basically, Enumerators can be used to read the data in the collection, but they cannot be used to modify the underlying collection. If you try to run the script, you get an error:
1 |
An error occurred while enumerating through a collection: Collection was modified; enumeration operation may not execute. |
To work around to this limitation, I used an Array $
MyObject
and populated it completely in the process block: In the End block, I effectively dropped the items of this object one by one. The final code (before I implemented -whatif processing) is:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
Function Remove-SQLObject { [CmdletBinding(SupportsShouldProcess=$true)] param( [Parameter(Position=0, Mandatory=$true, ValueFromPipeline = $true)] [ValidateScript({$_.GetType().Namespace -like "Microsoft.SqlServer.Management.Smo*" -and ($_.gettype().getinterfaces()|select -expand name) -contains 'idroppable'})] $SmoObject ) begin { $MyObject = @() } process { if ( $PSCmdlet.ShouldProcess("$($SmoObject.GetType().name) - $($SmoObject)") ) { foreach ($drop in $SmoObject) { $MyObject += $drop Write-verbose "Dropping $($SmoObject.GetType().name) - $($SmoObject)" } } } end { $count = $MyObject.count for ($i = 0; $i -lt $count; $i++) { $MyObject[$i].Drop() } } } |
The code that will eventually allow the -
whatif
argument to work properly is in the line 14 :
1 |
if ( $PSCmdlet.ShouldProcess("$($SmoObject.GetType().name) - $($SmoObject)") ) {...} |
So if the’ -
whatif'
parameter is supplied, then the parameter passed to theShouldProcess
method will be displayed, but the code in the scriptblock that follows the IF statement won’t be executed. Otherwise, the scriptblock after the IF statement will be executed. . In this case I am displaying the type of the object (table, stored procedure… etc.) and the Schema and Name of the object so you will be clear about what will be executed if you were to leave out the -Whatif.
In my case, because I had to call the drop method on the database object in the end block and not in the Process block, you might think, as I did, that I would need a variable to hold a flag as to whether I’d passed -whatif or not: Maybe Something like this?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
begin { $MyObject = @() $whatif = $true } process { if ( $PSCmdlet.ShouldProcess("$($SmoObject.GetType().name) - $($SmoObject)") ) { $whatif = $False foreach ($drop in $SmoObject) { $MyObject += $drop Write-verbose "Dropping $($SmoObject.GetType().name) - $($SmoObject)" } } } end { if ($whatif) { $count = $MyObject.count for ($i = 0; $i -lt $count; $i++) { $MyObject[$i].Drop() } |
Wrong. Just by passing the -whatif as a parameter, no operation or method will be performed in all the code of the function. I do not need to worry.
The function requires that an SMO object is passed to it. This required validation. I therefore had to add this rather daunting line that checks that the parameter is an SMO object and that it is droppable:
1 |
[ValidateScript({$_.GetType().Namespace -like "Microsoft.SqlServer.Management.Smo*" -and ($_.gettype().getinterfaces()|select -expand name) -contains 'idroppable'})] $SmoObject |
Don’t worry, I will cover the parameters’ metadata, and the ValidateScript
attribute, in the next article. For now it is good to know that the $SmoObject
parameter accepts only SMO Objects that can be dropped.
Now it is time to see the magic:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
create table ThePoshDBA_1 (id int) go create table ThePoshDBA_2 (id int) go create table ThePoshDBA_3 (id int) go create table ThePoshDBA_4 (id int) go create table ThePoshDBA_5 (id int) go create table ThePoshDBA_6 (id int) Get-MSSQLTable -Server . -Database "ThePoshDBA" -TableName "ThePoshDba*" | Remove-SQLObject -whatif |
Do not be scared if all that you see displayed is ….
1 2 3 4 5 6 |
What if: Performing operation "Remove-SQLObject" on Target "Table - [dbo].[ThePoshDBA_1]". What if: Performing operation "Remove-SQLObject" on Target "Table - [dbo].[ThePoshDBA_2]". What if: Performing operation "Remove-SQLObject" on Target "Table - [dbo].[ThePoshDBA_3]". What if: Performing operation "Remove-SQLObject" on Target "Table - [dbo].[ThePoshDBA_4]". What if: Performing operation "Remove-SQLObject" on Target "Table - [dbo].[ThePoshDBA_5]". What if: Performing operation "Remove-SQLObject" on Target "Table - [dbo].[ThePoshDBA_6]". |
… because your tables still will be in the Database ThePoshDBA. No operation was performed; it is just saying to you what it would do.
And of course to effectively drop the tables, you need only remove the -
whatif
parameter.
1 |
Get-MSSQLTable -Server . -Database "ThePoshDBA" -TableName "ThePoshDba*" | Remove-SQLObject |
Bear in mind that the problem I faced by using the Foreach
enumerator when deleting the objects is a particular problem for this function, and it is unlikely that you would ever need to write your -whatif implementation the same way, that is, by using the begin and end blocks of the function. The usual way to implement -
whatif
processing is:
1 2 3 4 5 6 7 8 |
..... process { if ( $PSCmdlet.ShouldProcess("Something In Here to Display") ) { Perform the action ...... } } ..... |
In fact, you can take advantage of using Enumerators. Let’s go a bit off-topic to look at a nice trick. Have you noticed that, for the cmdlet get-
eventlog,
you can either use an array as the parameter for the Servers or pass it in the pipeline? In this case when a parameter is defined as string[] you can do this …
1 |
Get-EventLog Application -computer (Get-Content Servers.txt) |
… instead of this …
1 |
Get-content Servers.txt | foreach {Get-EventLog Application -computer $_} |
You can implement the same functionality in your Function. Let’s see the part of the code of Chad Miller’s Get-SqlWmi; (thanks to him for teaching me this trick). This function uses the WMI ManagedComputer
cmdlet to get port, instance and service account WMI information for all SQL instances on a computer.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
function Get-SqlWmi { [CmdletBinding()] param( [Parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)] [ValidateNotNullorEmpty()] [string[]]$ComputerName ) #Microsoft.SqlServer.Management.Smo.Wmi.ManagedComputer only works on SQL 2005 and higher. If we fail to gather info at least output #null values and computername for 2000 servers in catch block BEGIN {} PROCESS { foreach ($computer in $computername) { try { $wmi = new-object "Microsoft.SqlServer.Management.Smo.Wmi.ManagedComputer" $Computer -ErrorAction 'Stop' ........ |
By using the foreach
enumerator , the parameter $Computername as string[]
and of course understanding the
ValueFromPipelineByName
and ValueFromPipelineByValue
, you can simulate the same thing that Get-
EventLog
does.
You can call the Get-
SQLWmi
by passing in an array directly :
1 |
Get-sqlwmi (get-content names.txt) |
…or through the pipeline by value :
1 |
Get-content names.txt | Get-sqlwmi |
…or by through the pipeline name :
1 |
Invoke-sqlcmd -ServerInstance "yourServer" -database "YourDb" -query "Select server_name AS 'ComputerName' FROM ServersTable" | Get-SqlWmi |
Don’t worry my friends, in the next part of these series, we will cover the ValueFromPipeline
property and the related attributes.
But, back to our subject, according to the Don Jones, there is something else to consider as well:
“Don’t forget that -confirm
and -whatif
are also passthrough
parameters. That is, if your function declares SupportsShouldProcess=$True,
and someone runs the function with -whatif
or -confirm
, those will be passed to any other cmdlets WITHIN YOUR FUNCTION that also support -confirm
and -whatif
. Since your change isn’t being made by a cmdlet, you can’t take advantage of that, so the If construct and using $psCmdlet.ShouldProcess()
is indeed the right way to go”.
Second License To Kill parameter- “Sir, you asked for a martini shaken not stirred. Do you -CONFIRM ?”
Sometimes even if we really are sure about an action, why not ask again? For this case we have the common parameter -
confirm
and as it is also enabled by SupportsShouldProcess
we can use it as well.
1 |
Get-MSSQLTable -Server . -Database "ThePoshDBA" -TableName "ThePoshDba*" | Remove-SQLObject -verbose -confirm |
And a prompt will be displayed to allow you to confirm your murder. No problems, we have license to kill.
ConfirmImpact Argument
To finish the ‘License To Kill’ options, there are some actions that are so dangerous that I want to prompt the user with the same -confirm question, but I want to do it every time that the function is called. I don’t want the user to be obliged to explicitly pass the -confirm parameter.
In this case, we can use the ConfirmImpact
option just adding in the [CmdletBinding()]
code :
1 |
[CmdletBinding(SupportsShouldProcess=$true,ConfirmImpact=LevelOfImpact)] |
As we see in the beginning of this article, The default is ‘ Medium’ but there are a choice of four impact levels ,’None’, ‘Low’, ‘Medium’ or ‘High’. We’ll code our function to specify ‘High’:
1 |
[CmdletBinding(SupportsShouldProcess=$true,ConfirmImpact='High')] |
This operation can be so potentially catastrophic that I am declaring that the impact is HIGH. Every time that I call the function I’ll be asked to confirm the operation.
‘Ha Laerte’, I hear you say, ‘this function is generic. I need this confirmation all the time, but not for all objects. I need confirmation for my tables, but not for the stored procedures. Is there some way for me to suppress this confirmation?’
‘Sure!’, I reply, ‘just pass the parameter -confirm:$false
‘.
1 |
Get-MSSQLStoredProcedure -Server . -Database "ThePoshDBA" -ProcedureName "Proc_ThePoshDba*" | Remove-SQLObject -verbose -confirm:$ |
1 2 3 |
VERBOSE: Dropping StoredProcedure - [dbo].[Proc_ThePoshDBA_1] VERBOSE: Dropping StoredProcedure - [dbo].[Proc_ThePoshDBA_2] VERBOSE: Dropping StoredProcedure - [dbo].[Proc_ThePoshDBA_3] |
Technically we can create a new parameter -force
to suppress this prompt, but again, in the words of the Don Jones:
“-Force is usually used to override a permissions problem, read-only, or something else; using it to suppress auto-confirm is a bit nonstandard, but it is comprehensible.“
Colleagues, we are about the business of learning how to write advanced code in PowerShell. It is time to be more than just “comprehensible”; right ?
DefaultParameterSetName Argument
There are cmdlets that cannot be called with two or more parameters from different Parameter Sets at the same time. Some parameters depend on others in the same set. I confess that this concept was a bit difficult to understand for me, but after a talk with the Jedi Council (Chad, Ravi, Shay, Jeffery), the apple fell on my head. I felt the gravity of the situation.
Yes. It is a privilege to me have the power to invoke the Jedi Cmdlets as Invoke-ChadMiller, Invoke-Ravikanth, Invoke-ShayLevy, Invoke-JefferyHicks, Invoke-Boe, and so on.
(Ed: Focus Laerte, let’s get back to an example )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
Function ImAGrownUpManNow { [cmdletbinding( SupportsShouldProcess=..... ConfirmImpact=.... DefaultParameterSet = "GroupSet1" )] param ( [Parameter(ParameterSet="GroupSet1",....) [String] $P1, [Parameter(ParameterSet="GroupSet1",....) [String] $P2, [Parameter(ParameterSet="GroupSet2",...) [String] $P3, [Parameter(ParameterSet="GroupSet2",...) [String] $P4, ) .... } |
The DefaultParameterSetName
in the GroupSet1t
means that if you have some positional parameters with default values, and the user doesn’t specify any parameter names, then the default set will be used.
The DefaultParameterSetName
is bound to the ParameterSetName
option that you can specify in the parameter set metadata. There is no sense in using the one without the other.
As you can see, the parameters P1
and P2
are from GroupSet1
,
and P3
and P4
are from GroupSet2
. This means that P1
or P2
cannot be passed together with P3
or P4
; but all of them are in the same Function.
I guess that you know the Get-
Process
cmdlet of course. I can get a process by ID
OR by Name
, not both. Without using multiple distinct parameter sets, you’d need something like Get
-
ProcessByID
and Get-ProcessByName
– Thanks to Shay Levy for this example.
In my case, if I Try …
1 |
Get-Process -id 1916 |
1 2 3 |
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName ------- ------ ----- ----- ----- ------ -- ----------- 63 3 772 2812 33 0,02 1916 armsvc |
…or …
1 |
Get-Process -Name armsvc |
1 2 3 |
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName ------- ------ ----- ----- ----- ------ -- ----------- 63 3 772 2812 33 0,02 1916 armsvc |
But If I try …
1 |
Get-Process -Id 1916 -Name armsvc |
1 |
Get-Process : Parameter set cannot be resolved using the specified named parameters...... |
Other great examples are in the SQLPSX - Invoke-SQLRestore
. Take a look at the code : – The parts with “…” was truncated by me to fit on the page.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
function Invoke-SqlRestore { param( [CmdletBinding(DefaultParametersetName="Restore")] [Parameter(Position=0, Mandatory=$true)] $sqlserver, [Parameter(ParameterSetName="Restore",...)] [string]$dbname, [Parameter(Position=2, Mandatory=$true)] [string]$filepath, [Parameter(ParameterSetName="Restore",...)] [...]$action='Database', [Parameter(ParameterSetName="Restore",...)] [string]$stopat, [Parameter(ParameterSetName="Restore",...)] [hashtable]$relocatefiles, [Parameter(ParameterSetName="Restore",...)] [switch]$force, [Parameter(ParameterSetName="Restore",...)] [switch]$norecovery, [Parameter(ParameterSetName="Restore",...)] [switch]$keepreplication, [Parameter(ParameterSetName="FileList",...)] [switch]$FileListOnly ) |
The parameters $dbname
, $
action
, $stopat
, $realocatefiles
, $force
, $norecovery
, and $keepreplication
are parts of the Restore ParameterSet
that is the DefaultParameterSetName
. The $FileListOnly
is from FileList
, $
Sqlserve
r and Filepath
are not part of any parameter set.
Why is this useful? Well, if I specify the FileListonly
in a restore backup , there is no need to use $action
, $stopat
or the entire Restore set. I just want FileListOnly
or, in a T-SQL example a Restore FileListOnly.
The same applies to FileList
ParameterSet. But using either Parameters Set in this function also requires a SQL Server Connection and a File path. This is why these options do not belong to any Parameter Set.
One last example: Do you remember when I said that “if you have some positional parameters with default values, and the user doesn’t specify any parameter names, the default set will be used”. Let’s see how this works. In the first function, the DefaultParameterSetName
is “First” and in the Second ParameterSetName
is “Second” :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
Function TestingDefaultParameterSetName_ToFirst { [CmdletBinding(DefaultParameterSetName = "First")] param ( [parameter(ParameterSetName="First")] [Int] $IamFirst = 10, [parameter(ParameterSetName="Second")] [int] $IamSecond = 20 ) Write-Host "------------------------------------------------------------" Write-Host "I am The TestingDefaultParameterSetName_ToFirst Function" Write-Host "Parameter Set : $($PSCmdlet.ParameterSetName)" switch ($PSCmdlet.ParameterSetName) { "First" { write-host "I am First : $($IamFirst)" ; break } "Second" { write-host "I am Second : $($IamSecond)" ; break } } Write-Host "------------------------------------------------------------" } Function TestingDefaultParameterSetName_ToSecond { [CmdletBinding(DefaultParameterSetName = "Second")] param ( [parameter(ParameterSetName="First")] [Int] $IamFirst = 30, [parameter(ParameterSetName="Second")] [int] $IamSecond = 40 ) Write-Host "------------------------------------------------------------" Write-Host "I am The TestingDefaultParameterSetName_ToSecond Function" Write-Host "Parameter Set : $($PSCmdlet.ParameterSetName)" switch ($PSCmdlet.ParameterSetName) { "First" { write-host "I am First : $($IamFirst)" ; break } "Second" { write-host "I am Second : $($IamSecond)" ; break } } Write-Host "------------------------------------------------------------" } |
Now let’s run each function without any parameters:
1 2 |
TestingDefaultParameterSetName_ToFirst TestingDefaultParameterSetName_ToSecond |
1 2 3 4 5 6 7 8 9 10 |
------------------------------------------------------------ I am The TestingDefaultParameterSetName_ToFirst Function Parameter Set : First I am First : 10 ------------------------------------------------------------ ------------------------------------------------------------ I am The TestingDefaultParameterSetName_ToSecond Function Parameter Set : Second I am Second : 40 ------------------------------------------------------------ |
Do you see how this works? I am not passing any parameter to them and the parameter set default was that specified at DefaultParameterSetName
That is it my friends. In this article we covered the famous [
CmdletBinding
(
)]
attribute and demonstrated how some of its magic is done. In the next article in this series, we will look at the wonderful ways that parameters can be validated with advanced functions.
References and Acknowlegements
I would like to thank some gentlemen who spared no effort to share their knowledge, either to me or to the community, and who took part, directly or indirectly, in the creation of this article. To my good friends Chad Miller, Shay Levy, Ravikanth Chaganti; and for his articles, books and blog, Sir Don Jones.
A special thanks to Sir Jeffery Hicks, Sir Phil Factor and Sir Bob Beauchemin that kindly gave your time to tech review and proofread the article.
A huge thanks to my Tech Editors Chis Massey and Andrew Clarke. The real magic is what these guys do for me
Books:
- Bruce Payette’s Windows PowerShell in Action, Second Edition
Blogs:
- Chad Miller – (@cmille19)
- Sev17 – SQL Server, PowerShell and so on
- Codeplex – SQLPSX (SQL Server PowerShell Extensions)
- Sean Kearney (@energizedtech)
- Implementing the-WHATIF into an Advanced Function in Windows PowerShell
- Don Jones (@concentrateddon)
- Jeffery Hicks (@JeffHicks)
- Ravikanth Chaganti (@ravikanth)
Load comments