Learn How PowerShell CmdletBinding Enhances Functions

Published:5 May 2022 - 10 min. read

Have you ever wanted to create a PowerShell cmdlet but didn’t know C# or another Microsoft .NET Framework language? Why keep ‘wanting’ when you have PowerShell CmdletBinding at your fingertips?

Not a reader? Watch this related video tutorial!
Not seeing the video? Make sure your ad blocker is disabled.

In this tutorial, you will learn how to use the PowerShell CmdletBinding attribute to enhance functions and make them behave like cmdlets.

Ready? Dig in and make your functions behave like never before!

Prerequisites

This tutorial will be a hands-on demonstration. As long as you have a Windows or Linux PC with PowerShell v5.1 or greater installed, you’re good to go — This tutorial uses a Windows 10 machine with PowerShell v5.1.

Creating a Basic Function to Organize Files with PowerShell

There are cases where you might have so many files of different types in a directory without a good organization. Keeping your files organized is always a good practice, so you’ll create a basic function to move files by file extensions to a specified folder.

A basic PowerShell function is not defined as having just a few lines of code. “Basic” in this context means that the function lacks the features of a PowerShell cmdlet. The function doesn’t have common parameters, and there’s no full control over the parameters available.

1. Open Windows PowerShell ISE as administrator.

2. Copy and paste the code below to the code editor, and run the code. The code below moves all .pdf, .doc, and .txt files on your Desktop to a single folder called Documents.

From this point throughout this tutorial, be sure to replace ADMIN in the Desktop folder path (C:\Users\ADMIN\Desktop\) with your computer username.

Function Group-Files
{    
    Param(
    [string]$Path,
    [string]$Folder
    )
    # Check if the destination folder exists. If not, then create it
    If ((Test-Path -Path ($Path + "\" + $Folder)) -eq $False) {        
        New-Item -Name $Folder -Path $Path -ItemType Directory
    }
    # Move Items
    Get-ChildItem $Path | ForEach-Object {
				# If the last four characters of the filename ends with .pdf, .doc, .txt
        If ($_.FullName.Substring($_.FullName.Length -4) -in ".pdf", ".doc", ".txt") 
				{
            Move-Item -Path $_.FullName -Destination ($Path + $Folder)
        }
    }
}
# Call the Group-Files function
Group-Files -Path C:\Users\ADMIN\Desktop\ -Folder "Documents"
Moving Files from Desktop to Documents Folder
Moving Files from Desktop to Documents Folder

After running the code above, you may have noticed that all the files just moved without asking you to confirm. Moreover, the function didn’t tell you what would happen if you ran the code. You’ll learn more about enhancing the function with CmdletBinding in the following sections.

3. Lastly, run the Get-Command below to list all the parameters available for the Group-Files function you created.

(Get-Command Group-Files).Parameters
Getting Parameters for the Group-Files Function
Getting Parameters for the Group-Files Function

Gaining Access to Common Parameters with the CmdletBinding Attribute

You’ve seen that a basic function works fine. But perhaps you prefer to make parameters mandatory or add a confirm action dialog box? If so, the CmdletBinding attribute will do the trick! The CmdletBinding attribute allows you to use common parameters available for PowerShell cmdlets.

The following code represents the syntax of the CmdletBindingattribute, including all its arguments.

{
   [CmdletBinding(
        ConfirmImpact=<string>,
				# Default parameter set name you want PowerShell to use 
				# if there is no parameter set.
        DefaultParameterSetName=<string>, 
				# uri to online help, must begin with http or https
        HelpURI=<uri>, 
				# Used when you’re trying to return data from a large database suchas MySQL.
        SupportsPaging=<boolean>, 
				# Adds three parameters – First, Skip, and IncludeTotalCount to the function.
        SupportsShouldProcess=<boolean>,
				# positional binding binds positions to parameters 
        PositionalBinding=<boolean>) as defined
   ]
	 # It's important to use the Param keyword
   Param ($Parameter1)

   Begin{}
   Process{}
   End{}
}

Now, run the Group-Files function below, adding the CmdletBinding attribute, to give it access to common parameters and display all available parameters.

Function Group-Files
{   
    [CmdletBinding()]
    Param(
    [string]$Path,
    [string]$Folder
    )
		#...   
}

# Gets all parameters available for the Group-Files function
(Get-Command Group-Files).Parameters

You can now see below that the Group-Files function has more parameters than you previously defined. All the common parameters are now available for the function to use.

Getting All Parameters for the Group-Files Function
Getting All Parameters for the Group-Files Function

Adding -WhatIf Switch to Double-Check Tasks

You’ve seen how a basic function works, but a function is meant for more incredible things. And you can create advanced functions in many ways, like using switch parameters.

Switch parameters in PowerShell allow you to add controls to your functions. If the switch is True, an action will run, and another action will run if the switch is False.

Advanced functions work like PowerShell cmdlets. With an advanced function, you can access all the common parameters available for cmdlets. But as you’ve seen, advanced functions are written in the PowerShell language while cmdlets are written in a Microsoft .NET Framework language, such as C#.

There are only two advanced switches for risk mitigation: WhatIf and Confirm switches. But you’ll start working on the -WhatIf switch in this tutorial. This switch lets you check what will happen (a dry run) if you run a function with high impact.

To see how the switch works, run the following command on PowerShell with the -WhatIf switch. The command displays the “What if” message explaining the operation of running the New-Item command.

New-Item -Name AdamBertram -Path C:\\Users\\ADMIN\\Desktop -ItemType Directory -WhatIf
Appending -WhatIf to confirm the New-Item Action
Appending -WhatIf to confirm the New-Item Action

Now, run the code below with the CmdletBinding added to make the -WhatIf accessible when the function is called.

From this point onwards, you’ll learn how to enhance the Group-Files function progressively and confirm the actions with the Write-Host command. And at the end of this tutorial, you’ll see the final structure of the enhanced function.

Function Group-Files
{   # Set SupportsShouldProcess to True, to make -WhatIf and -Confirm accessible
    [CmdletBinding(SupportsShouldProcess=$True)]
    Param(
    [string]$Path,
    [string]$Folder
    )
    
    If ($PSCmdlet.ShouldProcess($Path)) {
        Write-Host "WhatIf wasn't used, moving files..."
    }
    Else {
        Write-Host "WhatIf has been used! Doing nothing..."
    } 
}
# Calls the Grou-File function
Group-Files -Path C:\\Users\\ADMIN\\Desktop\\ -Folder "Documents" -WhatIf

There is a system variable called $WhatIfPreference, which is set to false by default. But when the variable is true, the Group-Files function will be automatically called with -WhatIf

You can see in the following screenshots that the function prints out two different messages depending on whether you used the -WhatIf switch or not.

Displaying a Message to Confirm -WhatIf was used
Displaying a Message to Confirm -WhatIf was used
Displaying a Message to Confirm -WhatIf wasn’t used
Displaying a Message to Confirm -WhatIf wasn’t used

Confirming Actions with the -Confirm Switch

Like the -WhatIf switch, the -Confirm switch tells you what a specific function or command will do. But what’s excellent with adding the -Confirm switch in a function is that you’ll get a prompt to confirm the actions to perform.

Adding the -Confirm switch comes in handy when your function is supposed to perform high-risk actions, such as moving or deleting files.

Run the following code to prompt for confirmation (-Confirm) before performing the specified action. The -Confirm parameter only works if SupportsShouldProcess is true, similar to -WhatIf.

Function Group-Files
{
		# Set SupportsShouldProcess to True to make -WhatIf and -Confirm accessibly
    [CmdletBinding(SupportsShouldProcess=$True)]
    Param([string]$Path, [string]$Folders)

    # If the user confirmed Yes, or user didn't use -Confirm
    If ($PSCmdlet.ShouldProcess($Path)) {
        Write-Host "Files Moved"
    }
    # If the user confirmed No
    Else {
        Write-Host "Action Declined"
    }
}

# Calls the Group-Files function
Group-Files -Path C:\\Users\\ADMIN\\Desktop\\ -Folder "Documents" -Confirm

Now, choose an option on whether to perform the action or not. The ShouldProcess method from the $PSCmdlet automatic variable will be used to control if the files are moved or not.

Confirming if Group-Files Function Should Run
Confirming if Group-Files Function Should Run

If you choose to confirm the action, you’ll see the message shown below in the PowerShell terminal. Otherwise, you’ll get the message saying “Action Declined” instead.

Viewing Confirmed Action Message
Viewing Confirmed Action Message

Setting Impact Levels on Functions

Perhaps you want to set an impact level to your function and not just rely on the -Confirm switch. If so, setting a confirmation impact to your function by adding the ConfirmImpact argument in CmdletBinding will do the trick.

Below is the syntax of the CmdletBinding. The ConfirmImpact argument’s level determines the “destructiveness” of the function’s action to perform.

[CmdletBinding(SupportsShouldProcess=$True, ConfirmImpact='level')]

You can set the ConfirmImpact argument’s level to one of the following:

  • None – PowerShell will not prompt confirmation for any actions unless you use the -Confirm switch.
  • Low – Actions with low, medium, or high risk will be automatically confirmed.
  • Medium – The default level of the ConfirmImpact argument, where PowerShell prompts confirmation for actions with medium or high risk, such as deleting files.
  • High – PowerShell prompts the user as if the function was called with the -Confirm parameter.

Now, run the code below to see how the ConfirmImpact argument and the preference $ConfirmPreference variable work side-by-side.

The action of ConfirmImpact depends on the $ConfirmPreference variable’s value. You’ll get a confirmation prompt if you set the ConfirmImpact level equal to or greater than the $ConfirmPreference variable’s value.

$ConfirmPreference='High' # Sets confirmation preference
Function Group-Files
{
		# Sets -WhatIf, -Confirm and ConfirmImpact accessible
    [CmdletBinding(SupportsShouldProcess=$True, ConfirmImpact='High')]
    Param([string]$Path, [string]$Folders)

    # If the user confirmed Yes, or user didn't use -Confirm
    If ($PSCmdlet.ShouldProcess($Path)) {
        Write-Host "Files Moved"
    }
    # If the user confirmed No
    Else {
        Write-Host "Action Declined"
    }
}

# Calls the Group-Files function
Group-Files -Path C:\\Users\\ADMIN\\Desktop\\ -Folder "Documents"

Since the ConfimImpact level is equal to the $ConfirmPreference variable value, PowerShell prompts for confirmation even without using the -Confirm switch, as shown below.

Viewing Action Confirmation Using ConfirmImpact Argument
Viewing Action Confirmation Using ConfirmImpact Argument

Displaying More Information with Write-Verbose Statements

Apart from the parameters you’ve seen in the previous examples, CmdletBinding provides many other common parameters, like -Verbose.

The -Verbose parameter displays more information about what is being done in the code. But how do you utilize it in your function? By adding Write-Verbose statements in your function.

Run the code below to see how Write-Verbose statements work on functions to display messages.

Function Group-Files
{
    [CmdletBinding(SupportsShouldProcess=$True)]
    Param([string]$Path, [string]$Folder)
    
		Write-Verbose "Creating the folder if it doesn't exist..."

    # If the user confirmed Yes, or user didn't use -Confirm switch
    If ($PSCmdlet.ShouldProcess($Path)) {
        Write-Host "Files will be moved"
				Write-Verbose "Moving files..."
    }
    # If the user confirmed No
    Else {
        Write-Host "Files will not be moved"
    }
}

# Calls the Group-Files function
Group-Files -Path C:\\Users\\ADMIN\\Desktop\\ -Folder "Documents"

You can see below that PowerShell displays the VERBOSE messages in the output when the function is called with the -Verbose parameter.

Printing verbose messages while the functions run is helpful for troubleshooting if anything goes wrong since you know precisely what the function is currently doing beforehand.

Displaying VERBOSE messages with Write-Verbose
Displaying VERBOSE messages with Write-Verbose

One key thing to note is that while cmdlets are available everywhere, advanced functions can only be accessed in the current session. And you can only use a few automatic variables in an advanced function. For example, the $args automatic variable is unavailable for an advanced function.

Controlling Parameters with the Parameter Attribute

The Group-Files function has just two parameters, and that’s all the function needs. But explicitly defining the behavior of the two parameters is good practice.

For instance, running the Group-Files function will not work without parameters, so you’ll set the $Path parameter as mandatory. At the same time, you’ll set a default value for the $Folders parameter.

Below is the syntax for the Parameter attribute and all its arguments.

Param
(
    [Parameter(
        Mandatory=<Boolean>,
        Position=<Integer>,
        ParameterSetName=<String>,
        ValueFromPipeline=<Boolean>,
        ValueFromPipelineByPropertyName=<Boolean>,
        ValueFromRemainingArguments=<Boolean>,
        HelpMessage=<String>,
     )]
    [string[]]
    $Parameter1
)

Now, run the following code to make the $Path parameter mandatory.

Function Group-Files
{
		[CmdletBinding(
				SupportsShouldProcess=$True # Since you're using the common parameters
		)]
    Param(
		[Parameter(
        Mandatory=$True,
        Position=0,
				# Since the Path parameter can also get its value from a pipeline input, 
				# provide access to the pipeline input
        ValueFromPipeline=$True,
        ValueFromPipelineByPropertyName=$True
     )]
    [string]$Path,

    [Parameter(
				Mandatory=$False, 
				Position=1
		)]
    [string]$Folder = "Documents" # Default value of $Folder parameter
    )

    # If the user confirmed Yes, or user didn't use -Confirm
    If ($PSCmdlet.ShouldProcess($Path)) {
        Write-Host "Files will be moved"
				Write-Verbose "Moving files..."
    }
    # If the user confirmed No
    Else {Write-Host "Files will not be moved"}
}

# Calls the Group-Files function without the -Path parameter
Group-Files -Folder "Documents"
Below is the syntax for the `Parameter` attribute and all its arguments.
Below is the syntax for the Parameter attribute and all its arguments.

Improving Functions with CmdletBinding and Parameter Attributes

Up to this point, you’ve experimented with the CmdletBinding and Parameter attributes, so it’s time to add the finishing touches to the Group-Files function. You’ll use all necessary arguments and attributes to improve the function.

Moreover, you’ll add the validation attributes below to ensure the user inputs the correct values for the parameters.

  • ValidateScript – Used for the $Path parameter to make sure the path is a folder and not a file.
  • ValidateCount – Ensures the minimum and maximum arguments for $Folder.

Run the following code containing all the necessary details for the Group-Files function to work more like an advanced function.

The code below checks your Desktop for files ending in .doc, .pdf, or .txt and asks for confirmation before moving them to the Documents folder.

Function Group-Files
{   
    [CmdletBinding(
        SupportsShouldProcess=$True, # Allows the use of -Confirm and -Whatif
        PositionalBinding=$True, # Binds Path and Folder by their position
        ConfirmImpact="Medium" # Confirms before moving files
    )]  
    Param(
    [Parameter(
        Mandatory=$True,
        ValueFromPipeline=$True,
        ValueFromPipelineByPropertyName=$True,
        HelpMessage="Enter a source folder path"
     )]
  	# Validate the path is a folder and not a file
    [ValidateScript({Test-Path $_ -PathType Container})]
    [String]$Path,

    [Parameter(
        Mandatory=$False,
        HelpMessage="Enter a destination path"
     )]

    [ValidateCount(1,1)] # Must provide 1 argument/folder name
    [String]$Folder = "Documents"
    )
		Begin {
	    # Uncomment the line below to activate -whatif
	    # $WhatIfPreference = $True
	
	    # Uncomment the line below not to ask for confirmation when moving each file
			# $ConfirmPreference = "High"
		}

		# Contains the main part of the function
	  Process { 
        # Check if folders exist. If not, then create them
        If ((Test-Path -Path ($Path + "\\" + $Folder)) -eq $False) {
            #Write-Host ($Path+"\\"+$_) does not exist!
            Write-Verbose "Creating folder $($Path+"\\"+$Folder) since it does not exist."
            New-Item -Name $Folder -Path $Path -ItemType Directory
        }
        # Debug message
        Write-Debug "Make sure files are not opened with any program before moving."
        # If the user confirmed Yes, or user didn't use -Confirm
        If ($PSCmdlet.ShouldProcess($Path)) {
            Write-Host "Files will be moved"
            # Get all the files in the $Path directory
						Write-Verbose "Moving files..."
            Get-ChildItem $Path | ForEach-Object {
		        # Documents
		        If ($_.FullName.Substring($_.FullName.Length -4) -in ".pdf", ".doc", ".txt") {
								Write-Verbose "Moving file $($_.FullName) to $($Path + $Folder)"
		            Move-Item -Path $_.FullName -Destination ($Path + $Folder)
								}
            }
        }
        # If the user confirmed No
        Else {
            Write-Host "Files will not be moved"
        }
    }
}
# Call the Group-Files function
Group-Files -Verbose -Debug
Displaying VERBOSE and DEBUG messages
Displaying VERBOSE and DEBUG messages

Conclusion

This tutorial aims to teach you how to enhance a basic function. Did it do well? You’ve progressively created advanced functions and gained features of compiled cmdlets. And at this point, you can already create your own cmdlets with PowerShell functions.

Now, why not enhance the functions in your old scripts to make them advanced!

Hate ads? Want to support the writer? Get many of our tutorials packaged as an ATA Guidebook.

Explore ATA Guidebooks

Looks like you're offline!