PowerShell Parameters are a set of commands and arguments for PowerShell (a command line program used to automate tasks). The parameters can be included in your scripts without having to write them out, which is great because it saves time.
Parameters are a feature of PowerShell that allow you to pass values into a script. Parameters can be used in any command, function, or script block. They are also available in PowerShell workflows and scripts. Read more in detail here: powershell script parameter.
One or more parameters, also known as arguments, may be used with any PowerShell command. You’re not creating proper PowerShell code if you don’t use PowerShell parameters in your PowerShell functions!
You’ll cover almost every aspect of generating and using PowerShell parameters or arguments in this tutorial!
This is an excerpt from PowerShell for SysAdmins, which I wrote. Check it out if you want to learn PowerShell or pick up some tips and techniques.
What Is the Purpose of a Parameter?
When you first start writing functions, you’ll have the choice of including or excluding parameters, as well as the behavior of those arguments.
Consider the following scenario: you have a function that installs Microsoft Office. Perhaps it secretly invokes the Office installation inside the method. For our purposes, it doesn’t important what the function performs. With the name of the function and the script block, the basic function looks like this.
## Run the quiet installer here using the function Install-Office
In this case, you just ran Install-Office without any options and it took care of everything.
Whether the Install-Office function had parameters or not made no difference. It didn’t seem to have any necessary arguments; otherwise, PowerShell wouldn’t have allowed us execute it without one.
When Should You Use PowerShell Parameters?
There are several versions of Office. It’s possible that you’ll need to set up Office 2013 and 2016. There is currently no method to indicate this. Every time you wanted to modify the behavior, you could edit the function’s code.
You might, for instance, construct two distinct methods to install various versions.
‘I installed Office 2013.’ function Install-Office2013 Write-Host ‘Wheeeeeeeeeeeeeeeeee ‘I installed Office 2016.’ function Install-Office2016 Write-Host ‘Wheeeeeeeeeeeeeeeeee
It’s possible to do this, but it’s not scalable. It compels you to build a new function for each new version of Office that is released. When you don’t have to, you’ll have to duplicate a lot of code.
Instead, you’ll need a means to modify the function’s functionality by passing in various values at runtime. How do you go about doing this?
Yep! Parameters, or as some refer to them, arguments.
You must add at least one argument to this method if we want to install multiple versions of Office without having to change the code every time.
Before you rapidly come up with a PowerShell parameter, you must first ask yourself, “What is the smallest modification or changes you foresee to be required in this function?”
Keep in mind that you must restart this function without modifying any of the code inside it. The parameter in this case is presumably obvious; you need to provide a Version argument. When you’ve got a function with tens of lines of code, though, the solution isn’t always obvious. It will always assist if you answer the question as accurately as possible.
So you’re aware that a Version argument is required. So, what’s next? You may now add one, but there are various ways to skin that cat, as with any excellent programming language.
Based on my almost decade of PowerShell knowledge, I’ll teach you the “optimal” approach to generate parameters in this lesson. Keep in mind, though, that this isn’t the only method to create a parameter.
Positional parameters are anything that exists. You may use these parameters to provide values to parameters without having to mention the parameter name. Positional criteria are useful, but they aren’t “best practice.” Why? Because they are more difficult to understand, particularly when a function has a lot of arguments.
Using PowerShell to Create a Simple Parameter
To add a parameter to a function, you’ll need two things: a param block and the parameter itself. The param keyword is followed by a set of parentheses to create a param block.
[CmdletBinding()] function Install-Office ‘I installed Office 2016.’ param() Write-Host ‘Wheeeeeeeeeeeeeeeeee
The function’s real functionality hasn’t altered at this time. We’ve just finished installing some piping in preparation for the first parameter.
You’ll now construct the parameter once the param block is in place. The method I recommend for creating a parameter starts with the parameter block, then the type of parameter, and finally the parameter variable name.
[CmdletBinding()] function Install-Office [Parameter()] param([Parameter()] param([Parameter()] par Write-Host ‘I installed Office 2016.’ [string]$Version) ‘Wheeeeeeeeeeeeeeeeee
We’ve now generated a PowerShell function parameter, but what precisely happened?
Every parameter should include a parameter block, which is an optional but suggested component. It is “function plumbing,” similar to the param block, which prepares the parameter for adding further functionality. The kind of argument it is is defined on the second line.
We’ve opted to convert the Version option to a string in this situation. If an explicit type is defined, any value supplied to this argument will be “converted” to a string if it isn’t already one.
The kind is not required, although it is strongly recommended. Defining the sort of parameter it is will help you avoid a lot of unpleasant problems down the line. Have faith in me.
Now that you’ve established the option, you may use the Version argument in the Install-Office command to provide a version string, such as 2013. The value supplied to the Version parameter is also known as the arguments or values of parameters.
PS> Install-Office -Version 2013 I installed Office 2016. Yippee!
What exactly is going on here? You told it you wanted version 2013, yet it still says it’s installed version 2016. You must remember to rename the function’s code to a variable after adding an argument. When a parameter is supplied to a function, the variable is extended to the value that was passed.
Replace the static text 2016 with the Version parameter variable, changing the single quotes to double quotes to allow the variable to grow.
[CmdletBinding()] function Install-Office [Parameter()] param([Parameter()] param([Parameter()] par $Version) ([string]$Version) ([string]$Version) ( Host-Write “I downloaded and installed Office $Version. What a relief! “,,,,,,,,,,
As you can see, any value you provide the Version argument is sent into the method as the $Version variable.
Attribute for Mandatory Parameters
Remember how I said the [Parameter()] line was merely “function plumbing” that was required to get the function ready for further work? The extra labor I mentioned previously is adding parameter properties to a parameter.
A parameter does not have to be a variable placeholder. Parameter characteristics and parameter validity are concepts in PowerShell. The functionality of a parameter may be changed in a variety of ways using parameter attributes.
The Mandatory keyword, for example, is one of the most typical parameter properties you’ll use. You may call the Install-Office function without supplying the Version argument by default, and it will work perfectly. The parameter Version would be optional. Although the $Version variable within the code would not grow since there was no value, it would still execute.
When establishing a parameter, you’ll often want the user to utilize it all of the time. You’ll rely on the argument’s value somewhere in the function’s code, and if the parameter isn’t supplied, the function will fail. You wish to require the user to supply this parameter value to your function in certain instances. You’d want that parameter to be required.
Once you have the fundamental architecture in place, forcing users to utilize a parameter is straightforward. Within the parenthesis of the parameter, you must insert the term Mandatory. Once you’ve done that, calling the function without the argument will cause it to pause until a value is entered.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$Version ) Write-Host “I installed Office $Version. Yippee!” } PS> Install-Office cmdlet Install-Office at command pipeline position 1 Supply values for the following parameters: Version:
Until you supply a value for the Version argument, the function will wait. When you press Enter, PowerShell will run the function and then go on. If you specify a value for the argument, PowerShell will not ask you for it every time you run the command.
Validation Attributes for PowerShell Parameters
One of the most popular parameter features is to make a parameter obligatory, but you may also utilize parameter validation attributes. It’s always important in programming to limit user input as much as possible. Limiting the data that users (or even you!) may send to your functions or scripts can minimize redundant code in your function that must cater for a variety of scenarios.
Observational Learning
For example, I showed sending the number 2013 to the Install-Office method since I knew it would work. The code was written by myself! I’m guessing (never do this in code!) that anybody who understands anything would select either 2013 or 2016 as the version. What you think is plain to you may not be so evident to others.
If you want to be technical about versions, specifying the 2013 version as 15.0 and the 2016 version as 16.0 would be more correct if Microsoft was still using the versioning schema they used in the past. But what if, expecting they’ll give a version of 2013 or 2016, you’ve included code in the method that searches for files containing those versions or something else?
Here’s an example of when the $Version string could be used in a file path. It will fail or do something worse if someone passes a value that does not complete a folder name of Office2013 or Office2016, such as removing unexpected folders or changing items you didn’t account for.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$Version ) Get-ChildItem -Path “\SRV1InstallersOffice$Version” } PS> Install-Office -Version ‘15.0’ Get-ChildItem : Cannot find path ‘SRV1InstallersOffice15.0’ because it does not exist. At line:7 char:5 Get-ChildItem -Path “\SRV1InstallersOffice$Version” ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CategoryInfo : ObjectNotFound: (SRV1InstallersOffice15.0:String) [Get-ChildItem], ItemNotFoundExcep tion FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand
You may use PowerShell parameter validation to restrict the user’s input to what you anticipate.
Validation Attribute Using the ValidateSet Parameter
You may utilize a variety of parameter validation techniques. Run Get-Help about Functions Advanced Parameters for a complete list. In this case, the ValidateSet property would be the most appropriate.
You may use the ValidateSet validation property to define a list of values that can be used as parameter values. I’d want to make sure that the user may only provide these values since we’re only accounting for the strings 2013 or 2016. Otherwise, the function will instantly fail and tell them of the reason.
Under the original parameter keyword, you may add parameter validation characteristics. You can see an array of objects within the parentheses of the parameter property in this example; 2013 and 2016. A parameter validation property notifies PowerShell that only 2013 or 2016 are accepted options for Version. If you attempt to pass anything other than what’s in the set, you’ll get an error message stating that you only have a limited amount of possibilities.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateSet(‘2013′,’2016’)] [string]$Version ) Get-ChildItem -Path “\SRV1InstallersOffice$Version” } PS> Install-Office -Version 15.0 Install-Office : Cannot validate argument on parameter ‘Version’. The argument “15.0” does not belong to the set “2013,2016” specified by the ValidateSet attribute. Supply an argument that is in the set and then try the command again. At line:1 char:25 Install-Office -Version 15.0 ~~~~ CategoryInfo : InvalidData: (:) [Install-Office], ParameterBindingValidationException FullyQualifiedErrorId : ParameterArgumentValidationError,Install-Office
The ValidateSet property is a frequently used validation attribute. Check out the Functions Advanced Parameters help topic by executing Get-Help about Functions Advanced Parameters for a comprehensive list of all the ways parameter values may be controlled.
Sets of Parameters
Assume you only want specific PowerShell parameters to be used in conjunction with other parameters. Perhaps you’ve given the Install-Office function a Path argument. This route will install the installer in whatever version it is. You don’t want the user to utilize the Version option in this scenario.
You need Sets of Parameters.
Sets of parameters may be created that can only be used with other parameters from the same set. You may now get the path to the installer by using both the Version and the Path parameters in the code below.
[CmdletBinding()] function Install-Office [Parameter(Mandatory)] param([Parameter(Mandatory)] param([Parameter(Mand [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(Mandatory)], [string]$Version, [string]$Version, [string]$Version, [string] [string] Path $Path) Get-ChildItem -Path “SRV1InstallersOffice$Version” if ($Version) elseif ($Path) Get-ChildItem -Path $Path -Path -Path -Path -Path -Path -Path -Path -Path –
However, this creates a difficulty since the user might utilize both parameters. Furthermore, since both parameters are required, they will be obliged to utilize both even if it is not what you desire. To address this, we may group all of the parameters together in a parameter set like the one below.
[CmdletBinding()] function Install-Office [Parameter(Mandatory,ParameterSetName = ‘ByVersion’)] param([Parameter(Mandatory,ParameterSetName = ‘ByVersion’)] param([Parameter(Mand [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(Mandatory, parameterSetName = ‘ByPath’)], [string]$Version, [string]$Version, [string]$Version, [string]$Version, [string]$Version, [string]$ [string] Path $Path) Get-ChildItem -Path “SRV1InstallersOffice$Version” if ($Version) elseif ($Path) Get-ChildItem -Path $Path -Path -Path -Path -Path -Path -Path -Path -Path –
You may control groups of parameters together by specifying a parameter set name on each parameter.
Set of Default Parameters
What happens if a user attempts to run Install-Office without specifying any parameters? You’ll get a nice error notice if this isn’t accounted for.
There are no parameters configured.
You’ll need to establish a default parameter set in the CmdletBinding() section to correct this. Changing [CmdletBinding()] to [CmdletBinding(DefaultParameterSetName = ‘ByVersion’)] informs the function to choose a parameter set to use if no arguments were explicitly supplied.
Because it will be utilizing that parameter set, anytime you run Install-Office, it will request for the Version parameter.
Input to the Pipeline
You’ve been generating functions with a PowerShell parameter that can only be given using the standard -ParameterName Value syntax in the examples so far. However, as you’ve seen, PowerShell includes an easy pipeline that lets you transfer objects from one command to the next without having to use the “usual” syntax.
You “chain” instructions together using the pipe symbol | when using the pipeline, enabling a user to submit the output of a command like Get-Service to Start-Service as a shortcut for giving the Name argument to Start-Service.
Using a Loop the “Old” Way
You’re installing Office and have a Version argument in the custom function you’re dealing with. Let’s imagine you have a CSV file with a list of machine names in one row and the Office version that has to be installed on them in the second row. This is what the CSV file looks like:
PC1,2016,PC2,2013,PC3,2016,PC4,2016,PC5,2016,PC6,2016,PC7,2016,PC8,2016,PC9
On that machine, you’d want to install the version of Office that’s next to it.
First, you’ll need to add a ComputerName argument to the method so that each iteration of the function gets a distinct computer name. I’ve added a Write-Host instance to observe how the variables grow within the fictitious function, and I’ve built some pseudocode to mimic some code that may be in the fictional function.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter()] [string]$ComputerName ) <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” }
You may do this by reading the CSV file and giving the data for the computer name and version to the Install-Office function after you’ve added the ComputerName argument to the function.
Install-Office -Version
One or more parameters, also known as arguments, may be used with any PowerShell command. You’re not creating proper PowerShell code if you don’t use PowerShell parameters in your PowerShell functions!
You’ll cover almost every aspect of generating and using PowerShell parameters or arguments in this tutorial!
This is an excerpt from PowerShell for SysAdmins, which I wrote. Check it out if you want to learn PowerShell or pick up some tips and techniques.
What Is the Purpose of a Parameter?
When you first start writing functions, you’ll have the choice of including or excluding parameters, as well as the behavior of those arguments.
Consider the following scenario: you have a function that installs Microsoft Office. Perhaps it secretly invokes the Office installation inside the method. For our purposes, it doesn’t important what the function performs. With the name of the function and the script block, the basic function looks like this.
## Run the quiet installer here using the function Install-Office
In this case, you just ran Install-Office without any options and it took care of everything.
Whether the Install-Office function had parameters or not made no difference. It didn’t seem to have any necessary arguments; otherwise, PowerShell wouldn’t have allowed us execute it without one.
When Should You Use PowerShell Parameters?
There are several versions of Office. It’s possible that you’ll need to set up Office 2013 and 2016. There is currently no method to indicate this. Every time you wanted to modify the behavior, you could edit the function’s code.
You might, for instance, construct two distinct methods to install various versions.
‘I installed Office 2013.’ function Install-Office2013 Write-Host ‘Wheeeeeeeeeeeeeeeeee ‘I installed Office 2016.’ function Install-Office2016 Write-Host ‘Wheeeeeeeeeeeeeeeeee
It’s possible to do this, but it’s not scalable. It compels you to build a new function for each new version of Office that is released. When you don’t have to, you’ll have to duplicate a lot of code.
Instead, you’ll need a means to modify the function’s functionality by passing in various values at runtime. How do you go about doing this?
Yep! Parameters, or as some refer to them, arguments.
You must add at least one argument to this method if we want to install multiple versions of Office without having to change the code every time.
Before you rapidly come up with a PowerShell parameter, you must first ask yourself, “What is the smallest modification or changes you foresee to be required in this function?”
Keep in mind that you must restart this function without modifying any of the code inside it. The parameter in this case is presumably obvious; you need to provide a Version argument. When you’ve got a function with tens of lines of code, though, the solution isn’t always obvious. It will always assist if you answer the question as accurately as possible.
So you’re aware that a Version argument is required. So, what’s next? You may now add one, but there are various ways to skin that cat, as with any excellent programming language.
Based on my almost decade of PowerShell knowledge, I’ll teach you the “optimal” approach to generate parameters in this lesson. Keep in mind, though, that this isn’t the only method to create a parameter.
Positional parameters are anything that exists. You may use these parameters to provide values to parameters without having to mention the parameter name. Positional criteria are useful, but they aren’t “best practice.” Why? Because they are more difficult to understand, particularly when a function has a lot of arguments.
Using PowerShell to Create a Simple Parameter
To add a parameter to a function, you’ll need two things: a param block and the parameter itself. The param keyword is followed by a set of parentheses to create a param block.
[CmdletBinding()] function Install-Office ‘I installed Office 2016.’ param() Write-Host ‘Wheeeeeeeeeeeeeeeeee
The function’s real functionality hasn’t altered at this time. We’ve just finished installing some piping in preparation for the first parameter.
You’ll now construct the parameter once the param block is in place. The method I recommend for creating a parameter starts with the parameter block, then the type of parameter, and finally the parameter variable name.
[CmdletBinding()] function Install-Office [Parameter()] param([Parameter()] param([Parameter()] par Write-Host ‘I installed Office 2016.’ [string]$Version) ‘Wheeeeeeeeeeeeeeeeee
We’ve now generated a PowerShell function parameter, but what precisely happened?
Every parameter should include a parameter block, which is an optional but suggested component. It is “function plumbing,” similar to the param block, which prepares the parameter for adding further functionality. The kind of argument it is is defined on the second line.
We’ve opted to convert the Version option to a string in this situation. If an explicit type is defined, any value supplied to this argument will be “converted” to a string if it isn’t already one.
The kind is not required, although it is strongly recommended. Defining the sort of parameter it is will help you avoid a lot of unpleasant problems down the line. Have faith in me.
Now that you’ve established the option, you may use the Version argument in the Install-Office command to provide a version string, such as 2013. The value supplied to the Version parameter is also known as the arguments or values of parameters.
PS> Install-Office -Version 2013 I installed Office 2016. Yippee!
What exactly is going on here? You told it you wanted version 2013, yet it still says it’s installed version 2016. You must remember to rename the function’s code to a variable after adding an argument. When a parameter is supplied to a function, the variable is extended to the value that was passed.
Replace the static text 2016 with the Version parameter variable, changing the single quotes to double quotes to allow the variable to grow.
[CmdletBinding()] function Install-Office [Parameter()] param([Parameter()] param([Parameter()] par $Version) ([string]$Version) ([string]$Version) ( Host-Write “I downloaded and installed Office $Version. What a relief! “,,,,,,,,,,
As you can see, any value you provide the Version argument is sent into the method as the $Version variable.
Attribute for Mandatory Parameters
Remember how I said the [Parameter()] line was merely “function plumbing” that was required to get the function ready for further work? The extra labor I mentioned previously is adding parameter properties to a parameter.
A parameter does not have to be a variable placeholder. Parameter characteristics and parameter validity are concepts in PowerShell. The functionality of a parameter may be changed in a variety of ways using parameter attributes.
The Mandatory keyword, for example, is one of the most typical parameter properties you’ll use. You may call the Install-Office function without supplying the Version argument by default, and it will work perfectly. The parameter Version would be optional. Although the $Version variable within the code would not grow since there was no value, it would still execute.
When establishing a parameter, you’ll often want the user to utilize it all of the time. You’ll rely on the argument’s value somewhere in the function’s code, and if the parameter isn’t supplied, the function will fail. You wish to require the user to supply this parameter value to your function in certain instances. You’d want that parameter to be required.
Once you have the fundamental architecture in place, forcing users to utilize a parameter is straightforward. Within the parenthesis of the parameter, you must insert the term Mandatory. Once you’ve done that, calling the function without the argument will cause it to pause until a value is entered.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$Version ) Write-Host “I installed Office $Version. Yippee!” } PS> Install-Office cmdlet Install-Office at command pipeline position 1 Supply values for the following parameters: Version:
Until you supply a value for the Version argument, the function will wait. When you press Enter, PowerShell will run the function and then go on. If you specify a value for the argument, PowerShell will not ask you for it every time you run the command.
Validation Attributes for PowerShell Parameters
One of the most popular parameter features is to make a parameter obligatory, but you may also utilize parameter validation attributes. It’s always important in programming to limit user input as much as possible. Limiting the data that users (or even you!) may send to your functions or scripts can minimize redundant code in your function that must cater for a variety of scenarios.
Observational Learning
For example, I showed sending the number 2013 to the Install-Office method since I knew it would work. The code was written by myself! I’m guessing (never do this in code!) that anybody who understands anything would select either 2013 or 2016 as the version. What you think is plain to you may not be so evident to others.
If you want to be technical about versions, specifying the 2013 version as 15.0 and the 2016 version as 16.0 would be more correct if Microsoft was still using the versioning schema they used in the past. But what if, expecting they’ll give a version of 2013 or 2016, you’ve included code in the method that searches for files containing those versions or something else?
Here’s an example of when the $Version string could be used in a file path. It will fail or do something worse if someone passes a value that does not complete a folder name of Office2013 or Office2016, such as removing unexpected folders or changing items you didn’t account for.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$Version ) Get-ChildItem -Path “\SRV1InstallersOffice$Version” } PS> Install-Office -Version ‘15.0’ Get-ChildItem : Cannot find path ‘SRV1InstallersOffice15.0’ because it does not exist. At line:7 char:5 Get-ChildItem -Path “\SRV1InstallersOffice$Version” ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CategoryInfo : ObjectNotFound: (SRV1InstallersOffice15.0:String) [Get-ChildItem], ItemNotFoundExcep tion FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand
You may use PowerShell parameter validation to restrict the user’s input to what you anticipate.
Validation Attribute Using the ValidateSet Parameter
You may utilize a variety of parameter validation techniques. Run Get-Help about Functions Advanced Parameters for a complete list. In this case, the ValidateSet property would be the most appropriate.
You may use the ValidateSet validation property to define a list of values that can be used as parameter values. I’d want to make sure that the user may only provide these values since we’re only accounting for the strings 2013 or 2016. Otherwise, the function will instantly fail and tell them of the reason.
Under the original parameter keyword, you may add parameter validation characteristics. You can see an array of objects within the parentheses of the parameter property in this example; 2013 and 2016. A parameter validation property notifies PowerShell that only 2013 or 2016 are accepted options for Version. If you attempt to pass anything other than what’s in the set, you’ll get an error message stating that you only have a limited amount of possibilities.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateSet(‘2013′,’2016’)] [string]$Version ) Get-ChildItem -Path “\SRV1InstallersOffice$Version” } PS> Install-Office -Version 15.0 Install-Office : Cannot validate argument on parameter ‘Version’. The argument “15.0” does not belong to the set “2013,2016” specified by the ValidateSet attribute. Supply an argument that is in the set and then try the command again. At line:1 char:25 Install-Office -Version 15.0 ~~~~ CategoryInfo : InvalidData: (:) [Install-Office], ParameterBindingValidationException FullyQualifiedErrorId : ParameterArgumentValidationError,Install-Office
The ValidateSet property is a frequently used validation attribute. Check out the Functions Advanced Parameters help topic by executing Get-Help about Functions Advanced Parameters for a comprehensive list of all the ways parameter values may be controlled.
Sets of Parameters
Assume you only want specific PowerShell parameters to be used in conjunction with other parameters. Perhaps you’ve given the Install-Office function a Path argument. This route will install the installer in whatever version it is. You don’t want the user to utilize the Version option in this scenario.
You need Sets of Parameters.
Sets of parameters may be created that can only be used with other parameters from the same set. You may now get the path to the installer by using both the Version and the Path parameters in the code below.
[CmdletBinding()] function Install-Office [Parameter(Mandatory)] param([Parameter(Mandatory)] param([Parameter(Mand [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(Mandatory)], [string]$Version, [string]$Version, [string]$Version, [string] [string] Path $Path) Get-ChildItem -Path “SRV1InstallersOffice$Version” if ($Version) elseif ($Path) Get-ChildItem -Path $Path -Path -Path -Path -Path -Path -Path -Path -Path –
However, this creates a difficulty since the user might utilize both parameters. Furthermore, since both parameters are required, they will be obliged to utilize both even if it is not what you desire. To address this, we may group all of the parameters together in a parameter set like the one below.
[CmdletBinding()] function Install-Office [Parameter(Mandatory,ParameterSetName = ‘ByVersion’)] param([Parameter(Mandatory,ParameterSetName = ‘ByVersion’)] param([Parameter(Mand [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(Mandatory, parameterSetName = ‘ByPath’)], [string]$Version, [string]$Version, [string]$Version, [string]$Version, [string]$Version, [string]$ [string] Path $Path) Get-ChildItem -Path “SRV1InstallersOffice$Version” if ($Version) elseif ($Path) Get-ChildItem -Path $Path -Path -Path -Path -Path -Path -Path -Path -Path –
You may control groups of parameters together by specifying a parameter set name on each parameter.
Set of Default Parameters
What happens if a user attempts to run Install-Office without specifying any parameters? You’ll get a nice error notice if this isn’t accounted for.
There are no parameters configured.
You’ll need to establish a default parameter set in the CmdletBinding() section to correct this. Changing [CmdletBinding()] to [CmdletBinding(DefaultParameterSetName = ‘ByVersion’)] informs the function to choose a parameter set to use if no arguments were explicitly supplied.
Because it will be utilizing that parameter set, anytime you run Install-Office, it will request for the Version parameter.
Input to the Pipeline
You’ve been generating functions with a PowerShell parameter that can only be given using the standard -ParameterName Value syntax in the examples so far. However, as you’ve seen, PowerShell includes an easy pipeline that lets you transfer objects from one command to the next without having to use the “usual” syntax.
You “chain” instructions together using the pipe symbol | when using the pipeline, enabling a user to submit the output of a command like Get-Service to Start-Service as a shortcut for giving the Name argument to Start-Service.
Using a Loop the “Old” Way
You’re installing Office and have a Version argument in the custom function you’re dealing with. Let’s imagine you have a CSV file with a list of machine names in one row and the Office version that has to be installed on them in the second row. This is what the CSV file looks like:
PC1,2016,PC2,2013,PC3,2016,PC4,2016,PC5,2016,PC6,2016,PC7,2016,PC8,2016,PC9
On that machine, you’d want to install the version of Office that’s next to it.
First, you’ll need to add a ComputerName argument to the method so that each iteration of the function gets a distinct computer name. I’ve added a Write-Host instance to observe how the variables grow within the fictitious function, and I’ve built some pseudocode to mimic some code that may be in the fictional function.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter()] [string]$ComputerName ) <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” }
You may do this by reading the CSV file and giving the data for the computer name and version to the Install-Office function after you’ve added the ComputerName argument to the function.
$computers = Import-Csv -Path ‘C:ComputerOfficeVersions.csv’ foreach ($pc in $computers) { Install-Office -Version $_.Version -ComputerName $_.ComputerName }
Building Input to the Pipeline for Parameters
The “old” technique of reading the CSV rows and passing each row’s properties to the function is to use a loop to transfer each row’s properties to the function. Instead of using the foreach loop, you should utilize the pipeline in this section.
The function does not support the pipeline in its current state. It would seem logical to suppose that you could use the pipeline to feed each computer’s name and version to the function. We’re reading the CSV and handing it to Install-Office straight below, however this doesn’t work.
PS> Import-Csv -Path ‘C:ComputerOfficeVersions.csv’ | Install-Office
You may make all the assumptions you want, but that won’t make it work. When you know that Import-Csv sends the Version argument as an object property, you’ll see that we’re being requested for it. Why isn’t it functioning properly? Because you haven’t yet implemented pipeline support.
There are two kinds of Input to the Pipeline in a PowerShell function; ByValue (entire object) and ByPropertyName (a single object property). Which do you think is the best way to stitch together the output of Import-Csv to the input of Install-Office?
You could use the ByPropertyName function without any restructuring since Import-Csv already returns the properties Version and ComputerName because they’re columns in the CSV.
It’s a lot easier than you would expect to add pipeline support to a custom function. ValueFromPipeline or ValueFromPipelineByPropertyName are two keywords that indicate a parameter attribute.
You’ll use ValueFromPipelineByPropertyName in this example to link the ComputerName and Version properties returned from Import-Csv to the Version and ComputerName arguments of Install-Office.
Because we want to bind both of these arguments, you’ll need to add this keyword to each of them, as seen below, and then restart the function using the pipeline.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory,ValueFromPipelineByPropertyName)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [string]$ComputerName ) <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” }
That’s strange. It just processed the CSV’s final row. What exactly is going on? Because you skipped over a notion that isn’t necessary when generating functions without pipeline support, it just performed the function for the final row.
Remember to Include the Process Block!
When you need to create a function that involves pipeline support, you must include (at a minimum) an “embedded” block inside of your function called. process. This process block tells PowerShell that when receiving Input to the Pipeline, to run the function for every iteration. By default, it will only execute the last one.
Other blocks, such as begin and end, may theoretically be added, although scripters don’t utilize them nearly as often.
I’ll add a process block with the code inside to instruct PowerShell to run this function for every object that comes in.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory,ValueFromPipelineByPropertyName)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [string]$ComputerName ) process { <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” } }
The Version and ComputerName attributes received from Import-Csv were now provided to Install-Office and tied to the Version and ComputerName arguments.
Resources
Check out my blog article on PowerShell functions to learn more about how function parameters operate.
I also recommend that you have a look at my Pluralsight course, Building Advanced PowerShell Functions and Modules, for an in-depth look at PowerShell functions, function parameters, and PowerShell modules.
.Version -ComputerName
One or more parameters, also known as arguments, may be used with any PowerShell command. You’re not creating proper PowerShell code if you don’t use PowerShell parameters in your PowerShell functions!
You’ll cover almost every aspect of generating and using PowerShell parameters or arguments in this tutorial!
This is an excerpt from PowerShell for SysAdmins, which I wrote. Check it out if you want to learn PowerShell or pick up some tips and techniques.
What Is the Purpose of a Parameter?
When you first start writing functions, you’ll have the choice of including or excluding parameters, as well as the behavior of those arguments.
Consider the following scenario: you have a function that installs Microsoft Office. Perhaps it secretly invokes the Office installation inside the method. For our purposes, it doesn’t important what the function performs. With the name of the function and the script block, the basic function looks like this.
## Run the quiet installer here using the function Install-Office
In this case, you just ran Install-Office without any options and it took care of everything.
Whether the Install-Office function had parameters or not made no difference. It didn’t seem to have any necessary arguments; otherwise, PowerShell wouldn’t have allowed us execute it without one.
When Should You Use PowerShell Parameters?
There are several versions of Office. It’s possible that you’ll need to set up Office 2013 and 2016. There is currently no method to indicate this. Every time you wanted to modify the behavior, you could edit the function’s code.
You might, for instance, construct two distinct methods to install various versions.
‘I installed Office 2013.’ function Install-Office2013 Write-Host ‘Wheeeeeeeeeeeeeeeeee ‘I installed Office 2016.’ function Install-Office2016 Write-Host ‘Wheeeeeeeeeeeeeeeeee
It’s possible to do this, but it’s not scalable. It compels you to build a new function for each new version of Office that is released. When you don’t have to, you’ll have to duplicate a lot of code.
Instead, you’ll need a means to modify the function’s functionality by passing in various values at runtime. How do you go about doing this?
Yep! Parameters, or as some refer to them, arguments.
You must add at least one argument to this method if we want to install multiple versions of Office without having to change the code every time.
Before you rapidly come up with a PowerShell parameter, you must first ask yourself, “What is the smallest modification or changes you foresee to be required in this function?”
Keep in mind that you must restart this function without modifying any of the code inside it. The parameter in this case is presumably obvious; you need to provide a Version argument. When you’ve got a function with tens of lines of code, though, the solution isn’t always obvious. It will always assist if you answer the question as accurately as possible.
So you’re aware that a Version argument is required. So, what’s next? You may now add one, but there are various ways to skin that cat, as with any excellent programming language.
Based on my almost decade of PowerShell knowledge, I’ll teach you the “optimal” approach to generate parameters in this lesson. Keep in mind, though, that this isn’t the only method to create a parameter.
Positional parameters are anything that exists. You may use these parameters to provide values to parameters without having to mention the parameter name. Positional criteria are useful, but they aren’t “best practice.” Why? Because they are more difficult to understand, particularly when a function has a lot of arguments.
Using PowerShell to Create a Simple Parameter
To add a parameter to a function, you’ll need two things: a param block and the parameter itself. The param keyword is followed by a set of parentheses to create a param block.
[CmdletBinding()] function Install-Office ‘I installed Office 2016.’ param() Write-Host ‘Wheeeeeeeeeeeeeeeeee
The function’s real functionality hasn’t altered at this time. We’ve just finished installing some piping in preparation for the first parameter.
You’ll now construct the parameter once the param block is in place. The method I recommend for creating a parameter starts with the parameter block, then the type of parameter, and finally the parameter variable name.
[CmdletBinding()] function Install-Office [Parameter()] param([Parameter()] param([Parameter()] par Write-Host ‘I installed Office 2016.’ [string]$Version) ‘Wheeeeeeeeeeeeeeeeee
We’ve now generated a PowerShell function parameter, but what precisely happened?
Every parameter should include a parameter block, which is an optional but suggested component. It is “function plumbing,” similar to the param block, which prepares the parameter for adding further functionality. The kind of argument it is is defined on the second line.
We’ve opted to convert the Version option to a string in this situation. If an explicit type is defined, any value supplied to this argument will be “converted” to a string if it isn’t already one.
The kind is not required, although it is strongly recommended. Defining the sort of parameter it is will help you avoid a lot of unpleasant problems down the line. Have faith in me.
Now that you’ve established the option, you may use the Version argument in the Install-Office command to provide a version string, such as 2013. The value supplied to the Version parameter is also known as the arguments or values of parameters.
PS> Install-Office -Version 2013 I installed Office 2016. Yippee!
What exactly is going on here? You told it you wanted version 2013, yet it still says it’s installed version 2016. You must remember to rename the function’s code to a variable after adding an argument. When a parameter is supplied to a function, the variable is extended to the value that was passed.
Replace the static text 2016 with the Version parameter variable, changing the single quotes to double quotes to allow the variable to grow.
[CmdletBinding()] function Install-Office [Parameter()] param([Parameter()] param([Parameter()] par $Version) ([string]$Version) ([string]$Version) ( Host-Write “I downloaded and installed Office $Version. What a relief! “,,,,,,,,,,
As you can see, any value you provide the Version argument is sent into the method as the $Version variable.
Attribute for Mandatory Parameters
Remember how I said the [Parameter()] line was merely “function plumbing” that was required to get the function ready for further work? The extra labor I mentioned previously is adding parameter properties to a parameter.
A parameter does not have to be a variable placeholder. Parameter characteristics and parameter validity are concepts in PowerShell. The functionality of a parameter may be changed in a variety of ways using parameter attributes.
The Mandatory keyword, for example, is one of the most typical parameter properties you’ll use. You may call the Install-Office function without supplying the Version argument by default, and it will work perfectly. The parameter Version would be optional. Although the $Version variable within the code would not grow since there was no value, it would still execute.
When establishing a parameter, you’ll often want the user to utilize it all of the time. You’ll rely on the argument’s value somewhere in the function’s code, and if the parameter isn’t supplied, the function will fail. You wish to require the user to supply this parameter value to your function in certain instances. You’d want that parameter to be required.
Once you have the fundamental architecture in place, forcing users to utilize a parameter is straightforward. Within the parenthesis of the parameter, you must insert the term Mandatory. Once you’ve done that, calling the function without the argument will cause it to pause until a value is entered.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$Version ) Write-Host “I installed Office $Version. Yippee!” } PS> Install-Office cmdlet Install-Office at command pipeline position 1 Supply values for the following parameters: Version:
Until you supply a value for the Version argument, the function will wait. When you press Enter, PowerShell will run the function and then go on. If you specify a value for the argument, PowerShell will not ask you for it every time you run the command.
Validation Attributes for PowerShell Parameters
One of the most popular parameter features is to make a parameter obligatory, but you may also utilize parameter validation attributes. It’s always important in programming to limit user input as much as possible. Limiting the data that users (or even you!) may send to your functions or scripts can minimize redundant code in your function that must cater for a variety of scenarios.
Observational Learning
For example, I showed sending the number 2013 to the Install-Office method since I knew it would work. The code was written by myself! I’m guessing (never do this in code!) that anybody who understands anything would select either 2013 or 2016 as the version. What you think is plain to you may not be so evident to others.
If you want to be technical about versions, specifying the 2013 version as 15.0 and the 2016 version as 16.0 would be more correct if Microsoft was still using the versioning schema they used in the past. But what if, expecting they’ll give a version of 2013 or 2016, you’ve included code in the method that searches for files containing those versions or something else?
Here’s an example of when the $Version string could be used in a file path. It will fail or do something worse if someone passes a value that does not complete a folder name of Office2013 or Office2016, such as removing unexpected folders or changing items you didn’t account for.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$Version ) Get-ChildItem -Path “\SRV1InstallersOffice$Version” } PS> Install-Office -Version ‘15.0’ Get-ChildItem : Cannot find path ‘SRV1InstallersOffice15.0’ because it does not exist. At line:7 char:5 Get-ChildItem -Path “\SRV1InstallersOffice$Version” ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CategoryInfo : ObjectNotFound: (SRV1InstallersOffice15.0:String) [Get-ChildItem], ItemNotFoundExcep tion FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand
You may use PowerShell parameter validation to restrict the user’s input to what you anticipate.
Validation Attribute Using the ValidateSet Parameter
You may utilize a variety of parameter validation techniques. Run Get-Help about Functions Advanced Parameters for a complete list. In this case, the ValidateSet property would be the most appropriate.
You may use the ValidateSet validation property to define a list of values that can be used as parameter values. I’d want to make sure that the user may only provide these values since we’re only accounting for the strings 2013 or 2016. Otherwise, the function will instantly fail and tell them of the reason.
Under the original parameter keyword, you may add parameter validation characteristics. You can see an array of objects within the parentheses of the parameter property in this example; 2013 and 2016. A parameter validation property notifies PowerShell that only 2013 or 2016 are accepted options for Version. If you attempt to pass anything other than what’s in the set, you’ll get an error message stating that you only have a limited amount of possibilities.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateSet(‘2013′,’2016’)] [string]$Version ) Get-ChildItem -Path “\SRV1InstallersOffice$Version” } PS> Install-Office -Version 15.0 Install-Office : Cannot validate argument on parameter ‘Version’. The argument “15.0” does not belong to the set “2013,2016” specified by the ValidateSet attribute. Supply an argument that is in the set and then try the command again. At line:1 char:25 Install-Office -Version 15.0 ~~~~ CategoryInfo : InvalidData: (:) [Install-Office], ParameterBindingValidationException FullyQualifiedErrorId : ParameterArgumentValidationError,Install-Office
The ValidateSet property is a frequently used validation attribute. Check out the Functions Advanced Parameters help topic by executing Get-Help about Functions Advanced Parameters for a comprehensive list of all the ways parameter values may be controlled.
Sets of Parameters
Assume you only want specific PowerShell parameters to be used in conjunction with other parameters. Perhaps you’ve given the Install-Office function a Path argument. This route will install the installer in whatever version it is. You don’t want the user to utilize the Version option in this scenario.
You need Sets of Parameters.
Sets of parameters may be created that can only be used with other parameters from the same set. You may now get the path to the installer by using both the Version and the Path parameters in the code below.
[CmdletBinding()] function Install-Office [Parameter(Mandatory)] param([Parameter(Mandatory)] param([Parameter(Mand [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(Mandatory)], [string]$Version, [string]$Version, [string]$Version, [string] [string] Path $Path) Get-ChildItem -Path “SRV1InstallersOffice$Version” if ($Version) elseif ($Path) Get-ChildItem -Path $Path -Path -Path -Path -Path -Path -Path -Path -Path –
However, this creates a difficulty since the user might utilize both parameters. Furthermore, since both parameters are required, they will be obliged to utilize both even if it is not what you desire. To address this, we may group all of the parameters together in a parameter set like the one below.
[CmdletBinding()] function Install-Office [Parameter(Mandatory,ParameterSetName = ‘ByVersion’)] param([Parameter(Mandatory,ParameterSetName = ‘ByVersion’)] param([Parameter(Mand [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(Mandatory, parameterSetName = ‘ByPath’)], [string]$Version, [string]$Version, [string]$Version, [string]$Version, [string]$Version, [string]$ [string] Path $Path) Get-ChildItem -Path “SRV1InstallersOffice$Version” if ($Version) elseif ($Path) Get-ChildItem -Path $Path -Path -Path -Path -Path -Path -Path -Path -Path –
You may control groups of parameters together by specifying a parameter set name on each parameter.
Set of Default Parameters
What happens if a user attempts to run Install-Office without specifying any parameters? You’ll get a nice error notice if this isn’t accounted for.
There are no parameters configured.
You’ll need to establish a default parameter set in the CmdletBinding() section to correct this. Changing [CmdletBinding()] to [CmdletBinding(DefaultParameterSetName = ‘ByVersion’)] informs the function to choose a parameter set to use if no arguments were explicitly supplied.
Because it will be utilizing that parameter set, anytime you run Install-Office, it will request for the Version parameter.
Input to the Pipeline
You’ve been generating functions with a PowerShell parameter that can only be given using the standard -ParameterName Value syntax in the examples so far. However, as you’ve seen, PowerShell includes an easy pipeline that lets you transfer objects from one command to the next without having to use the “usual” syntax.
You “chain” instructions together using the pipe symbol | when using the pipeline, enabling a user to submit the output of a command like Get-Service to Start-Service as a shortcut for giving the Name argument to Start-Service.
Using a Loop the “Old” Way
You’re installing Office and have a Version argument in the custom function you’re dealing with. Let’s imagine you have a CSV file with a list of machine names in one row and the Office version that has to be installed on them in the second row. This is what the CSV file looks like:
PC1,2016,PC2,2013,PC3,2016,PC4,2016,PC5,2016,PC6,2016,PC7,2016,PC8,2016,PC9
On that machine, you’d want to install the version of Office that’s next to it.
First, you’ll need to add a ComputerName argument to the method so that each iteration of the function gets a distinct computer name. I’ve added a Write-Host instance to observe how the variables grow within the fictitious function, and I’ve built some pseudocode to mimic some code that may be in the fictional function.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter()] [string]$ComputerName ) <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” }
You may do this by reading the CSV file and giving the data for the computer name and version to the Install-Office function after you’ve added the ComputerName argument to the function.
$computers = Import-Csv -Path ‘C:ComputerOfficeVersions.csv’ foreach ($pc in $computers) { Install-Office -Version $_.Version -ComputerName $_.ComputerName }
Building Input to the Pipeline for Parameters
The “old” technique of reading the CSV rows and passing each row’s properties to the function is to use a loop to transfer each row’s properties to the function. Instead of using the foreach loop, you should utilize the pipeline in this section.
The function does not support the pipeline in its current state. It would seem logical to suppose that you could use the pipeline to feed each computer’s name and version to the function. We’re reading the CSV and handing it to Install-Office straight below, however this doesn’t work.
PS> Import-Csv -Path ‘C:ComputerOfficeVersions.csv’ | Install-Office
You may make all the assumptions you want, but that won’t make it work. When you know that Import-Csv sends the Version argument as an object property, you’ll see that we’re being requested for it. Why isn’t it functioning properly? Because you haven’t yet implemented pipeline support.
There are two kinds of Input to the Pipeline in a PowerShell function; ByValue (entire object) and ByPropertyName (a single object property). Which do you think is the best way to stitch together the output of Import-Csv to the input of Install-Office?
You could use the ByPropertyName function without any restructuring since Import-Csv already returns the properties Version and ComputerName because they’re columns in the CSV.
It’s a lot easier than you would expect to add pipeline support to a custom function. ValueFromPipeline or ValueFromPipelineByPropertyName are two keywords that indicate a parameter attribute.
You’ll use ValueFromPipelineByPropertyName in this example to link the ComputerName and Version properties returned from Import-Csv to the Version and ComputerName arguments of Install-Office.
Because we want to bind both of these arguments, you’ll need to add this keyword to each of them, as seen below, and then restart the function using the pipeline.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory,ValueFromPipelineByPropertyName)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [string]$ComputerName ) <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” }
That’s strange. It just processed the CSV’s final row. What exactly is going on? Because you skipped over a notion that isn’t necessary when generating functions without pipeline support, it just performed the function for the final row.
Remember to Include the Process Block!
When you need to create a function that involves pipeline support, you must include (at a minimum) an “embedded” block inside of your function called. process. This process block tells PowerShell that when receiving Input to the Pipeline, to run the function for every iteration. By default, it will only execute the last one.
Other blocks, such as begin and end, may theoretically be added, although scripters don’t utilize them nearly as often.
I’ll add a process block with the code inside to instruct PowerShell to run this function for every object that comes in.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory,ValueFromPipelineByPropertyName)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [string]$ComputerName ) process { <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” } }
The Version and ComputerName attributes received from Import-Csv were now provided to Install-Office and tied to the Version and ComputerName arguments.
Resources
Check out my blog article on PowerShell functions to learn more about how function parameters operate.
I also recommend that you have a look at my Pluralsight course, Building Advanced PowerShell Functions and Modules, for an in-depth look at PowerShell functions, function parameters, and PowerShell modules.
.ComputerName
One or more parameters, also known as arguments, may be used with any PowerShell command. You’re not creating proper PowerShell code if you don’t use PowerShell parameters in your PowerShell functions!
You’ll cover almost every aspect of generating and using PowerShell parameters or arguments in this tutorial!
This is an excerpt from PowerShell for SysAdmins, which I wrote. Check it out if you want to learn PowerShell or pick up some tips and techniques.
What Is the Purpose of a Parameter?
When you first start writing functions, you’ll have the choice of including or excluding parameters, as well as the behavior of those arguments.
Consider the following scenario: you have a function that installs Microsoft Office. Perhaps it secretly invokes the Office installation inside the method. For our purposes, it doesn’t important what the function performs. With the name of the function and the script block, the basic function looks like this.
## Run the quiet installer here using the function Install-Office
In this case, you just ran Install-Office without any options and it took care of everything.
Whether the Install-Office function had parameters or not made no difference. It didn’t seem to have any necessary arguments; otherwise, PowerShell wouldn’t have allowed us execute it without one.
When Should You Use PowerShell Parameters?
There are several versions of Office. It’s possible that you’ll need to set up Office 2013 and 2016. There is currently no method to indicate this. Every time you wanted to modify the behavior, you could edit the function’s code.
You might, for instance, construct two distinct methods to install various versions.
‘I installed Office 2013.’ function Install-Office2013 Write-Host ‘Wheeeeeeeeeeeeeeeeee ‘I installed Office 2016.’ function Install-Office2016 Write-Host ‘Wheeeeeeeeeeeeeeeeee
It’s possible to do this, but it’s not scalable. It compels you to build a new function for each new version of Office that is released. When you don’t have to, you’ll have to duplicate a lot of code.
Instead, you’ll need a means to modify the function’s functionality by passing in various values at runtime. How do you go about doing this?
Yep! Parameters, or as some refer to them, arguments.
You must add at least one argument to this method if we want to install multiple versions of Office without having to change the code every time.
Before you rapidly come up with a PowerShell parameter, you must first ask yourself, “What is the smallest modification or changes you foresee to be required in this function?”
Keep in mind that you must restart this function without modifying any of the code inside it. The parameter in this case is presumably obvious; you need to provide a Version argument. When you’ve got a function with tens of lines of code, though, the solution isn’t always obvious. It will always assist if you answer the question as accurately as possible.
So you’re aware that a Version argument is required. So, what’s next? You may now add one, but there are various ways to skin that cat, as with any excellent programming language.
Based on my almost decade of PowerShell knowledge, I’ll teach you the “optimal” approach to generate parameters in this lesson. Keep in mind, though, that this isn’t the only method to create a parameter.
Positional parameters are anything that exists. You may use these parameters to provide values to parameters without having to mention the parameter name. Positional criteria are useful, but they aren’t “best practice.” Why? Because they are more difficult to understand, particularly when a function has a lot of arguments.
Using PowerShell to Create a Simple Parameter
To add a parameter to a function, you’ll need two things: a param block and the parameter itself. The param keyword is followed by a set of parentheses to create a param block.
[CmdletBinding()] function Install-Office ‘I installed Office 2016.’ param() Write-Host ‘Wheeeeeeeeeeeeeeeeee
The function’s real functionality hasn’t altered at this time. We’ve just finished installing some piping in preparation for the first parameter.
You’ll now construct the parameter once the param block is in place. The method I recommend for creating a parameter starts with the parameter block, then the type of parameter, and finally the parameter variable name.
[CmdletBinding()] function Install-Office [Parameter()] param([Parameter()] param([Parameter()] par Write-Host ‘I installed Office 2016.’ [string]$Version) ‘Wheeeeeeeeeeeeeeeeee
We’ve now generated a PowerShell function parameter, but what precisely happened?
Every parameter should include a parameter block, which is an optional but suggested component. It is “function plumbing,” similar to the param block, which prepares the parameter for adding further functionality. The kind of argument it is is defined on the second line.
We’ve opted to convert the Version option to a string in this situation. If an explicit type is defined, any value supplied to this argument will be “converted” to a string if it isn’t already one.
The kind is not required, although it is strongly recommended. Defining the sort of parameter it is will help you avoid a lot of unpleasant problems down the line. Have faith in me.
Now that you’ve established the option, you may use the Version argument in the Install-Office command to provide a version string, such as 2013. The value supplied to the Version parameter is also known as the arguments or values of parameters.
PS> Install-Office -Version 2013 I installed Office 2016. Yippee!
What exactly is going on here? You told it you wanted version 2013, yet it still says it’s installed version 2016. You must remember to rename the function’s code to a variable after adding an argument. When a parameter is supplied to a function, the variable is extended to the value that was passed.
Replace the static text 2016 with the Version parameter variable, changing the single quotes to double quotes to allow the variable to grow.
[CmdletBinding()] function Install-Office [Parameter()] param([Parameter()] param([Parameter()] par $Version) ([string]$Version) ([string]$Version) ( Host-Write “I downloaded and installed Office $Version. What a relief! “,,,,,,,,,,
As you can see, any value you provide the Version argument is sent into the method as the $Version variable.
Attribute for Mandatory Parameters
Remember how I said the [Parameter()] line was merely “function plumbing” that was required to get the function ready for further work? The extra labor I mentioned previously is adding parameter properties to a parameter.
A parameter does not have to be a variable placeholder. Parameter characteristics and parameter validity are concepts in PowerShell. The functionality of a parameter may be changed in a variety of ways using parameter attributes.
The Mandatory keyword, for example, is one of the most typical parameter properties you’ll use. You may call the Install-Office function without supplying the Version argument by default, and it will work perfectly. The parameter Version would be optional. Although the $Version variable within the code would not grow since there was no value, it would still execute.
When establishing a parameter, you’ll often want the user to utilize it all of the time. You’ll rely on the argument’s value somewhere in the function’s code, and if the parameter isn’t supplied, the function will fail. You wish to require the user to supply this parameter value to your function in certain instances. You’d want that parameter to be required.
Once you have the fundamental architecture in place, forcing users to utilize a parameter is straightforward. Within the parenthesis of the parameter, you must insert the term Mandatory. Once you’ve done that, calling the function without the argument will cause it to pause until a value is entered.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$Version ) Write-Host “I installed Office $Version. Yippee!” } PS> Install-Office cmdlet Install-Office at command pipeline position 1 Supply values for the following parameters: Version:
Until you supply a value for the Version argument, the function will wait. When you press Enter, PowerShell will run the function and then go on. If you specify a value for the argument, PowerShell will not ask you for it every time you run the command.
Validation Attributes for PowerShell Parameters
One of the most popular parameter features is to make a parameter obligatory, but you may also utilize parameter validation attributes. It’s always important in programming to limit user input as much as possible. Limiting the data that users (or even you!) may send to your functions or scripts can minimize redundant code in your function that must cater for a variety of scenarios.
Observational Learning
For example, I showed sending the number 2013 to the Install-Office method since I knew it would work. The code was written by myself! I’m guessing (never do this in code!) that anybody who understands anything would select either 2013 or 2016 as the version. What you think is plain to you may not be so evident to others.
If you want to be technical about versions, specifying the 2013 version as 15.0 and the 2016 version as 16.0 would be more correct if Microsoft was still using the versioning schema they used in the past. But what if, expecting they’ll give a version of 2013 or 2016, you’ve included code in the method that searches for files containing those versions or something else?
Here’s an example of when the $Version string could be used in a file path. It will fail or do something worse if someone passes a value that does not complete a folder name of Office2013 or Office2016, such as removing unexpected folders or changing items you didn’t account for.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$Version ) Get-ChildItem -Path “\SRV1InstallersOffice$Version” } PS> Install-Office -Version ‘15.0’ Get-ChildItem : Cannot find path ‘SRV1InstallersOffice15.0’ because it does not exist. At line:7 char:5 Get-ChildItem -Path “\SRV1InstallersOffice$Version” ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CategoryInfo : ObjectNotFound: (SRV1InstallersOffice15.0:String) [Get-ChildItem], ItemNotFoundExcep tion FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand
You may use PowerShell parameter validation to restrict the user’s input to what you anticipate.
Validation Attribute Using the ValidateSet Parameter
You may utilize a variety of parameter validation techniques. Run Get-Help about Functions Advanced Parameters for a complete list. In this case, the ValidateSet property would be the most appropriate.
You may use the ValidateSet validation property to define a list of values that can be used as parameter values. I’d want to make sure that the user may only provide these values since we’re only accounting for the strings 2013 or 2016. Otherwise, the function will instantly fail and tell them of the reason.
Under the original parameter keyword, you may add parameter validation characteristics. You can see an array of objects within the parentheses of the parameter property in this example; 2013 and 2016. A parameter validation property notifies PowerShell that only 2013 or 2016 are accepted options for Version. If you attempt to pass anything other than what’s in the set, you’ll get an error message stating that you only have a limited amount of possibilities.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateSet(‘2013′,’2016’)] [string]$Version ) Get-ChildItem -Path “\SRV1InstallersOffice$Version” } PS> Install-Office -Version 15.0 Install-Office : Cannot validate argument on parameter ‘Version’. The argument “15.0” does not belong to the set “2013,2016” specified by the ValidateSet attribute. Supply an argument that is in the set and then try the command again. At line:1 char:25 Install-Office -Version 15.0 ~~~~ CategoryInfo : InvalidData: (:) [Install-Office], ParameterBindingValidationException FullyQualifiedErrorId : ParameterArgumentValidationError,Install-Office
The ValidateSet property is a frequently used validation attribute. Check out the Functions Advanced Parameters help topic by executing Get-Help about Functions Advanced Parameters for a comprehensive list of all the ways parameter values may be controlled.
Sets of Parameters
Assume you only want specific PowerShell parameters to be used in conjunction with other parameters. Perhaps you’ve given the Install-Office function a Path argument. This route will install the installer in whatever version it is. You don’t want the user to utilize the Version option in this scenario.
You need Sets of Parameters.
Sets of parameters may be created that can only be used with other parameters from the same set. You may now get the path to the installer by using both the Version and the Path parameters in the code below.
[CmdletBinding()] function Install-Office [Parameter(Mandatory)] param([Parameter(Mandatory)] param([Parameter(Mand [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(Mandatory)], [string]$Version, [string]$Version, [string]$Version, [string] [string] Path $Path) Get-ChildItem -Path “SRV1InstallersOffice$Version” if ($Version) elseif ($Path) Get-ChildItem -Path $Path -Path -Path -Path -Path -Path -Path -Path -Path –
However, this creates a difficulty since the user might utilize both parameters. Furthermore, since both parameters are required, they will be obliged to utilize both even if it is not what you desire. To address this, we may group all of the parameters together in a parameter set like the one below.
[CmdletBinding()] function Install-Office [Parameter(Mandatory,ParameterSetName = ‘ByVersion’)] param([Parameter(Mandatory,ParameterSetName = ‘ByVersion’)] param([Parameter(Mand [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(Mandatory, parameterSetName = ‘ByPath’)], [string]$Version, [string]$Version, [string]$Version, [string]$Version, [string]$Version, [string]$ [string] Path $Path) Get-ChildItem -Path “SRV1InstallersOffice$Version” if ($Version) elseif ($Path) Get-ChildItem -Path $Path -Path -Path -Path -Path -Path -Path -Path -Path –
You may control groups of parameters together by specifying a parameter set name on each parameter.
Set of Default Parameters
What happens if a user attempts to run Install-Office without specifying any parameters? You’ll get a nice error notice if this isn’t accounted for.
There are no parameters configured.
You’ll need to establish a default parameter set in the CmdletBinding() section to correct this. Changing [CmdletBinding()] to [CmdletBinding(DefaultParameterSetName = ‘ByVersion’)] informs the function to choose a parameter set to use if no arguments were explicitly supplied.
Because it will be utilizing that parameter set, anytime you run Install-Office, it will request for the Version parameter.
Input to the Pipeline
You’ve been generating functions with a PowerShell parameter that can only be given using the standard -ParameterName Value syntax in the examples so far. However, as you’ve seen, PowerShell includes an easy pipeline that lets you transfer objects from one command to the next without having to use the “usual” syntax.
You “chain” instructions together using the pipe symbol | when using the pipeline, enabling a user to submit the output of a command like Get-Service to Start-Service as a shortcut for giving the Name argument to Start-Service.
Using a Loop the “Old” Way
You’re installing Office and have a Version argument in the custom function you’re dealing with. Let’s imagine you have a CSV file with a list of machine names in one row and the Office version that has to be installed on them in the second row. This is what the CSV file looks like:
PC1,2016,PC2,2013,PC3,2016,PC4,2016,PC5,2016,PC6,2016,PC7,2016,PC8,2016,PC9
On that machine, you’d want to install the version of Office that’s next to it.
First, you’ll need to add a ComputerName argument to the method so that each iteration of the function gets a distinct computer name. I’ve added a Write-Host instance to observe how the variables grow within the fictitious function, and I’ve built some pseudocode to mimic some code that may be in the fictional function.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter()] [string]$ComputerName ) <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” }
You may do this by reading the CSV file and giving the data for the computer name and version to the Install-Office function after you’ve added the ComputerName argument to the function.
$computers = Import-Csv -Path ‘C:ComputerOfficeVersions.csv’ foreach ($pc in $computers) { Install-Office -Version $_.Version -ComputerName $_.ComputerName }
Building Input to the Pipeline for Parameters
The “old” technique of reading the CSV rows and passing each row’s properties to the function is to use a loop to transfer each row’s properties to the function. Instead of using the foreach loop, you should utilize the pipeline in this section.
The function does not support the pipeline in its current state. It would seem logical to suppose that you could use the pipeline to feed each computer’s name and version to the function. We’re reading the CSV and handing it to Install-Office straight below, however this doesn’t work.
PS> Import-Csv -Path ‘C:ComputerOfficeVersions.csv’ | Install-Office
You may make all the assumptions you want, but that won’t make it work. When you know that Import-Csv sends the Version argument as an object property, you’ll see that we’re being requested for it. Why isn’t it functioning properly? Because you haven’t yet implemented pipeline support.
There are two kinds of Input to the Pipeline in a PowerShell function; ByValue (entire object) and ByPropertyName (a single object property). Which do you think is the best way to stitch together the output of Import-Csv to the input of Install-Office?
You could use the ByPropertyName function without any restructuring since Import-Csv already returns the properties Version and ComputerName because they’re columns in the CSV.
It’s a lot easier than you would expect to add pipeline support to a custom function. ValueFromPipeline or ValueFromPipelineByPropertyName are two keywords that indicate a parameter attribute.
You’ll use ValueFromPipelineByPropertyName in this example to link the ComputerName and Version properties returned from Import-Csv to the Version and ComputerName arguments of Install-Office.
Because we want to bind both of these arguments, you’ll need to add this keyword to each of them, as seen below, and then restart the function using the pipeline.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory,ValueFromPipelineByPropertyName)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [string]$ComputerName ) <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” }
That’s strange. It just processed the CSV’s final row. What exactly is going on? Because you skipped over a notion that isn’t necessary when generating functions without pipeline support, it just performed the function for the final row.
Remember to Include the Process Block!
When you need to create a function that involves pipeline support, you must include (at a minimum) an “embedded” block inside of your function called. process. This process block tells PowerShell that when receiving Input to the Pipeline, to run the function for every iteration. By default, it will only execute the last one.
Other blocks, such as begin and end, may theoretically be added, although scripters don’t utilize them nearly as often.
I’ll add a process block with the code inside to instruct PowerShell to run this function for every object that comes in.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory,ValueFromPipelineByPropertyName)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [string]$ComputerName ) process { <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” } }
The Version and ComputerName attributes received from Import-Csv were now provided to Install-Office and tied to the Version and ComputerName arguments.
Resources
Check out my blog article on PowerShell functions to learn more about how function parameters operate.
I also recommend that you have a look at my Pluralsight course, Building Advanced PowerShell Functions and Modules, for an in-depth look at PowerShell functions, function parameters, and PowerShell modules.
.ComputerName
One or more parameters, also known as arguments, may be used with any PowerShell command. You’re not creating proper PowerShell code if you don’t use PowerShell parameters in your PowerShell functions!
You’ll cover almost every aspect of generating and using PowerShell parameters or arguments in this tutorial!
This is an excerpt from PowerShell for SysAdmins, which I wrote. Check it out if you want to learn PowerShell or pick up some tips and techniques.
What Is the Purpose of a Parameter?
When you first start writing functions, you’ll have the choice of including or excluding parameters, as well as the behavior of those arguments.
Consider the following scenario: you have a function that installs Microsoft Office. Perhaps it secretly invokes the Office installation inside the method. For our purposes, it doesn’t important what the function performs. With the name of the function and the script block, the basic function looks like this.
## Run the quiet installer here using the function Install-Office
In this case, you just ran Install-Office without any options and it took care of everything.
Whether the Install-Office function had parameters or not made no difference. It didn’t seem to have any necessary arguments; otherwise, PowerShell wouldn’t have allowed us execute it without one.
When Should You Use PowerShell Parameters?
There are several versions of Office. It’s possible that you’ll need to set up Office 2013 and 2016. There is currently no method to indicate this. Every time you wanted to modify the behavior, you could edit the function’s code.
You might, for instance, construct two distinct methods to install various versions.
‘I installed Office 2013.’ function Install-Office2013 Write-Host ‘Wheeeeeeeeeeeeeeeeee ‘I installed Office 2016.’ function Install-Office2016 Write-Host ‘Wheeeeeeeeeeeeeeeeee
It’s possible to do this, but it’s not scalable. It compels you to build a new function for each new version of Office that is released. When you don’t have to, you’ll have to duplicate a lot of code.
Instead, you’ll need a means to modify the function’s functionality by passing in various values at runtime. How do you go about doing this?
Yep! Parameters, or as some refer to them, arguments.
You must add at least one argument to this method if we want to install multiple versions of Office without having to change the code every time.
Before you rapidly come up with a PowerShell parameter, you must first ask yourself, “What is the smallest modification or changes you foresee to be required in this function?”
Keep in mind that you must restart this function without modifying any of the code inside it. The parameter in this case is presumably obvious; you need to provide a Version argument. When you’ve got a function with tens of lines of code, though, the solution isn’t always obvious. It will always assist if you answer the question as accurately as possible.
So you’re aware that a Version argument is required. So, what’s next? You may now add one, but there are various ways to skin that cat, as with any excellent programming language.
Based on my almost decade of PowerShell knowledge, I’ll teach you the “optimal” approach to generate parameters in this lesson. Keep in mind, though, that this isn’t the only method to create a parameter.
Positional parameters are anything that exists. You may use these parameters to provide values to parameters without having to mention the parameter name. Positional criteria are useful, but they aren’t “best practice.” Why? Because they are more difficult to understand, particularly when a function has a lot of arguments.
Using PowerShell to Create a Simple Parameter
To add a parameter to a function, you’ll need two things: a param block and the parameter itself. The param keyword is followed by a set of parentheses to create a param block.
[CmdletBinding()] function Install-Office ‘I installed Office 2016.’ param() Write-Host ‘Wheeeeeeeeeeeeeeeeee
The function’s real functionality hasn’t altered at this time. We’ve just finished installing some piping in preparation for the first parameter.
You’ll now construct the parameter once the param block is in place. The method I recommend for creating a parameter starts with the parameter block, then the type of parameter, and finally the parameter variable name.
[CmdletBinding()] function Install-Office [Parameter()] param([Parameter()] param([Parameter()] par Write-Host ‘I installed Office 2016.’ [string]$Version) ‘Wheeeeeeeeeeeeeeeeee
We’ve now generated a PowerShell function parameter, but what precisely happened?
Every parameter should include a parameter block, which is an optional but suggested component. It is “function plumbing,” similar to the param block, which prepares the parameter for adding further functionality. The kind of argument it is is defined on the second line.
We’ve opted to convert the Version option to a string in this situation. If an explicit type is defined, any value supplied to this argument will be “converted” to a string if it isn’t already one.
The kind is not required, although it is strongly recommended. Defining the sort of parameter it is will help you avoid a lot of unpleasant problems down the line. Have faith in me.
Now that you’ve established the option, you may use the Version argument in the Install-Office command to provide a version string, such as 2013. The value supplied to the Version parameter is also known as the arguments or values of parameters.
PS> Install-Office -Version 2013 I installed Office 2016. Yippee!
What exactly is going on here? You told it you wanted version 2013, yet it still says it’s installed version 2016. You must remember to rename the function’s code to a variable after adding an argument. When a parameter is supplied to a function, the variable is extended to the value that was passed.
Replace the static text 2016 with the Version parameter variable, changing the single quotes to double quotes to allow the variable to grow.
[CmdletBinding()] function Install-Office [Parameter()] param([Parameter()] param([Parameter()] par $Version) ([string]$Version) ([string]$Version) ( Host-Write “I downloaded and installed Office $Version. What a relief! “,,,,,,,,,,
As you can see, any value you provide the Version argument is sent into the method as the $Version variable.
Attribute for Mandatory Parameters
Remember how I said the [Parameter()] line was merely “function plumbing” that was required to get the function ready for further work? The extra labor I mentioned previously is adding parameter properties to a parameter.
A parameter does not have to be a variable placeholder. Parameter characteristics and parameter validity are concepts in PowerShell. The functionality of a parameter may be changed in a variety of ways using parameter attributes.
The Mandatory keyword, for example, is one of the most typical parameter properties you’ll use. You may call the Install-Office function without supplying the Version argument by default, and it will work perfectly. The parameter Version would be optional. Although the $Version variable within the code would not grow since there was no value, it would still execute.
When establishing a parameter, you’ll often want the user to utilize it all of the time. You’ll rely on the argument’s value somewhere in the function’s code, and if the parameter isn’t supplied, the function will fail. You wish to require the user to supply this parameter value to your function in certain instances. You’d want that parameter to be required.
Once you have the fundamental architecture in place, forcing users to utilize a parameter is straightforward. Within the parenthesis of the parameter, you must insert the term Mandatory. Once you’ve done that, calling the function without the argument will cause it to pause until a value is entered.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$Version ) Write-Host “I installed Office $Version. Yippee!” } PS> Install-Office cmdlet Install-Office at command pipeline position 1 Supply values for the following parameters: Version:
Until you supply a value for the Version argument, the function will wait. When you press Enter, PowerShell will run the function and then go on. If you specify a value for the argument, PowerShell will not ask you for it every time you run the command.
Validation Attributes for PowerShell Parameters
One of the most popular parameter features is to make a parameter obligatory, but you may also utilize parameter validation attributes. It’s always important in programming to limit user input as much as possible. Limiting the data that users (or even you!) may send to your functions or scripts can minimize redundant code in your function that must cater for a variety of scenarios.
Observational Learning
For example, I showed sending the number 2013 to the Install-Office method since I knew it would work. The code was written by myself! I’m guessing (never do this in code!) that anybody who understands anything would select either 2013 or 2016 as the version. What you think is plain to you may not be so evident to others.
If you want to be technical about versions, specifying the 2013 version as 15.0 and the 2016 version as 16.0 would be more correct if Microsoft was still using the versioning schema they used in the past. But what if, expecting they’ll give a version of 2013 or 2016, you’ve included code in the method that searches for files containing those versions or something else?
Here’s an example of when the $Version string could be used in a file path. It will fail or do something worse if someone passes a value that does not complete a folder name of Office2013 or Office2016, such as removing unexpected folders or changing items you didn’t account for.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$Version ) Get-ChildItem -Path “\SRV1InstallersOffice$Version” } PS> Install-Office -Version ‘15.0’ Get-ChildItem : Cannot find path ‘SRV1InstallersOffice15.0’ because it does not exist. At line:7 char:5 Get-ChildItem -Path “\SRV1InstallersOffice$Version” ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CategoryInfo : ObjectNotFound: (SRV1InstallersOffice15.0:String) [Get-ChildItem], ItemNotFoundExcep tion FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand
You may use PowerShell parameter validation to restrict the user’s input to what you anticipate.
Validation Attribute Using the ValidateSet Parameter
You may utilize a variety of parameter validation techniques. Run Get-Help about Functions Advanced Parameters for a complete list. In this case, the ValidateSet property would be the most appropriate.
You may use the ValidateSet validation property to define a list of values that can be used as parameter values. I’d want to make sure that the user may only provide these values since we’re only accounting for the strings 2013 or 2016. Otherwise, the function will instantly fail and tell them of the reason.
Under the original parameter keyword, you may add parameter validation characteristics. You can see an array of objects within the parentheses of the parameter property in this example; 2013 and 2016. A parameter validation property notifies PowerShell that only 2013 or 2016 are accepted options for Version. If you attempt to pass anything other than what’s in the set, you’ll get an error message stating that you only have a limited amount of possibilities.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateSet(‘2013′,’2016’)] [string]$Version ) Get-ChildItem -Path “\SRV1InstallersOffice$Version” } PS> Install-Office -Version 15.0 Install-Office : Cannot validate argument on parameter ‘Version’. The argument “15.0” does not belong to the set “2013,2016” specified by the ValidateSet attribute. Supply an argument that is in the set and then try the command again. At line:1 char:25 Install-Office -Version 15.0 ~~~~ CategoryInfo : InvalidData: (:) [Install-Office], ParameterBindingValidationException FullyQualifiedErrorId : ParameterArgumentValidationError,Install-Office
The ValidateSet property is a frequently used validation attribute. Check out the Functions Advanced Parameters help topic by executing Get-Help about Functions Advanced Parameters for a comprehensive list of all the ways parameter values may be controlled.
Sets of Parameters
Assume you only want specific PowerShell parameters to be used in conjunction with other parameters. Perhaps you’ve given the Install-Office function a Path argument. This route will install the installer in whatever version it is. You don’t want the user to utilize the Version option in this scenario.
You need Sets of Parameters.
Sets of parameters may be created that can only be used with other parameters from the same set. You may now get the path to the installer by using both the Version and the Path parameters in the code below.
[CmdletBinding()] function Install-Office [Parameter(Mandatory)] param([Parameter(Mandatory)] param([Parameter(Mand [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(Mandatory)], [string]$Version, [string]$Version, [string]$Version, [string] [string] Path $Path) Get-ChildItem -Path “SRV1InstallersOffice$Version” if ($Version) elseif ($Path) Get-ChildItem -Path $Path -Path -Path -Path -Path -Path -Path -Path -Path –
However, this creates a difficulty since the user might utilize both parameters. Furthermore, since both parameters are required, they will be obliged to utilize both even if it is not what you desire. To address this, we may group all of the parameters together in a parameter set like the one below.
[CmdletBinding()] function Install-Office [Parameter(Mandatory,ParameterSetName = ‘ByVersion’)] param([Parameter(Mandatory,ParameterSetName = ‘ByVersion’)] param([Parameter(Mand [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(Mandatory, parameterSetName = ‘ByPath’)], [string]$Version, [string]$Version, [string]$Version, [string]$Version, [string]$Version, [string]$ [string] Path $Path) Get-ChildItem -Path “SRV1InstallersOffice$Version” if ($Version) elseif ($Path) Get-ChildItem -Path $Path -Path -Path -Path -Path -Path -Path -Path -Path –
You may control groups of parameters together by specifying a parameter set name on each parameter.
Set of Default Parameters
What happens if a user attempts to run Install-Office without specifying any parameters? You’ll get a nice error notice if this isn’t accounted for.
There are no parameters configured.
You’ll need to establish a default parameter set in the CmdletBinding() section to correct this. Changing [CmdletBinding()] to [CmdletBinding(DefaultParameterSetName = ‘ByVersion’)] informs the function to choose a parameter set to use if no arguments were explicitly supplied.
Because it will be utilizing that parameter set, anytime you run Install-Office, it will request for the Version parameter.
Input to the Pipeline
You’ve been generating functions with a PowerShell parameter that can only be given using the standard -ParameterName Value syntax in the examples so far. However, as you’ve seen, PowerShell includes an easy pipeline that lets you transfer objects from one command to the next without having to use the “usual” syntax.
You “chain” instructions together using the pipe symbol | when using the pipeline, enabling a user to submit the output of a command like Get-Service to Start-Service as a shortcut for giving the Name argument to Start-Service.
Using a Loop the “Old” Way
You’re installing Office and have a Version argument in the custom function you’re dealing with. Let’s imagine you have a CSV file with a list of machine names in one row and the Office version that has to be installed on them in the second row. This is what the CSV file looks like:
PC1,2016,PC2,2013,PC3,2016,PC4,2016,PC5,2016,PC6,2016,PC7,2016,PC8,2016,PC9
On that machine, you’d want to install the version of Office that’s next to it.
First, you’ll need to add a ComputerName argument to the method so that each iteration of the function gets a distinct computer name. I’ve added a Write-Host instance to observe how the variables grow within the fictitious function, and I’ve built some pseudocode to mimic some code that may be in the fictional function.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter()] [string]$ComputerName ) <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” }
You may do this by reading the CSV file and giving the data for the computer name and version to the Install-Office function after you’ve added the ComputerName argument to the function.
$computers = Import-Csv -Path ‘C:ComputerOfficeVersions.csv’ foreach ($pc in $computers) { Install-Office -Version $_.Version -ComputerName $_.ComputerName }
Building Input to the Pipeline for Parameters
The “old” technique of reading the CSV rows and passing each row’s properties to the function is to use a loop to transfer each row’s properties to the function. Instead of using the foreach loop, you should utilize the pipeline in this section.
The function does not support the pipeline in its current state. It would seem logical to suppose that you could use the pipeline to feed each computer’s name and version to the function. We’re reading the CSV and handing it to Install-Office straight below, however this doesn’t work.
PS> Import-Csv -Path ‘C:ComputerOfficeVersions.csv’ | Install-Office
You may make all the assumptions you want, but that won’t make it work. When you know that Import-Csv sends the Version argument as an object property, you’ll see that we’re being requested for it. Why isn’t it functioning properly? Because you haven’t yet implemented pipeline support.
There are two kinds of Input to the Pipeline in a PowerShell function; ByValue (entire object) and ByPropertyName (a single object property). Which do you think is the best way to stitch together the output of Import-Csv to the input of Install-Office?
You could use the ByPropertyName function without any restructuring since Import-Csv already returns the properties Version and ComputerName because they’re columns in the CSV.
It’s a lot easier than you would expect to add pipeline support to a custom function. ValueFromPipeline or ValueFromPipelineByPropertyName are two keywords that indicate a parameter attribute.
You’ll use ValueFromPipelineByPropertyName in this example to link the ComputerName and Version properties returned from Import-Csv to the Version and ComputerName arguments of Install-Office.
Because we want to bind both of these arguments, you’ll need to add this keyword to each of them, as seen below, and then restart the function using the pipeline.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory,ValueFromPipelineByPropertyName)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [string]$ComputerName ) <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” }
That’s strange. It just processed the CSV’s final row. What exactly is going on? Because you skipped over a notion that isn’t necessary when generating functions without pipeline support, it just performed the function for the final row.
Remember to Include the Process Block!
When you need to create a function that involves pipeline support, you must include (at a minimum) an “embedded” block inside of your function called. process. This process block tells PowerShell that when receiving Input to the Pipeline, to run the function for every iteration. By default, it will only execute the last one.
Other blocks, such as begin and end, may theoretically be added, although scripters don’t utilize them nearly as often.
I’ll add a process block with the code inside to instruct PowerShell to run this function for every object that comes in.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory,ValueFromPipelineByPropertyName)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [string]$ComputerName ) process { <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” } }
The Version and ComputerName attributes received from Import-Csv were now provided to Install-Office and tied to the Version and ComputerName arguments.
Resources
Check out my blog article on PowerShell functions to learn more about how function parameters operate.
I also recommend that you have a look at my Pluralsight course, Building Advanced PowerShell Functions and Modules, for an in-depth look at PowerShell functions, function parameters, and PowerShell modules.
.ComputerName
One or more parameters, also known as arguments, may be used with any PowerShell command. You’re not creating proper PowerShell code if you don’t use PowerShell parameters in your PowerShell functions!
You’ll cover almost every aspect of generating and using PowerShell parameters or arguments in this tutorial!
This is an excerpt from PowerShell for SysAdmins, which I wrote. Check it out if you want to learn PowerShell or pick up some tips and techniques.
What Is the Purpose of a Parameter?
When you first start writing functions, you’ll have the choice of including or excluding parameters, as well as the behavior of those arguments.
Consider the following scenario: you have a function that installs Microsoft Office. Perhaps it secretly invokes the Office installation inside the method. For our purposes, it doesn’t important what the function performs. With the name of the function and the script block, the basic function looks like this.
## Run the quiet installer here using the function Install-Office
In this case, you just ran Install-Office without any options and it took care of everything.
Whether the Install-Office function had parameters or not made no difference. It didn’t seem to have any necessary arguments; otherwise, PowerShell wouldn’t have allowed us execute it without one.
When Should You Use PowerShell Parameters?
There are several versions of Office. It’s possible that you’ll need to set up Office 2013 and 2016. There is currently no method to indicate this. Every time you wanted to modify the behavior, you could edit the function’s code.
You might, for instance, construct two distinct methods to install various versions.
‘I installed Office 2013.’ function Install-Office2013 Write-Host ‘Wheeeeeeeeeeeeeeeeee ‘I installed Office 2016.’ function Install-Office2016 Write-Host ‘Wheeeeeeeeeeeeeeeeee
It’s possible to do this, but it’s not scalable. It compels you to build a new function for each new version of Office that is released. When you don’t have to, you’ll have to duplicate a lot of code.
Instead, you’ll need a means to modify the function’s functionality by passing in various values at runtime. How do you go about doing this?
Yep! Parameters, or as some refer to them, arguments.
You must add at least one argument to this method if we want to install multiple versions of Office without having to change the code every time.
Before you rapidly come up with a PowerShell parameter, you must first ask yourself, “What is the smallest modification or changes you foresee to be required in this function?”
Keep in mind that you must restart this function without modifying any of the code inside it. The parameter in this case is presumably obvious; you need to provide a Version argument. When you’ve got a function with tens of lines of code, though, the solution isn’t always obvious. It will always assist if you answer the question as accurately as possible.
So you’re aware that a Version argument is required. So, what’s next? You may now add one, but there are various ways to skin that cat, as with any excellent programming language.
Based on my almost decade of PowerShell knowledge, I’ll teach you the “optimal” approach to generate parameters in this lesson. Keep in mind, though, that this isn’t the only method to create a parameter.
Positional parameters are anything that exists. You may use these parameters to provide values to parameters without having to mention the parameter name. Positional criteria are useful, but they aren’t “best practice.” Why? Because they are more difficult to understand, particularly when a function has a lot of arguments.
Using PowerShell to Create a Simple Parameter
To add a parameter to a function, you’ll need two things: a param block and the parameter itself. The param keyword is followed by a set of parentheses to create a param block.
[CmdletBinding()] function Install-Office ‘I installed Office 2016.’ param() Write-Host ‘Wheeeeeeeeeeeeeeeeee
The function’s real functionality hasn’t altered at this time. We’ve just finished installing some piping in preparation for the first parameter.
You’ll now construct the parameter once the param block is in place. The method I recommend for creating a parameter starts with the parameter block, then the type of parameter, and finally the parameter variable name.
[CmdletBinding()] function Install-Office [Parameter()] param([Parameter()] param([Parameter()] par Write-Host ‘I installed Office 2016.’ [string]$Version) ‘Wheeeeeeeeeeeeeeeeee
We’ve now generated a PowerShell function parameter, but what precisely happened?
Every parameter should include a parameter block, which is an optional but suggested component. It is “function plumbing,” similar to the param block, which prepares the parameter for adding further functionality. The kind of argument it is is defined on the second line.
We’ve opted to convert the Version option to a string in this situation. If an explicit type is defined, any value supplied to this argument will be “converted” to a string if it isn’t already one.
The kind is not required, although it is strongly recommended. Defining the sort of parameter it is will help you avoid a lot of unpleasant problems down the line. Have faith in me.
Now that you’ve established the option, you may use the Version argument in the Install-Office command to provide a version string, such as 2013. The value supplied to the Version parameter is also known as the arguments or values of parameters.
PS> Install-Office -Version 2013 I installed Office 2016. Yippee!
What exactly is going on here? You told it you wanted version 2013, yet it still says it’s installed version 2016. You must remember to rename the function’s code to a variable after adding an argument. When a parameter is supplied to a function, the variable is extended to the value that was passed.
Replace the static text 2016 with the Version parameter variable, changing the single quotes to double quotes to allow the variable to grow.
[CmdletBinding()] function Install-Office [Parameter()] param([Parameter()] param([Parameter()] par $Version) ([string]$Version) ([string]$Version) ( Host-Write “I downloaded and installed Office $Version. What a relief! “,,,,,,,,,,
As you can see, any value you provide the Version argument is sent into the method as the $Version variable.
Attribute for Mandatory Parameters
Remember how I said the [Parameter()] line was merely “function plumbing” that was required to get the function ready for further work? The extra labor I mentioned previously is adding parameter properties to a parameter.
A parameter does not have to be a variable placeholder. Parameter characteristics and parameter validity are concepts in PowerShell. The functionality of a parameter may be changed in a variety of ways using parameter attributes.
The Mandatory keyword, for example, is one of the most typical parameter properties you’ll use. You may call the Install-Office function without supplying the Version argument by default, and it will work perfectly. The parameter Version would be optional. Although the $Version variable within the code would not grow since there was no value, it would still execute.
When establishing a parameter, you’ll often want the user to utilize it all of the time. You’ll rely on the argument’s value somewhere in the function’s code, and if the parameter isn’t supplied, the function will fail. You wish to require the user to supply this parameter value to your function in certain instances. You’d want that parameter to be required.
Once you have the fundamental architecture in place, forcing users to utilize a parameter is straightforward. Within the parenthesis of the parameter, you must insert the term Mandatory. Once you’ve done that, calling the function without the argument will cause it to pause until a value is entered.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$Version ) Write-Host “I installed Office $Version. Yippee!” } PS> Install-Office cmdlet Install-Office at command pipeline position 1 Supply values for the following parameters: Version:
Until you supply a value for the Version argument, the function will wait. When you press Enter, PowerShell will run the function and then go on. If you specify a value for the argument, PowerShell will not ask you for it every time you run the command.
Validation Attributes for PowerShell Parameters
One of the most popular parameter features is to make a parameter obligatory, but you may also utilize parameter validation attributes. It’s always important in programming to limit user input as much as possible. Limiting the data that users (or even you!) may send to your functions or scripts can minimize redundant code in your function that must cater for a variety of scenarios.
Observational Learning
For example, I showed sending the number 2013 to the Install-Office method since I knew it would work. The code was written by myself! I’m guessing (never do this in code!) that anybody who understands anything would select either 2013 or 2016 as the version. What you think is plain to you may not be so evident to others.
If you want to be technical about versions, specifying the 2013 version as 15.0 and the 2016 version as 16.0 would be more correct if Microsoft was still using the versioning schema they used in the past. But what if, expecting they’ll give a version of 2013 or 2016, you’ve included code in the method that searches for files containing those versions or something else?
Here’s an example of when the $Version string could be used in a file path. It will fail or do something worse if someone passes a value that does not complete a folder name of Office2013 or Office2016, such as removing unexpected folders or changing items you didn’t account for.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$Version ) Get-ChildItem -Path “\SRV1InstallersOffice$Version” } PS> Install-Office -Version ‘15.0’ Get-ChildItem : Cannot find path ‘SRV1InstallersOffice15.0’ because it does not exist. At line:7 char:5 Get-ChildItem -Path “\SRV1InstallersOffice$Version” ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CategoryInfo : ObjectNotFound: (SRV1InstallersOffice15.0:String) [Get-ChildItem], ItemNotFoundExcep tion FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand
You may use PowerShell parameter validation to restrict the user’s input to what you anticipate.
Validation Attribute Using the ValidateSet Parameter
You may utilize a variety of parameter validation techniques. Run Get-Help about Functions Advanced Parameters for a complete list. In this case, the ValidateSet property would be the most appropriate.
You may use the ValidateSet validation property to define a list of values that can be used as parameter values. I’d want to make sure that the user may only provide these values since we’re only accounting for the strings 2013 or 2016. Otherwise, the function will instantly fail and tell them of the reason.
Under the original parameter keyword, you may add parameter validation characteristics. You can see an array of objects within the parentheses of the parameter property in this example; 2013 and 2016. A parameter validation property notifies PowerShell that only 2013 or 2016 are accepted options for Version. If you attempt to pass anything other than what’s in the set, you’ll get an error message stating that you only have a limited amount of possibilities.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateSet(‘2013′,’2016’)] [string]$Version ) Get-ChildItem -Path “\SRV1InstallersOffice$Version” } PS> Install-Office -Version 15.0 Install-Office : Cannot validate argument on parameter ‘Version’. The argument “15.0” does not belong to the set “2013,2016” specified by the ValidateSet attribute. Supply an argument that is in the set and then try the command again. At line:1 char:25 Install-Office -Version 15.0 ~~~~ CategoryInfo : InvalidData: (:) [Install-Office], ParameterBindingValidationException FullyQualifiedErrorId : ParameterArgumentValidationError,Install-Office
The ValidateSet property is a frequently used validation attribute. Check out the Functions Advanced Parameters help topic by executing Get-Help about Functions Advanced Parameters for a comprehensive list of all the ways parameter values may be controlled.
Sets of Parameters
Assume you only want specific PowerShell parameters to be used in conjunction with other parameters. Perhaps you’ve given the Install-Office function a Path argument. This route will install the installer in whatever version it is. You don’t want the user to utilize the Version option in this scenario.
You need Sets of Parameters.
Sets of parameters may be created that can only be used with other parameters from the same set. You may now get the path to the installer by using both the Version and the Path parameters in the code below.
[CmdletBinding()] function Install-Office [Parameter(Mandatory)] param([Parameter(Mandatory)] param([Parameter(Mand [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(Mandatory)], [string]$Version, [string]$Version, [string]$Version, [string] [string] Path $Path) Get-ChildItem -Path “SRV1InstallersOffice$Version” if ($Version) elseif ($Path) Get-ChildItem -Path $Path -Path -Path -Path -Path -Path -Path -Path -Path –
However, this creates a difficulty since the user might utilize both parameters. Furthermore, since both parameters are required, they will be obliged to utilize both even if it is not what you desire. To address this, we may group all of the parameters together in a parameter set like the one below.
[CmdletBinding()] function Install-Office [Parameter(Mandatory,ParameterSetName = ‘ByVersion’)] param([Parameter(Mandatory,ParameterSetName = ‘ByVersion’)] param([Parameter(Mand [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(Mandatory, parameterSetName = ‘ByPath’)], [string]$Version, [string]$Version, [string]$Version, [string]$Version, [string]$Version, [string]$ [string] Path $Path) Get-ChildItem -Path “SRV1InstallersOffice$Version” if ($Version) elseif ($Path) Get-ChildItem -Path $Path -Path -Path -Path -Path -Path -Path -Path -Path –
You may control groups of parameters together by specifying a parameter set name on each parameter.
Set of Default Parameters
What happens if a user attempts to run Install-Office without specifying any parameters? You’ll get a nice error notice if this isn’t accounted for.
There are no parameters configured.
You’ll need to establish a default parameter set in the CmdletBinding() section to correct this. Changing [CmdletBinding()] to [CmdletBinding(DefaultParameterSetName = ‘ByVersion’)] informs the function to choose a parameter set to use if no arguments were explicitly supplied.
Because it will be utilizing that parameter set, anytime you run Install-Office, it will request for the Version parameter.
Input to the Pipeline
You’ve been generating functions with a PowerShell parameter that can only be given using the standard -ParameterName Value syntax in the examples so far. However, as you’ve seen, PowerShell includes an easy pipeline that lets you transfer objects from one command to the next without having to use the “usual” syntax.
You “chain” instructions together using the pipe symbol | when using the pipeline, enabling a user to submit the output of a command like Get-Service to Start-Service as a shortcut for giving the Name argument to Start-Service.
Using a Loop the “Old” Way
You’re installing Office and have a Version argument in the custom function you’re dealing with. Let’s imagine you have a CSV file with a list of machine names in one row and the Office version that has to be installed on them in the second row. This is what the CSV file looks like:
PC1,2016,PC2,2013,PC3,2016,PC4,2016,PC5,2016,PC6,2016,PC7,2016,PC8,2016,PC9
On that machine, you’d want to install the version of Office that’s next to it.
First, you’ll need to add a ComputerName argument to the method so that each iteration of the function gets a distinct computer name. I’ve added a Write-Host instance to observe how the variables grow within the fictitious function, and I’ve built some pseudocode to mimic some code that may be in the fictional function.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter()] [string]$ComputerName ) <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” }
You may do this by reading the CSV file and giving the data for the computer name and version to the Install-Office function after you’ve added the ComputerName argument to the function.
$computers = Import-Csv -Path ‘C:ComputerOfficeVersions.csv’ foreach ($pc in $computers) { Install-Office -Version $_.Version -ComputerName $_.ComputerName }
Building Input to the Pipeline for Parameters
The “old” technique of reading the CSV rows and passing each row’s properties to the function is to use a loop to transfer each row’s properties to the function. Instead of using the foreach loop, you should utilize the pipeline in this section.
The function does not support the pipeline in its current state. It would seem logical to suppose that you could use the pipeline to feed each computer’s name and version to the function. We’re reading the CSV and handing it to Install-Office straight below, however this doesn’t work.
PS> Import-Csv -Path ‘C:ComputerOfficeVersions.csv’ | Install-Office
You may make all the assumptions you want, but that won’t make it work. When you know that Import-Csv sends the Version argument as an object property, you’ll see that we’re being requested for it. Why isn’t it functioning properly? Because you haven’t yet implemented pipeline support.
There are two kinds of Input to the Pipeline in a PowerShell function; ByValue (entire object) and ByPropertyName (a single object property). Which do you think is the best way to stitch together the output of Import-Csv to the input of Install-Office?
You could use the ByPropertyName function without any restructuring since Import-Csv already returns the properties Version and ComputerName because they’re columns in the CSV.
It’s a lot easier than you would expect to add pipeline support to a custom function. ValueFromPipeline or ValueFromPipelineByPropertyName are two keywords that indicate a parameter attribute.
You’ll use ValueFromPipelineByPropertyName in this example to link the ComputerName and Version properties returned from Import-Csv to the Version and ComputerName arguments of Install-Office.
Because we want to bind both of these arguments, you’ll need to add this keyword to each of them, as seen below, and then restart the function using the pipeline.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory,ValueFromPipelineByPropertyName)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [string]$ComputerName ) <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” }
That’s strange. It just processed the CSV’s final row. What exactly is going on? Because you skipped over a notion that isn’t necessary when generating functions without pipeline support, it just performed the function for the final row.
Remember to Include the Process Block!
When you need to create a function that involves pipeline support, you must include (at a minimum) an “embedded” block inside of your function called. process. This process block tells PowerShell that when receiving Input to the Pipeline, to run the function for every iteration. By default, it will only execute the last one.
Other blocks, such as begin and end, may theoretically be added, although scripters don’t utilize them nearly as often.
I’ll add a process block with the code inside to instruct PowerShell to run this function for every object that comes in.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory,ValueFromPipelineByPropertyName)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [string]$ComputerName ) process { <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” } }
The Version and ComputerName attributes received from Import-Csv were now provided to Install-Office and tied to the Version and ComputerName arguments.
Resources
Check out my blog article on PowerShell functions to learn more about how function parameters operate.
I also recommend that you have a look at my Pluralsight course, Building Advanced PowerShell Functions and Modules, for an in-depth look at PowerShell functions, function parameters, and PowerShell modules.
.ComputerName
One or more parameters, also known as arguments, may be used with any PowerShell command. You’re not creating proper PowerShell code if you don’t use PowerShell parameters in your PowerShell functions!
You’ll cover almost every aspect of generating and using PowerShell parameters or arguments in this tutorial!
This is an excerpt from PowerShell for SysAdmins, which I wrote. Check it out if you want to learn PowerShell or pick up some tips and techniques.
What Is the Purpose of a Parameter?
When you first start writing functions, you’ll have the choice of including or excluding parameters, as well as the behavior of those arguments.
Consider the following scenario: you have a function that installs Microsoft Office. Perhaps it secretly invokes the Office installation inside the method. For our purposes, it doesn’t important what the function performs. With the name of the function and the script block, the basic function looks like this.
## Run the quiet installer here using the function Install-Office
In this case, you just ran Install-Office without any options and it took care of everything.
Whether the Install-Office function had parameters or not made no difference. It didn’t seem to have any necessary arguments; otherwise, PowerShell wouldn’t have allowed us execute it without one.
When Should You Use PowerShell Parameters?
There are several versions of Office. It’s possible that you’ll need to set up Office 2013 and 2016. There is currently no method to indicate this. Every time you wanted to modify the behavior, you could edit the function’s code.
You might, for instance, construct two distinct methods to install various versions.
‘I installed Office 2013.’ function Install-Office2013 Write-Host ‘Wheeeeeeeeeeeeeeeeee ‘I installed Office 2016.’ function Install-Office2016 Write-Host ‘Wheeeeeeeeeeeeeeeeee
It’s possible to do this, but it’s not scalable. It compels you to build a new function for each new version of Office that is released. When you don’t have to, you’ll have to duplicate a lot of code.
Instead, you’ll need a means to modify the function’s functionality by passing in various values at runtime. How do you go about doing this?
Yep! Parameters, or as some refer to them, arguments.
You must add at least one argument to this method if we want to install multiple versions of Office without having to change the code every time.
Before you rapidly come up with a PowerShell parameter, you must first ask yourself, “What is the smallest modification or changes you foresee to be required in this function?”
Keep in mind that you must restart this function without modifying any of the code inside it. The parameter in this case is presumably obvious; you need to provide a Version argument. When you’ve got a function with tens of lines of code, though, the solution isn’t always obvious. It will always assist if you answer the question as accurately as possible.
So you’re aware that a Version argument is required. So, what’s next? You may now add one, but there are various ways to skin that cat, as with any excellent programming language.
Based on my almost decade of PowerShell knowledge, I’ll teach you the “optimal” approach to generate parameters in this lesson. Keep in mind, though, that this isn’t the only method to create a parameter.
Positional parameters are anything that exists. You may use these parameters to provide values to parameters without having to mention the parameter name. Positional criteria are useful, but they aren’t “best practice.” Why? Because they are more difficult to understand, particularly when a function has a lot of arguments.
Using PowerShell to Create a Simple Parameter
To add a parameter to a function, you’ll need two things: a param block and the parameter itself. The param keyword is followed by a set of parentheses to create a param block.
[CmdletBinding()] function Install-Office ‘I installed Office 2016.’ param() Write-Host ‘Wheeeeeeeeeeeeeeeeee
The function’s real functionality hasn’t altered at this time. We’ve just finished installing some piping in preparation for the first parameter.
You’ll now construct the parameter once the param block is in place. The method I recommend for creating a parameter starts with the parameter block, then the type of parameter, and finally the parameter variable name.
[CmdletBinding()] function Install-Office [Parameter()] param([Parameter()] param([Parameter()] par Write-Host ‘I installed Office 2016.’ [string]$Version) ‘Wheeeeeeeeeeeeeeeeee
We’ve now generated a PowerShell function parameter, but what precisely happened?
Every parameter should include a parameter block, which is an optional but suggested component. It is “function plumbing,” similar to the param block, which prepares the parameter for adding further functionality. The kind of argument it is is defined on the second line.
We’ve opted to convert the Version option to a string in this situation. If an explicit type is defined, any value supplied to this argument will be “converted” to a string if it isn’t already one.
The kind is not required, although it is strongly recommended. Defining the sort of parameter it is will help you avoid a lot of unpleasant problems down the line. Have faith in me.
Now that you’ve established the option, you may use the Version argument in the Install-Office command to provide a version string, such as 2013. The value supplied to the Version parameter is also known as the arguments or values of parameters.
PS> Install-Office -Version 2013 I installed Office 2016. Yippee!
What exactly is going on here? You told it you wanted version 2013, yet it still says it’s installed version 2016. You must remember to rename the function’s code to a variable after adding an argument. When a parameter is supplied to a function, the variable is extended to the value that was passed.
Replace the static text 2016 with the Version parameter variable, changing the single quotes to double quotes to allow the variable to grow.
[CmdletBinding()] function Install-Office [Parameter()] param([Parameter()] param([Parameter()] par $Version) ([string]$Version) ([string]$Version) ( Host-Write “I downloaded and installed Office $Version. What a relief! “,,,,,,,,,,
As you can see, any value you provide the Version argument is sent into the method as the $Version variable.
Attribute for Mandatory Parameters
Remember how I said the [Parameter()] line was merely “function plumbing” that was required to get the function ready for further work? The extra labor I mentioned previously is adding parameter properties to a parameter.
A parameter does not have to be a variable placeholder. Parameter characteristics and parameter validity are concepts in PowerShell. The functionality of a parameter may be changed in a variety of ways using parameter attributes.
The Mandatory keyword, for example, is one of the most typical parameter properties you’ll use. You may call the Install-Office function without supplying the Version argument by default, and it will work perfectly. The parameter Version would be optional. Although the $Version variable within the code would not grow since there was no value, it would still execute.
When establishing a parameter, you’ll often want the user to utilize it all of the time. You’ll rely on the argument’s value somewhere in the function’s code, and if the parameter isn’t supplied, the function will fail. You wish to require the user to supply this parameter value to your function in certain instances. You’d want that parameter to be required.
Once you have the fundamental architecture in place, forcing users to utilize a parameter is straightforward. Within the parenthesis of the parameter, you must insert the term Mandatory. Once you’ve done that, calling the function without the argument will cause it to pause until a value is entered.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$Version ) Write-Host “I installed Office $Version. Yippee!” } PS> Install-Office cmdlet Install-Office at command pipeline position 1 Supply values for the following parameters: Version:
Until you supply a value for the Version argument, the function will wait. When you press Enter, PowerShell will run the function and then go on. If you specify a value for the argument, PowerShell will not ask you for it every time you run the command.
Validation Attributes for PowerShell Parameters
One of the most popular parameter features is to make a parameter obligatory, but you may also utilize parameter validation attributes. It’s always important in programming to limit user input as much as possible. Limiting the data that users (or even you!) may send to your functions or scripts can minimize redundant code in your function that must cater for a variety of scenarios.
Observational Learning
For example, I showed sending the number 2013 to the Install-Office method since I knew it would work. The code was written by myself! I’m guessing (never do this in code!) that anybody who understands anything would select either 2013 or 2016 as the version. What you think is plain to you may not be so evident to others.
If you want to be technical about versions, specifying the 2013 version as 15.0 and the 2016 version as 16.0 would be more correct if Microsoft was still using the versioning schema they used in the past. But what if, expecting they’ll give a version of 2013 or 2016, you’ve included code in the method that searches for files containing those versions or something else?
Here’s an example of when the $Version string could be used in a file path. It will fail or do something worse if someone passes a value that does not complete a folder name of Office2013 or Office2016, such as removing unexpected folders or changing items you didn’t account for.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$Version ) Get-ChildItem -Path “\SRV1InstallersOffice$Version” } PS> Install-Office -Version ‘15.0’ Get-ChildItem : Cannot find path ‘SRV1InstallersOffice15.0’ because it does not exist. At line:7 char:5 Get-ChildItem -Path “\SRV1InstallersOffice$Version” ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CategoryInfo : ObjectNotFound: (SRV1InstallersOffice15.0:String) [Get-ChildItem], ItemNotFoundExcep tion FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand
You may use PowerShell parameter validation to restrict the user’s input to what you anticipate.
Validation Attribute Using the ValidateSet Parameter
You may utilize a variety of parameter validation techniques. Run Get-Help about Functions Advanced Parameters for a complete list. In this case, the ValidateSet property would be the most appropriate.
You may use the ValidateSet validation property to define a list of values that can be used as parameter values. I’d want to make sure that the user may only provide these values since we’re only accounting for the strings 2013 or 2016. Otherwise, the function will instantly fail and tell them of the reason.
Under the original parameter keyword, you may add parameter validation characteristics. You can see an array of objects within the parentheses of the parameter property in this example; 2013 and 2016. A parameter validation property notifies PowerShell that only 2013 or 2016 are accepted options for Version. If you attempt to pass anything other than what’s in the set, you’ll get an error message stating that you only have a limited amount of possibilities.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateSet(‘2013′,’2016’)] [string]$Version ) Get-ChildItem -Path “\SRV1InstallersOffice$Version” } PS> Install-Office -Version 15.0 Install-Office : Cannot validate argument on parameter ‘Version’. The argument “15.0” does not belong to the set “2013,2016” specified by the ValidateSet attribute. Supply an argument that is in the set and then try the command again. At line:1 char:25 Install-Office -Version 15.0 ~~~~ CategoryInfo : InvalidData: (:) [Install-Office], ParameterBindingValidationException FullyQualifiedErrorId : ParameterArgumentValidationError,Install-Office
The ValidateSet property is a frequently used validation attribute. Check out the Functions Advanced Parameters help topic by executing Get-Help about Functions Advanced Parameters for a comprehensive list of all the ways parameter values may be controlled.
Sets of Parameters
Assume you only want specific PowerShell parameters to be used in conjunction with other parameters. Perhaps you’ve given the Install-Office function a Path argument. This route will install the installer in whatever version it is. You don’t want the user to utilize the Version option in this scenario.
You need Sets of Parameters.
Sets of parameters may be created that can only be used with other parameters from the same set. You may now get the path to the installer by using both the Version and the Path parameters in the code below.
[CmdletBinding()] function Install-Office [Parameter(Mandatory)] param([Parameter(Mandatory)] param([Parameter(Mand [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(Mandatory)], [string]$Version, [string]$Version, [string]$Version, [string] [string] Path $Path) Get-ChildItem -Path “SRV1InstallersOffice$Version” if ($Version) elseif ($Path) Get-ChildItem -Path $Path -Path -Path -Path -Path -Path -Path -Path -Path –
However, this creates a difficulty since the user might utilize both parameters. Furthermore, since both parameters are required, they will be obliged to utilize both even if it is not what you desire. To address this, we may group all of the parameters together in a parameter set like the one below.
[CmdletBinding()] function Install-Office [Parameter(Mandatory,ParameterSetName = ‘ByVersion’)] param([Parameter(Mandatory,ParameterSetName = ‘ByVersion’)] param([Parameter(Mand [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(Mandatory, parameterSetName = ‘ByPath’)], [string]$Version, [string]$Version, [string]$Version, [string]$Version, [string]$Version, [string]$ [string] Path $Path) Get-ChildItem -Path “SRV1InstallersOffice$Version” if ($Version) elseif ($Path) Get-ChildItem -Path $Path -Path -Path -Path -Path -Path -Path -Path -Path –
You may control groups of parameters together by specifying a parameter set name on each parameter.
Set of Default Parameters
What happens if a user attempts to run Install-Office without specifying any parameters? You’ll get a nice error notice if this isn’t accounted for.
There are no parameters configured.
You’ll need to establish a default parameter set in the CmdletBinding() section to correct this. Changing [CmdletBinding()] to [CmdletBinding(DefaultParameterSetName = ‘ByVersion’)] informs the function to choose a parameter set to use if no arguments were explicitly supplied.
Because it will be utilizing that parameter set, anytime you run Install-Office, it will request for the Version parameter.
Input to the Pipeline
You’ve been generating functions with a PowerShell parameter that can only be given using the standard -ParameterName Value syntax in the examples so far. However, as you’ve seen, PowerShell includes an easy pipeline that lets you transfer objects from one command to the next without having to use the “usual” syntax.
You “chain” instructions together using the pipe symbol | when using the pipeline, enabling a user to submit the output of a command like Get-Service to Start-Service as a shortcut for giving the Name argument to Start-Service.
Using a Loop the “Old” Way
You’re installing Office and have a Version argument in the custom function you’re dealing with. Let’s imagine you have a CSV file with a list of machine names in one row and the Office version that has to be installed on them in the second row. This is what the CSV file looks like:
PC1,2016,PC2,2013,PC3,2016,PC4,2016,PC5,2016,PC6,2016,PC7,2016,PC8,2016,PC9
On that machine, you’d want to install the version of Office that’s next to it.
First, you’ll need to add a ComputerName argument to the method so that each iteration of the function gets a distinct computer name. I’ve added a Write-Host instance to observe how the variables grow within the fictitious function, and I’ve built some pseudocode to mimic some code that may be in the fictional function.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter()] [string]$ComputerName ) <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” }
You may do this by reading the CSV file and giving the data for the computer name and version to the Install-Office function after you’ve added the ComputerName argument to the function.
$computers = Import-Csv -Path ‘C:ComputerOfficeVersions.csv’ foreach ($pc in $computers) { Install-Office -Version $_.Version -ComputerName $_.ComputerName }
Building Input to the Pipeline for Parameters
The “old” technique of reading the CSV rows and passing each row’s properties to the function is to use a loop to transfer each row’s properties to the function. Instead of using the foreach loop, you should utilize the pipeline in this section.
The function does not support the pipeline in its current state. It would seem logical to suppose that you could use the pipeline to feed each computer’s name and version to the function. We’re reading the CSV and handing it to Install-Office straight below, however this doesn’t work.
PS> Import-Csv -Path ‘C:ComputerOfficeVersions.csv’ | Install-Office
You may make all the assumptions you want, but that won’t make it work. When you know that Import-Csv sends the Version argument as an object property, you’ll see that we’re being requested for it. Why isn’t it functioning properly? Because you haven’t yet implemented pipeline support.
There are two kinds of Input to the Pipeline in a PowerShell function; ByValue (entire object) and ByPropertyName (a single object property). Which do you think is the best way to stitch together the output of Import-Csv to the input of Install-Office?
You could use the ByPropertyName function without any restructuring since Import-Csv already returns the properties Version and ComputerName because they’re columns in the CSV.
It’s a lot easier than you would expect to add pipeline support to a custom function. ValueFromPipeline or ValueFromPipelineByPropertyName are two keywords that indicate a parameter attribute.
You’ll use ValueFromPipelineByPropertyName in this example to link the ComputerName and Version properties returned from Import-Csv to the Version and ComputerName arguments of Install-Office.
Because we want to bind both of these arguments, you’ll need to add this keyword to each of them, as seen below, and then restart the function using the pipeline.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory,ValueFromPipelineByPropertyName)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [string]$ComputerName ) <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” }
That’s strange. It just processed the CSV’s final row. What exactly is going on? Because you skipped over a notion that isn’t necessary when generating functions without pipeline support, it just performed the function for the final row.
Remember to Include the Process Block!
When you need to create a function that involves pipeline support, you must include (at a minimum) an “embedded” block inside of your function called. process. This process block tells PowerShell that when receiving Input to the Pipeline, to run the function for every iteration. By default, it will only execute the last one.
Other blocks, such as begin and end, may theoretically be added, although scripters don’t utilize them nearly as often.
I’ll add a process block with the code inside to instruct PowerShell to run this function for every object that comes in.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory,ValueFromPipelineByPropertyName)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [string]$ComputerName ) process { <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” } }
The Version and ComputerName attributes received from Import-Csv were now provided to Install-Office and tied to the Version and ComputerName arguments.
Resources
Check out my blog article on PowerShell functions to learn more about how function parameters operate.
I also recommend that you have a look at my Pluralsight course, Building Advanced PowerShell Functions and Modules, for an in-depth look at PowerShell functions, function parameters, and PowerShell modules.
.ComputerName
One or more parameters, also known as arguments, may be used with any PowerShell command. You’re not creating proper PowerShell code if you don’t use PowerShell parameters in your PowerShell functions!
You’ll cover almost every aspect of generating and using PowerShell parameters or arguments in this tutorial!
This is an excerpt from PowerShell for SysAdmins, which I wrote. Check it out if you want to learn PowerShell or pick up some tips and techniques.
What Is the Purpose of a Parameter?
When you first start writing functions, you’ll have the choice of including or excluding parameters, as well as the behavior of those arguments.
Consider the following scenario: you have a function that installs Microsoft Office. Perhaps it secretly invokes the Office installation inside the method. For our purposes, it doesn’t important what the function performs. With the name of the function and the script block, the basic function looks like this.
## Run the quiet installer here using the function Install-Office
In this case, you just ran Install-Office without any options and it took care of everything.
Whether the Install-Office function had parameters or not made no difference. It didn’t seem to have any necessary arguments; otherwise, PowerShell wouldn’t have allowed us execute it without one.
When Should You Use PowerShell Parameters?
There are several versions of Office. It’s possible that you’ll need to set up Office 2013 and 2016. There is currently no method to indicate this. Every time you wanted to modify the behavior, you could edit the function’s code.
You might, for instance, construct two distinct methods to install various versions.
‘I installed Office 2013.’ function Install-Office2013 Write-Host ‘Wheeeeeeeeeeeeeeeeee ‘I installed Office 2016.’ function Install-Office2016 Write-Host ‘Wheeeeeeeeeeeeeeeeee
It’s possible to do this, but it’s not scalable. It compels you to build a new function for each new version of Office that is released. When you don’t have to, you’ll have to duplicate a lot of code.
Instead, you’ll need a means to modify the function’s functionality by passing in various values at runtime. How do you go about doing this?
Yep! Parameters, or as some refer to them, arguments.
You must add at least one argument to this method if we want to install multiple versions of Office without having to change the code every time.
Before you rapidly come up with a PowerShell parameter, you must first ask yourself, “What is the smallest modification or changes you foresee to be required in this function?”
Keep in mind that you must restart this function without modifying any of the code inside it. The parameter in this case is presumably obvious; you need to provide a Version argument. When you’ve got a function with tens of lines of code, though, the solution isn’t always obvious. It will always assist if you answer the question as accurately as possible.
So you’re aware that a Version argument is required. So, what’s next? You may now add one, but there are various ways to skin that cat, as with any excellent programming language.
Based on my almost decade of PowerShell knowledge, I’ll teach you the “optimal” approach to generate parameters in this lesson. Keep in mind, though, that this isn’t the only method to create a parameter.
Positional parameters are anything that exists. You may use these parameters to provide values to parameters without having to mention the parameter name. Positional criteria are useful, but they aren’t “best practice.” Why? Because they are more difficult to understand, particularly when a function has a lot of arguments.
Using PowerShell to Create a Simple Parameter
To add a parameter to a function, you’ll need two things: a param block and the parameter itself. The param keyword is followed by a set of parentheses to create a param block.
[CmdletBinding()] function Install-Office ‘I installed Office 2016.’ param() Write-Host ‘Wheeeeeeeeeeeeeeeeee
The function’s real functionality hasn’t altered at this time. We’ve just finished installing some piping in preparation for the first parameter.
You’ll now construct the parameter once the param block is in place. The method I recommend for creating a parameter starts with the parameter block, then the type of parameter, and finally the parameter variable name.
[CmdletBinding()] function Install-Office [Parameter()] param([Parameter()] param([Parameter()] par Write-Host ‘I installed Office 2016.’ [string]$Version) ‘Wheeeeeeeeeeeeeeeeee
We’ve now generated a PowerShell function parameter, but what precisely happened?
Every parameter should include a parameter block, which is an optional but suggested component. It is “function plumbing,” similar to the param block, which prepares the parameter for adding further functionality. The kind of argument it is is defined on the second line.
We’ve opted to convert the Version option to a string in this situation. If an explicit type is defined, any value supplied to this argument will be “converted” to a string if it isn’t already one.
The kind is not required, although it is strongly recommended. Defining the sort of parameter it is will help you avoid a lot of unpleasant problems down the line. Have faith in me.
Now that you’ve established the option, you may use the Version argument in the Install-Office command to provide a version string, such as 2013. The value supplied to the Version parameter is also known as the arguments or values of parameters.
PS> Install-Office -Version 2013 I installed Office 2016. Yippee!
What exactly is going on here? You told it you wanted version 2013, yet it still says it’s installed version 2016. You must remember to rename the function’s code to a variable after adding an argument. When a parameter is supplied to a function, the variable is extended to the value that was passed.
Replace the static text 2016 with the Version parameter variable, changing the single quotes to double quotes to allow the variable to grow.
[CmdletBinding()] function Install-Office [Parameter()] param([Parameter()] param([Parameter()] par $Version) ([string]$Version) ([string]$Version) ( Host-Write “I downloaded and installed Office $Version. What a relief! “,,,,,,,,,,
As you can see, any value you provide the Version argument is sent into the method as the $Version variable.
Attribute for Mandatory Parameters
Remember how I said the [Parameter()] line was merely “function plumbing” that was required to get the function ready for further work? The extra labor I mentioned previously is adding parameter properties to a parameter.
A parameter does not have to be a variable placeholder. Parameter characteristics and parameter validity are concepts in PowerShell. The functionality of a parameter may be changed in a variety of ways using parameter attributes.
The Mandatory keyword, for example, is one of the most typical parameter properties you’ll use. You may call the Install-Office function without supplying the Version argument by default, and it will work perfectly. The parameter Version would be optional. Although the $Version variable within the code would not grow since there was no value, it would still execute.
When establishing a parameter, you’ll often want the user to utilize it all of the time. You’ll rely on the argument’s value somewhere in the function’s code, and if the parameter isn’t supplied, the function will fail. You wish to require the user to supply this parameter value to your function in certain instances. You’d want that parameter to be required.
Once you have the fundamental architecture in place, forcing users to utilize a parameter is straightforward. Within the parenthesis of the parameter, you must insert the term Mandatory. Once you’ve done that, calling the function without the argument will cause it to pause until a value is entered.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$Version ) Write-Host “I installed Office $Version. Yippee!” } PS> Install-Office cmdlet Install-Office at command pipeline position 1 Supply values for the following parameters: Version:
Until you supply a value for the Version argument, the function will wait. When you press Enter, PowerShell will run the function and then go on. If you specify a value for the argument, PowerShell will not ask you for it every time you run the command.
Validation Attributes for PowerShell Parameters
One of the most popular parameter features is to make a parameter obligatory, but you may also utilize parameter validation attributes. It’s always important in programming to limit user input as much as possible. Limiting the data that users (or even you!) may send to your functions or scripts can minimize redundant code in your function that must cater for a variety of scenarios.
Observational Learning
For example, I showed sending the number 2013 to the Install-Office method since I knew it would work. The code was written by myself! I’m guessing (never do this in code!) that anybody who understands anything would select either 2013 or 2016 as the version. What you think is plain to you may not be so evident to others.
If you want to be technical about versions, specifying the 2013 version as 15.0 and the 2016 version as 16.0 would be more correct if Microsoft was still using the versioning schema they used in the past. But what if, expecting they’ll give a version of 2013 or 2016, you’ve included code in the method that searches for files containing those versions or something else?
Here’s an example of when the $Version string could be used in a file path. It will fail or do something worse if someone passes a value that does not complete a folder name of Office2013 or Office2016, such as removing unexpected folders or changing items you didn’t account for.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$Version ) Get-ChildItem -Path “\SRV1InstallersOffice$Version” } PS> Install-Office -Version ‘15.0’ Get-ChildItem : Cannot find path ‘SRV1InstallersOffice15.0’ because it does not exist. At line:7 char:5 Get-ChildItem -Path “\SRV1InstallersOffice$Version” ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CategoryInfo : ObjectNotFound: (SRV1InstallersOffice15.0:String) [Get-ChildItem], ItemNotFoundExcep tion FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand
You may use PowerShell parameter validation to restrict the user’s input to what you anticipate.
Validation Attribute Using the ValidateSet Parameter
You may utilize a variety of parameter validation techniques. Run Get-Help about Functions Advanced Parameters for a complete list. In this case, the ValidateSet property would be the most appropriate.
You may use the ValidateSet validation property to define a list of values that can be used as parameter values. I’d want to make sure that the user may only provide these values since we’re only accounting for the strings 2013 or 2016. Otherwise, the function will instantly fail and tell them of the reason.
Under the original parameter keyword, you may add parameter validation characteristics. You can see an array of objects within the parentheses of the parameter property in this example; 2013 and 2016. A parameter validation property notifies PowerShell that only 2013 or 2016 are accepted options for Version. If you attempt to pass anything other than what’s in the set, you’ll get an error message stating that you only have a limited amount of possibilities.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateSet(‘2013′,’2016’)] [string]$Version ) Get-ChildItem -Path “\SRV1InstallersOffice$Version” } PS> Install-Office -Version 15.0 Install-Office : Cannot validate argument on parameter ‘Version’. The argument “15.0” does not belong to the set “2013,2016” specified by the ValidateSet attribute. Supply an argument that is in the set and then try the command again. At line:1 char:25 Install-Office -Version 15.0 ~~~~ CategoryInfo : InvalidData: (:) [Install-Office], ParameterBindingValidationException FullyQualifiedErrorId : ParameterArgumentValidationError,Install-Office
The ValidateSet property is a frequently used validation attribute. Check out the Functions Advanced Parameters help topic by executing Get-Help about Functions Advanced Parameters for a comprehensive list of all the ways parameter values may be controlled.
Sets of Parameters
Assume you only want specific PowerShell parameters to be used in conjunction with other parameters. Perhaps you’ve given the Install-Office function a Path argument. This route will install the installer in whatever version it is. You don’t want the user to utilize the Version option in this scenario.
You need Sets of Parameters.
Sets of parameters may be created that can only be used with other parameters from the same set. You may now get the path to the installer by using both the Version and the Path parameters in the code below.
[CmdletBinding()] function Install-Office [Parameter(Mandatory)] param([Parameter(Mandatory)] param([Parameter(Mand [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(Mandatory)], [string]$Version, [string]$Version, [string]$Version, [string] [string] Path $Path) Get-ChildItem -Path “SRV1InstallersOffice$Version” if ($Version) elseif ($Path) Get-ChildItem -Path $Path -Path -Path -Path -Path -Path -Path -Path -Path –
However, this creates a difficulty since the user might utilize both parameters. Furthermore, since both parameters are required, they will be obliged to utilize both even if it is not what you desire. To address this, we may group all of the parameters together in a parameter set like the one below.
[CmdletBinding()] function Install-Office [Parameter(Mandatory,ParameterSetName = ‘ByVersion’)] param([Parameter(Mandatory,ParameterSetName = ‘ByVersion’)] param([Parameter(Mand [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(Mandatory, parameterSetName = ‘ByPath’)], [string]$Version, [string]$Version, [string]$Version, [string]$Version, [string]$Version, [string]$ [string] Path $Path) Get-ChildItem -Path “SRV1InstallersOffice$Version” if ($Version) elseif ($Path) Get-ChildItem -Path $Path -Path -Path -Path -Path -Path -Path -Path -Path –
You may control groups of parameters together by specifying a parameter set name on each parameter.
Set of Default Parameters
What happens if a user attempts to run Install-Office without specifying any parameters? You’ll get a nice error notice if this isn’t accounted for.
There are no parameters configured.
You’ll need to establish a default parameter set in the CmdletBinding() section to correct this. Changing [CmdletBinding()] to [CmdletBinding(DefaultParameterSetName = ‘ByVersion’)] informs the function to choose a parameter set to use if no arguments were explicitly supplied.
Because it will be utilizing that parameter set, anytime you run Install-Office, it will request for the Version parameter.
Input to the Pipeline
You’ve been generating functions with a PowerShell parameter that can only be given using the standard -ParameterName Value syntax in the examples so far. However, as you’ve seen, PowerShell includes an easy pipeline that lets you transfer objects from one command to the next without having to use the “usual” syntax.
You “chain” instructions together using the pipe symbol | when using the pipeline, enabling a user to submit the output of a command like Get-Service to Start-Service as a shortcut for giving the Name argument to Start-Service.
Using a Loop the “Old” Way
You’re installing Office and have a Version argument in the custom function you’re dealing with. Let’s imagine you have a CSV file with a list of machine names in one row and the Office version that has to be installed on them in the second row. This is what the CSV file looks like:
PC1,2016,PC2,2013,PC3,2016,PC4,2016,PC5,2016,PC6,2016,PC7,2016,PC8,2016,PC9
On that machine, you’d want to install the version of Office that’s next to it.
First, you’ll need to add a ComputerName argument to the method so that each iteration of the function gets a distinct computer name. I’ve added a Write-Host instance to observe how the variables grow within the fictitious function, and I’ve built some pseudocode to mimic some code that may be in the fictional function.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter()] [string]$ComputerName ) <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” }
You may do this by reading the CSV file and giving the data for the computer name and version to the Install-Office function after you’ve added the ComputerName argument to the function.
$computers = Import-Csv -Path ‘C:ComputerOfficeVersions.csv’ foreach ($pc in $computers) { Install-Office -Version $_.Version -ComputerName $_.ComputerName }
Building Input to the Pipeline for Parameters
The “old” technique of reading the CSV rows and passing each row’s properties to the function is to use a loop to transfer each row’s properties to the function. Instead of using the foreach loop, you should utilize the pipeline in this section.
The function does not support the pipeline in its current state. It would seem logical to suppose that you could use the pipeline to feed each computer’s name and version to the function. We’re reading the CSV and handing it to Install-Office straight below, however this doesn’t work.
PS> Import-Csv -Path ‘C:ComputerOfficeVersions.csv’ | Install-Office
You may make all the assumptions you want, but that won’t make it work. When you know that Import-Csv sends the Version argument as an object property, you’ll see that we’re being requested for it. Why isn’t it functioning properly? Because you haven’t yet implemented pipeline support.
There are two kinds of Input to the Pipeline in a PowerShell function; ByValue (entire object) and ByPropertyName (a single object property). Which do you think is the best way to stitch together the output of Import-Csv to the input of Install-Office?
You could use the ByPropertyName function without any restructuring since Import-Csv already returns the properties Version and ComputerName because they’re columns in the CSV.
It’s a lot easier than you would expect to add pipeline support to a custom function. ValueFromPipeline or ValueFromPipelineByPropertyName are two keywords that indicate a parameter attribute.
You’ll use ValueFromPipelineByPropertyName in this example to link the ComputerName and Version properties returned from Import-Csv to the Version and ComputerName arguments of Install-Office.
Because we want to bind both of these arguments, you’ll need to add this keyword to each of them, as seen below, and then restart the function using the pipeline.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory,ValueFromPipelineByPropertyName)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [string]$ComputerName ) <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” }
That’s strange. It just processed the CSV’s final row. What exactly is going on? Because you skipped over a notion that isn’t necessary when generating functions without pipeline support, it just performed the function for the final row.
Remember to Include the Process Block!
When you need to create a function that involves pipeline support, you must include (at a minimum) an “embedded” block inside of your function called. process. This process block tells PowerShell that when receiving Input to the Pipeline, to run the function for every iteration. By default, it will only execute the last one.
Other blocks, such as begin and end, may theoretically be added, although scripters don’t utilize them nearly as often.
I’ll add a process block with the code inside to instruct PowerShell to run this function for every object that comes in.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory,ValueFromPipelineByPropertyName)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [string]$ComputerName ) process { <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” } }
The Version and ComputerName attributes received from Import-Csv were now provided to Install-Office and tied to the Version and ComputerName arguments.
Resources
Check out my blog article on PowerShell functions to learn more about how function parameters operate.
I also recommend that you have a look at my Pluralsight course, Building Advanced PowerShell Functions and Modules, for an in-depth look at PowerShell functions, function parameters, and PowerShell modules.
.ComputerName
One or more parameters, also known as arguments, may be used with any PowerShell command. You’re not creating proper PowerShell code if you don’t use PowerShell parameters in your PowerShell functions!
You’ll cover almost every aspect of generating and using PowerShell parameters or arguments in this tutorial!
This is an excerpt from PowerShell for SysAdmins, which I wrote. Check it out if you want to learn PowerShell or pick up some tips and techniques.
What Is the Purpose of a Parameter?
When you first start writing functions, you’ll have the choice of including or excluding parameters, as well as the behavior of those arguments.
Consider the following scenario: you have a function that installs Microsoft Office. Perhaps it secretly invokes the Office installation inside the method. For our purposes, it doesn’t important what the function performs. With the name of the function and the script block, the basic function looks like this.
## Run the quiet installer here using the function Install-Office
In this case, you just ran Install-Office without any options and it took care of everything.
Whether the Install-Office function had parameters or not made no difference. It didn’t seem to have any necessary arguments; otherwise, PowerShell wouldn’t have allowed us execute it without one.
When Should You Use PowerShell Parameters?
There are several versions of Office. It’s possible that you’ll need to set up Office 2013 and 2016. There is currently no method to indicate this. Every time you wanted to modify the behavior, you could edit the function’s code.
You might, for instance, construct two distinct methods to install various versions.
‘I installed Office 2013.’ function Install-Office2013 Write-Host ‘Wheeeeeeeeeeeeeeeeee ‘I installed Office 2016.’ function Install-Office2016 Write-Host ‘Wheeeeeeeeeeeeeeeeee
It’s possible to do this, but it’s not scalable. It compels you to build a new function for each new version of Office that is released. When you don’t have to, you’ll have to duplicate a lot of code.
Instead, you’ll need a means to modify the function’s functionality by passing in various values at runtime. How do you go about doing this?
Yep! Parameters, or as some refer to them, arguments.
You must add at least one argument to this method if we want to install multiple versions of Office without having to change the code every time.
Before you rapidly come up with a PowerShell parameter, you must first ask yourself, “What is the smallest modification or changes you foresee to be required in this function?”
Keep in mind that you must restart this function without modifying any of the code inside it. The parameter in this case is presumably obvious; you need to provide a Version argument. When you’ve got a function with tens of lines of code, though, the solution isn’t always obvious. It will always assist if you answer the question as accurately as possible.
So you’re aware that a Version argument is required. So, what’s next? You may now add one, but there are various ways to skin that cat, as with any excellent programming language.
Based on my almost decade of PowerShell knowledge, I’ll teach you the “optimal” approach to generate parameters in this lesson. Keep in mind, though, that this isn’t the only method to create a parameter.
Positional parameters are anything that exists. You may use these parameters to provide values to parameters without having to mention the parameter name. Positional criteria are useful, but they aren’t “best practice.” Why? Because they are more difficult to understand, particularly when a function has a lot of arguments.
Using PowerShell to Create a Simple Parameter
To add a parameter to a function, you’ll need two things: a param block and the parameter itself. The param keyword is followed by a set of parentheses to create a param block.
[CmdletBinding()] function Install-Office ‘I installed Office 2016.’ param() Write-Host ‘Wheeeeeeeeeeeeeeeeee
The function’s real functionality hasn’t altered at this time. We’ve just finished installing some piping in preparation for the first parameter.
You’ll now construct the parameter once the param block is in place. The method I recommend for creating a parameter starts with the parameter block, then the type of parameter, and finally the parameter variable name.
[CmdletBinding()] function Install-Office [Parameter()] param([Parameter()] param([Parameter()] par Write-Host ‘I installed Office 2016.’ [string]$Version) ‘Wheeeeeeeeeeeeeeeeee
We’ve now generated a PowerShell function parameter, but what precisely happened?
Every parameter should include a parameter block, which is an optional but suggested component. It is “function plumbing,” similar to the param block, which prepares the parameter for adding further functionality. The kind of argument it is is defined on the second line.
We’ve opted to convert the Version option to a string in this situation. If an explicit type is defined, any value supplied to this argument will be “converted” to a string if it isn’t already one.
The kind is not required, although it is strongly recommended. Defining the sort of parameter it is will help you avoid a lot of unpleasant problems down the line. Have faith in me.
Now that you’ve established the option, you may use the Version argument in the Install-Office command to provide a version string, such as 2013. The value supplied to the Version parameter is also known as the arguments or values of parameters.
PS> Install-Office -Version 2013 I installed Office 2016. Yippee!
What exactly is going on here? You told it you wanted version 2013, yet it still says it’s installed version 2016. You must remember to rename the function’s code to a variable after adding an argument. When a parameter is supplied to a function, the variable is extended to the value that was passed.
Replace the static text 2016 with the Version parameter variable, changing the single quotes to double quotes to allow the variable to grow.
[CmdletBinding()] function Install-Office [Parameter()] param([Parameter()] param([Parameter()] par $Version) ([string]$Version) ([string]$Version) ( Host-Write “I downloaded and installed Office $Version. What a relief! “,,,,,,,,,,
As you can see, any value you provide the Version argument is sent into the method as the $Version variable.
Attribute for Mandatory Parameters
Remember how I said the [Parameter()] line was merely “function plumbing” that was required to get the function ready for further work? The extra labor I mentioned previously is adding parameter properties to a parameter.
A parameter does not have to be a variable placeholder. Parameter characteristics and parameter validity are concepts in PowerShell. The functionality of a parameter may be changed in a variety of ways using parameter attributes.
The Mandatory keyword, for example, is one of the most typical parameter properties you’ll use. You may call the Install-Office function without supplying the Version argument by default, and it will work perfectly. The parameter Version would be optional. Although the $Version variable within the code would not grow since there was no value, it would still execute.
When establishing a parameter, you’ll often want the user to utilize it all of the time. You’ll rely on the argument’s value somewhere in the function’s code, and if the parameter isn’t supplied, the function will fail. You wish to require the user to supply this parameter value to your function in certain instances. You’d want that parameter to be required.
Once you have the fundamental architecture in place, forcing users to utilize a parameter is straightforward. Within the parenthesis of the parameter, you must insert the term Mandatory. Once you’ve done that, calling the function without the argument will cause it to pause until a value is entered.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$Version ) Write-Host “I installed Office $Version. Yippee!” } PS> Install-Office cmdlet Install-Office at command pipeline position 1 Supply values for the following parameters: Version:
Until you supply a value for the Version argument, the function will wait. When you press Enter, PowerShell will run the function and then go on. If you specify a value for the argument, PowerShell will not ask you for it every time you run the command.
Validation Attributes for PowerShell Parameters
One of the most popular parameter features is to make a parameter obligatory, but you may also utilize parameter validation attributes. It’s always important in programming to limit user input as much as possible. Limiting the data that users (or even you!) may send to your functions or scripts can minimize redundant code in your function that must cater for a variety of scenarios.
Observational Learning
For example, I showed sending the number 2013 to the Install-Office method since I knew it would work. The code was written by myself! I’m guessing (never do this in code!) that anybody who understands anything would select either 2013 or 2016 as the version. What you think is plain to you may not be so evident to others.
If you want to be technical about versions, specifying the 2013 version as 15.0 and the 2016 version as 16.0 would be more correct if Microsoft was still using the versioning schema they used in the past. But what if, expecting they’ll give a version of 2013 or 2016, you’ve included code in the method that searches for files containing those versions or something else?
Here’s an example of when the $Version string could be used in a file path. It will fail or do something worse if someone passes a value that does not complete a folder name of Office2013 or Office2016, such as removing unexpected folders or changing items you didn’t account for.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$Version ) Get-ChildItem -Path “\SRV1InstallersOffice$Version” } PS> Install-Office -Version ‘15.0’ Get-ChildItem : Cannot find path ‘SRV1InstallersOffice15.0’ because it does not exist. At line:7 char:5 Get-ChildItem -Path “\SRV1InstallersOffice$Version” ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CategoryInfo : ObjectNotFound: (SRV1InstallersOffice15.0:String) [Get-ChildItem], ItemNotFoundExcep tion FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand
You may use PowerShell parameter validation to restrict the user’s input to what you anticipate.
Validation Attribute Using the ValidateSet Parameter
You may utilize a variety of parameter validation techniques. Run Get-Help about Functions Advanced Parameters for a complete list. In this case, the ValidateSet property would be the most appropriate.
You may use the ValidateSet validation property to define a list of values that can be used as parameter values. I’d want to make sure that the user may only provide these values since we’re only accounting for the strings 2013 or 2016. Otherwise, the function will instantly fail and tell them of the reason.
Under the original parameter keyword, you may add parameter validation characteristics. You can see an array of objects within the parentheses of the parameter property in this example; 2013 and 2016. A parameter validation property notifies PowerShell that only 2013 or 2016 are accepted options for Version. If you attempt to pass anything other than what’s in the set, you’ll get an error message stating that you only have a limited amount of possibilities.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateSet(‘2013′,’2016’)] [string]$Version ) Get-ChildItem -Path “\SRV1InstallersOffice$Version” } PS> Install-Office -Version 15.0 Install-Office : Cannot validate argument on parameter ‘Version’. The argument “15.0” does not belong to the set “2013,2016” specified by the ValidateSet attribute. Supply an argument that is in the set and then try the command again. At line:1 char:25 Install-Office -Version 15.0 ~~~~ CategoryInfo : InvalidData: (:) [Install-Office], ParameterBindingValidationException FullyQualifiedErrorId : ParameterArgumentValidationError,Install-Office
The ValidateSet property is a frequently used validation attribute. Check out the Functions Advanced Parameters help topic by executing Get-Help about Functions Advanced Parameters for a comprehensive list of all the ways parameter values may be controlled.
Sets of Parameters
Assume you only want specific PowerShell parameters to be used in conjunction with other parameters. Perhaps you’ve given the Install-Office function a Path argument. This route will install the installer in whatever version it is. You don’t want the user to utilize the Version option in this scenario.
You need Sets of Parameters.
Sets of parameters may be created that can only be used with other parameters from the same set. You may now get the path to the installer by using both the Version and the Path parameters in the code below.
[CmdletBinding()] function Install-Office [Parameter(Mandatory)] param([Parameter(Mandatory)] param([Parameter(Mand [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(Mandatory)], [string]$Version, [string]$Version, [string]$Version, [string] [string] Path $Path) Get-ChildItem -Path “SRV1InstallersOffice$Version” if ($Version) elseif ($Path) Get-ChildItem -Path $Path -Path -Path -Path -Path -Path -Path -Path -Path –
However, this creates a difficulty since the user might utilize both parameters. Furthermore, since both parameters are required, they will be obliged to utilize both even if it is not what you desire. To address this, we may group all of the parameters together in a parameter set like the one below.
[CmdletBinding()] function Install-Office [Parameter(Mandatory,ParameterSetName = ‘ByVersion’)] param([Parameter(Mandatory,ParameterSetName = ‘ByVersion’)] param([Parameter(Mand [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(Mandatory, parameterSetName = ‘ByPath’)], [string]$Version, [string]$Version, [string]$Version, [string]$Version, [string]$Version, [string]$ [string] Path $Path) Get-ChildItem -Path “SRV1InstallersOffice$Version” if ($Version) elseif ($Path) Get-ChildItem -Path $Path -Path -Path -Path -Path -Path -Path -Path -Path –
You may control groups of parameters together by specifying a parameter set name on each parameter.
Set of Default Parameters
What happens if a user attempts to run Install-Office without specifying any parameters? You’ll get a nice error notice if this isn’t accounted for.
There are no parameters configured.
You’ll need to establish a default parameter set in the CmdletBinding() section to correct this. Changing [CmdletBinding()] to [CmdletBinding(DefaultParameterSetName = ‘ByVersion’)] informs the function to choose a parameter set to use if no arguments were explicitly supplied.
Because it will be utilizing that parameter set, anytime you run Install-Office, it will request for the Version parameter.
Input to the Pipeline
You’ve been generating functions with a PowerShell parameter that can only be given using the standard -ParameterName Value syntax in the examples so far. However, as you’ve seen, PowerShell includes an easy pipeline that lets you transfer objects from one command to the next without having to use the “usual” syntax.
You “chain” instructions together using the pipe symbol | when using the pipeline, enabling a user to submit the output of a command like Get-Service to Start-Service as a shortcut for giving the Name argument to Start-Service.
Using a Loop the “Old” Way
You’re installing Office and have a Version argument in the custom function you’re dealing with. Let’s imagine you have a CSV file with a list of machine names in one row and the Office version that has to be installed on them in the second row. This is what the CSV file looks like:
PC1,2016,PC2,2013,PC3,2016,PC4,2016,PC5,2016,PC6,2016,PC7,2016,PC8,2016,PC9
On that machine, you’d want to install the version of Office that’s next to it.
First, you’ll need to add a ComputerName argument to the method so that each iteration of the function gets a distinct computer name. I’ve added a Write-Host instance to observe how the variables grow within the fictitious function, and I’ve built some pseudocode to mimic some code that may be in the fictional function.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter()] [string]$ComputerName ) <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” }
You may do this by reading the CSV file and giving the data for the computer name and version to the Install-Office function after you’ve added the ComputerName argument to the function.
$computers = Import-Csv -Path ‘C:ComputerOfficeVersions.csv’ foreach ($pc in $computers) { Install-Office -Version $_.Version -ComputerName $_.ComputerName }
Building Input to the Pipeline for Parameters
The “old” technique of reading the CSV rows and passing each row’s properties to the function is to use a loop to transfer each row’s properties to the function. Instead of using the foreach loop, you should utilize the pipeline in this section.
The function does not support the pipeline in its current state. It would seem logical to suppose that you could use the pipeline to feed each computer’s name and version to the function. We’re reading the CSV and handing it to Install-Office straight below, however this doesn’t work.
PS> Import-Csv -Path ‘C:ComputerOfficeVersions.csv’ | Install-Office
You may make all the assumptions you want, but that won’t make it work. When you know that Import-Csv sends the Version argument as an object property, you’ll see that we’re being requested for it. Why isn’t it functioning properly? Because you haven’t yet implemented pipeline support.
There are two kinds of Input to the Pipeline in a PowerShell function; ByValue (entire object) and ByPropertyName (a single object property). Which do you think is the best way to stitch together the output of Import-Csv to the input of Install-Office?
You could use the ByPropertyName function without any restructuring since Import-Csv already returns the properties Version and ComputerName because they’re columns in the CSV.
It’s a lot easier than you would expect to add pipeline support to a custom function. ValueFromPipeline or ValueFromPipelineByPropertyName are two keywords that indicate a parameter attribute.
You’ll use ValueFromPipelineByPropertyName in this example to link the ComputerName and Version properties returned from Import-Csv to the Version and ComputerName arguments of Install-Office.
Because we want to bind both of these arguments, you’ll need to add this keyword to each of them, as seen below, and then restart the function using the pipeline.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory,ValueFromPipelineByPropertyName)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [string]$ComputerName ) <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” }
That’s strange. It just processed the CSV’s final row. What exactly is going on? Because you skipped over a notion that isn’t necessary when generating functions without pipeline support, it just performed the function for the final row.
Remember to Include the Process Block!
When you need to create a function that involves pipeline support, you must include (at a minimum) an “embedded” block inside of your function called. process. This process block tells PowerShell that when receiving Input to the Pipeline, to run the function for every iteration. By default, it will only execute the last one.
Other blocks, such as begin and end, may theoretically be added, although scripters don’t utilize them nearly as often.
I’ll add a process block with the code inside to instruct PowerShell to run this function for every object that comes in.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory,ValueFromPipelineByPropertyName)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [string]$ComputerName ) process { <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” } }
The Version and ComputerName attributes received from Import-Csv were now provided to Install-Office and tied to the Version and ComputerName arguments.
Resources
Check out my blog article on PowerShell functions to learn more about how function parameters operate.
I also recommend that you have a look at my Pluralsight course, Building Advanced PowerShell Functions and Modules, for an in-depth look at PowerShell functions, function parameters, and PowerShell modules.
.ComputerName
One or more parameters, also known as arguments, may be used with any PowerShell command. You’re not creating proper PowerShell code if you don’t use PowerShell parameters in your PowerShell functions!
You’ll cover almost every aspect of generating and using PowerShell parameters or arguments in this tutorial!
This is an excerpt from PowerShell for SysAdmins, which I wrote. Check it out if you want to learn PowerShell or pick up some tips and techniques.
What Is the Purpose of a Parameter?
When you first start writing functions, you’ll have the choice of including or excluding parameters, as well as the behavior of those arguments.
Consider the following scenario: you have a function that installs Microsoft Office. Perhaps it secretly invokes the Office installation inside the method. For our purposes, it doesn’t important what the function performs. With the name of the function and the script block, the basic function looks like this.
## Run the quiet installer here using the function Install-Office
In this case, you just ran Install-Office without any options and it took care of everything.
Whether the Install-Office function had parameters or not made no difference. It didn’t seem to have any necessary arguments; otherwise, PowerShell wouldn’t have allowed us execute it without one.
When Should You Use PowerShell Parameters?
There are several versions of Office. It’s possible that you’ll need to set up Office 2013 and 2016. There is currently no method to indicate this. Every time you wanted to modify the behavior, you could edit the function’s code.
You might, for instance, construct two distinct methods to install various versions.
‘I installed Office 2013.’ function Install-Office2013 Write-Host ‘Wheeeeeeeeeeeeeeeeee ‘I installed Office 2016.’ function Install-Office2016 Write-Host ‘Wheeeeeeeeeeeeeeeeee
It’s possible to do this, but it’s not scalable. It compels you to build a new function for each new version of Office that is released. When you don’t have to, you’ll have to duplicate a lot of code.
Instead, you’ll need a means to modify the function’s functionality by passing in various values at runtime. How do you go about doing this?
Yep! Parameters, or as some refer to them, arguments.
You must add at least one argument to this method if we want to install multiple versions of Office without having to change the code every time.
Before you rapidly come up with a PowerShell parameter, you must first ask yourself, “What is the smallest modification or changes you foresee to be required in this function?”
Keep in mind that you must restart this function without modifying any of the code inside it. The parameter in this case is presumably obvious; you need to provide a Version argument. When you’ve got a function with tens of lines of code, though, the solution isn’t always obvious. It will always assist if you answer the question as accurately as possible.
So you’re aware that a Version argument is required. So, what’s next? You may now add one, but there are various ways to skin that cat, as with any excellent programming language.
Based on my almost decade of PowerShell knowledge, I’ll teach you the “optimal” approach to generate parameters in this lesson. Keep in mind, though, that this isn’t the only method to create a parameter.
Positional parameters are anything that exists. You may use these parameters to provide values to parameters without having to mention the parameter name. Positional criteria are useful, but they aren’t “best practice.” Why? Because they are more difficult to understand, particularly when a function has a lot of arguments.
Using PowerShell to Create a Simple Parameter
To add a parameter to a function, you’ll need two things: a param block and the parameter itself. The param keyword is followed by a set of parentheses to create a param block.
[CmdletBinding()] function Install-Office ‘I installed Office 2016.’ param() Write-Host ‘Wheeeeeeeeeeeeeeeeee
The function’s real functionality hasn’t altered at this time. We’ve just finished installing some piping in preparation for the first parameter.
You’ll now construct the parameter once the param block is in place. The method I recommend for creating a parameter starts with the parameter block, then the type of parameter, and finally the parameter variable name.
[CmdletBinding()] function Install-Office [Parameter()] param([Parameter()] param([Parameter()] par Write-Host ‘I installed Office 2016.’ [string]$Version) ‘Wheeeeeeeeeeeeeeeeee
We’ve now generated a PowerShell function parameter, but what precisely happened?
Every parameter should include a parameter block, which is an optional but suggested component. It is “function plumbing,” similar to the param block, which prepares the parameter for adding further functionality. The kind of argument it is is defined on the second line.
We’ve opted to convert the Version option to a string in this situation. If an explicit type is defined, any value supplied to this argument will be “converted” to a string if it isn’t already one.
The kind is not required, although it is strongly recommended. Defining the sort of parameter it is will help you avoid a lot of unpleasant problems down the line. Have faith in me.
Now that you’ve established the option, you may use the Version argument in the Install-Office command to provide a version string, such as 2013. The value supplied to the Version parameter is also known as the arguments or values of parameters.
PS> Install-Office -Version 2013 I installed Office 2016. Yippee!
What exactly is going on here? You told it you wanted version 2013, yet it still says it’s installed version 2016. You must remember to rename the function’s code to a variable after adding an argument. When a parameter is supplied to a function, the variable is extended to the value that was passed.
Replace the static text 2016 with the Version parameter variable, changing the single quotes to double quotes to allow the variable to grow.
[CmdletBinding()] function Install-Office [Parameter()] param([Parameter()] param([Parameter()] par $Version) ([string]$Version) ([string]$Version) ( Host-Write “I downloaded and installed Office $Version. What a relief! “,,,,,,,,,,
As you can see, any value you provide the Version argument is sent into the method as the $Version variable.
Attribute for Mandatory Parameters
Remember how I said the [Parameter()] line was merely “function plumbing” that was required to get the function ready for further work? The extra labor I mentioned previously is adding parameter properties to a parameter.
A parameter does not have to be a variable placeholder. Parameter characteristics and parameter validity are concepts in PowerShell. The functionality of a parameter may be changed in a variety of ways using parameter attributes.
The Mandatory keyword, for example, is one of the most typical parameter properties you’ll use. You may call the Install-Office function without supplying the Version argument by default, and it will work perfectly. The parameter Version would be optional. Although the $Version variable within the code would not grow since there was no value, it would still execute.
When establishing a parameter, you’ll often want the user to utilize it all of the time. You’ll rely on the argument’s value somewhere in the function’s code, and if the parameter isn’t supplied, the function will fail. You wish to require the user to supply this parameter value to your function in certain instances. You’d want that parameter to be required.
Once you have the fundamental architecture in place, forcing users to utilize a parameter is straightforward. Within the parenthesis of the parameter, you must insert the term Mandatory. Once you’ve done that, calling the function without the argument will cause it to pause until a value is entered.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$Version ) Write-Host “I installed Office $Version. Yippee!” } PS> Install-Office cmdlet Install-Office at command pipeline position 1 Supply values for the following parameters: Version:
Until you supply a value for the Version argument, the function will wait. When you press Enter, PowerShell will run the function and then go on. If you specify a value for the argument, PowerShell will not ask you for it every time you run the command.
Validation Attributes for PowerShell Parameters
One of the most popular parameter features is to make a parameter obligatory, but you may also utilize parameter validation attributes. It’s always important in programming to limit user input as much as possible. Limiting the data that users (or even you!) may send to your functions or scripts can minimize redundant code in your function that must cater for a variety of scenarios.
Observational Learning
For example, I showed sending the number 2013 to the Install-Office method since I knew it would work. The code was written by myself! I’m guessing (never do this in code!) that anybody who understands anything would select either 2013 or 2016 as the version. What you think is plain to you may not be so evident to others.
If you want to be technical about versions, specifying the 2013 version as 15.0 and the 2016 version as 16.0 would be more correct if Microsoft was still using the versioning schema they used in the past. But what if, expecting they’ll give a version of 2013 or 2016, you’ve included code in the method that searches for files containing those versions or something else?
Here’s an example of when the $Version string could be used in a file path. It will fail or do something worse if someone passes a value that does not complete a folder name of Office2013 or Office2016, such as removing unexpected folders or changing items you didn’t account for.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$Version ) Get-ChildItem -Path “\SRV1InstallersOffice$Version” } PS> Install-Office -Version ‘15.0’ Get-ChildItem : Cannot find path ‘SRV1InstallersOffice15.0’ because it does not exist. At line:7 char:5 Get-ChildItem -Path “\SRV1InstallersOffice$Version” ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CategoryInfo : ObjectNotFound: (SRV1InstallersOffice15.0:String) [Get-ChildItem], ItemNotFoundExcep tion FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand
You may use PowerShell parameter validation to restrict the user’s input to what you anticipate.
Validation Attribute Using the ValidateSet Parameter
You may utilize a variety of parameter validation techniques. Run Get-Help about Functions Advanced Parameters for a complete list. In this case, the ValidateSet property would be the most appropriate.
You may use the ValidateSet validation property to define a list of values that can be used as parameter values. I’d want to make sure that the user may only provide these values since we’re only accounting for the strings 2013 or 2016. Otherwise, the function will instantly fail and tell them of the reason.
Under the original parameter keyword, you may add parameter validation characteristics. You can see an array of objects within the parentheses of the parameter property in this example; 2013 and 2016. A parameter validation property notifies PowerShell that only 2013 or 2016 are accepted options for Version. If you attempt to pass anything other than what’s in the set, you’ll get an error message stating that you only have a limited amount of possibilities.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateSet(‘2013′,’2016’)] [string]$Version ) Get-ChildItem -Path “\SRV1InstallersOffice$Version” } PS> Install-Office -Version 15.0 Install-Office : Cannot validate argument on parameter ‘Version’. The argument “15.0” does not belong to the set “2013,2016” specified by the ValidateSet attribute. Supply an argument that is in the set and then try the command again. At line:1 char:25 Install-Office -Version 15.0 ~~~~ CategoryInfo : InvalidData: (:) [Install-Office], ParameterBindingValidationException FullyQualifiedErrorId : ParameterArgumentValidationError,Install-Office
The ValidateSet property is a frequently used validation attribute. Check out the Functions Advanced Parameters help topic by executing Get-Help about Functions Advanced Parameters for a comprehensive list of all the ways parameter values may be controlled.
Sets of Parameters
Assume you only want specific PowerShell parameters to be used in conjunction with other parameters. Perhaps you’ve given the Install-Office function a Path argument. This route will install the installer in whatever version it is. You don’t want the user to utilize the Version option in this scenario.
You need Sets of Parameters.
Sets of parameters may be created that can only be used with other parameters from the same set. You may now get the path to the installer by using both the Version and the Path parameters in the code below.
[CmdletBinding()] function Install-Office [Parameter(Mandatory)] param([Parameter(Mandatory)] param([Parameter(Mand [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(Mandatory)], [string]$Version, [string]$Version, [string]$Version, [string] [string] Path $Path) Get-ChildItem -Path “SRV1InstallersOffice$Version” if ($Version) elseif ($Path) Get-ChildItem -Path $Path -Path -Path -Path -Path -Path -Path -Path -Path –
However, this creates a difficulty since the user might utilize both parameters. Furthermore, since both parameters are required, they will be obliged to utilize both even if it is not what you desire. To address this, we may group all of the parameters together in a parameter set like the one below.
[CmdletBinding()] function Install-Office [Parameter(Mandatory,ParameterSetName = ‘ByVersion’)] param([Parameter(Mandatory,ParameterSetName = ‘ByVersion’)] param([Parameter(Mand [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(Mandatory, parameterSetName = ‘ByPath’)], [string]$Version, [string]$Version, [string]$Version, [string]$Version, [string]$Version, [string]$ [string] Path $Path) Get-ChildItem -Path “SRV1InstallersOffice$Version” if ($Version) elseif ($Path) Get-ChildItem -Path $Path -Path -Path -Path -Path -Path -Path -Path -Path –
You may control groups of parameters together by specifying a parameter set name on each parameter.
Set of Default Parameters
What happens if a user attempts to run Install-Office without specifying any parameters? You’ll get a nice error notice if this isn’t accounted for.
There are no parameters configured.
You’ll need to establish a default parameter set in the CmdletBinding() section to correct this. Changing [CmdletBinding()] to [CmdletBinding(DefaultParameterSetName = ‘ByVersion’)] informs the function to choose a parameter set to use if no arguments were explicitly supplied.
Because it will be utilizing that parameter set, anytime you run Install-Office, it will request for the Version parameter.
Input to the Pipeline
You’ve been generating functions with a PowerShell parameter that can only be given using the standard -ParameterName Value syntax in the examples so far. However, as you’ve seen, PowerShell includes an easy pipeline that lets you transfer objects from one command to the next without having to use the “usual” syntax.
You “chain” instructions together using the pipe symbol | when using the pipeline, enabling a user to submit the output of a command like Get-Service to Start-Service as a shortcut for giving the Name argument to Start-Service.
Using a Loop the “Old” Way
You’re installing Office and have a Version argument in the custom function you’re dealing with. Let’s imagine you have a CSV file with a list of machine names in one row and the Office version that has to be installed on them in the second row. This is what the CSV file looks like:
PC1,2016,PC2,2013,PC3,2016,PC4,2016,PC5,2016,PC6,2016,PC7,2016,PC8,2016,PC9
On that machine, you’d want to install the version of Office that’s next to it.
First, you’ll need to add a ComputerName argument to the method so that each iteration of the function gets a distinct computer name. I’ve added a Write-Host instance to observe how the variables grow within the fictitious function, and I’ve built some pseudocode to mimic some code that may be in the fictional function.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter()] [string]$ComputerName ) <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” }
You may do this by reading the CSV file and giving the data for the computer name and version to the Install-Office function after you’ve added the ComputerName argument to the function.
$computers = Import-Csv -Path ‘C:ComputerOfficeVersions.csv’ foreach ($pc in $computers) { Install-Office -Version $_.Version -ComputerName $_.ComputerName }
Building Input to the Pipeline for Parameters
The “old” technique of reading the CSV rows and passing each row’s properties to the function is to use a loop to transfer each row’s properties to the function. Instead of using the foreach loop, you should utilize the pipeline in this section.
The function does not support the pipeline in its current state. It would seem logical to suppose that you could use the pipeline to feed each computer’s name and version to the function. We’re reading the CSV and handing it to Install-Office straight below, however this doesn’t work.
PS> Import-Csv -Path ‘C:ComputerOfficeVersions.csv’ | Install-Office
You may make all the assumptions you want, but that won’t make it work. When you know that Import-Csv sends the Version argument as an object property, you’ll see that we’re being requested for it. Why isn’t it functioning properly? Because you haven’t yet implemented pipeline support.
There are two kinds of Input to the Pipeline in a PowerShell function; ByValue (entire object) and ByPropertyName (a single object property). Which do you think is the best way to stitch together the output of Import-Csv to the input of Install-Office?
You could use the ByPropertyName function without any restructuring since Import-Csv already returns the properties Version and ComputerName because they’re columns in the CSV.
It’s a lot easier than you would expect to add pipeline support to a custom function. ValueFromPipeline or ValueFromPipelineByPropertyName are two keywords that indicate a parameter attribute.
You’ll use ValueFromPipelineByPropertyName in this example to link the ComputerName and Version properties returned from Import-Csv to the Version and ComputerName arguments of Install-Office.
Because we want to bind both of these arguments, you’ll need to add this keyword to each of them, as seen below, and then restart the function using the pipeline.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory,ValueFromPipelineByPropertyName)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [string]$ComputerName ) <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” }
That’s strange. It just processed the CSV’s final row. What exactly is going on? Because you skipped over a notion that isn’t necessary when generating functions without pipeline support, it just performed the function for the final row.
Remember to Include the Process Block!
When you need to create a function that involves pipeline support, you must include (at a minimum) an “embedded” block inside of your function called. process. This process block tells PowerShell that when receiving Input to the Pipeline, to run the function for every iteration. By default, it will only execute the last one.
Other blocks, such as begin and end, may theoretically be added, although scripters don’t utilize them nearly as often.
I’ll add a process block with the code inside to instruct PowerShell to run this function for every object that comes in.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory,ValueFromPipelineByPropertyName)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [string]$ComputerName ) process { <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” } }
The Version and ComputerName attributes received from Import-Csv were now provided to Install-Office and tied to the Version and ComputerName arguments.
Resources
Check out my blog article on PowerShell functions to learn more about how function parameters operate.
I also recommend that you have a look at my Pluralsight course, Building Advanced PowerShell Functions and Modules, for an in-depth look at PowerShell functions, function parameters, and PowerShell modules.
.ComputerName
One or more parameters, also known as arguments, may be used with any PowerShell command. You’re not creating proper PowerShell code if you don’t use PowerShell parameters in your PowerShell functions!
You’ll cover almost every aspect of generating and using PowerShell parameters or arguments in this tutorial!
This is an excerpt from PowerShell for SysAdmins, which I wrote. Check it out if you want to learn PowerShell or pick up some tips and techniques.
What Is the Purpose of a Parameter?
When you first start writing functions, you’ll have the choice of including or excluding parameters, as well as the behavior of those arguments.
Consider the following scenario: you have a function that installs Microsoft Office. Perhaps it secretly invokes the Office installation inside the method. For our purposes, it doesn’t important what the function performs. With the name of the function and the script block, the basic function looks like this.
## Run the quiet installer here using the function Install-Office
In this case, you just ran Install-Office without any options and it took care of everything.
Whether the Install-Office function had parameters or not made no difference. It didn’t seem to have any necessary arguments; otherwise, PowerShell wouldn’t have allowed us execute it without one.
When Should You Use PowerShell Parameters?
There are several versions of Office. It’s possible that you’ll need to set up Office 2013 and 2016. There is currently no method to indicate this. Every time you wanted to modify the behavior, you could edit the function’s code.
You might, for instance, construct two distinct methods to install various versions.
‘I installed Office 2013.’ function Install-Office2013 Write-Host ‘Wheeeeeeeeeeeeeeeeee ‘I installed Office 2016.’ function Install-Office2016 Write-Host ‘Wheeeeeeeeeeeeeeeeee
It’s possible to do this, but it’s not scalable. It compels you to build a new function for each new version of Office that is released. When you don’t have to, you’ll have to duplicate a lot of code.
Instead, you’ll need a means to modify the function’s functionality by passing in various values at runtime. How do you go about doing this?
Yep! Parameters, or as some refer to them, arguments.
You must add at least one argument to this method if we want to install multiple versions of Office without having to change the code every time.
Before you rapidly come up with a PowerShell parameter, you must first ask yourself, “What is the smallest modification or changes you foresee to be required in this function?”
Keep in mind that you must restart this function without modifying any of the code inside it. The parameter in this case is presumably obvious; you need to provide a Version argument. When you’ve got a function with tens of lines of code, though, the solution isn’t always obvious. It will always assist if you answer the question as accurately as possible.
So you’re aware that a Version argument is required. So, what’s next? You may now add one, but there are various ways to skin that cat, as with any excellent programming language.
Based on my almost decade of PowerShell knowledge, I’ll teach you the “optimal” approach to generate parameters in this lesson. Keep in mind, though, that this isn’t the only method to create a parameter.
Positional parameters are anything that exists. You may use these parameters to provide values to parameters without having to mention the parameter name. Positional criteria are useful, but they aren’t “best practice.” Why? Because they are more difficult to understand, particularly when a function has a lot of arguments.
Using PowerShell to Create a Simple Parameter
To add a parameter to a function, you’ll need two things: a param block and the parameter itself. The param keyword is followed by a set of parentheses to create a param block.
[CmdletBinding()] function Install-Office ‘I installed Office 2016.’ param() Write-Host ‘Wheeeeeeeeeeeeeeeeee
The function’s real functionality hasn’t altered at this time. We’ve just finished installing some piping in preparation for the first parameter.
You’ll now construct the parameter once the param block is in place. The method I recommend for creating a parameter starts with the parameter block, then the type of parameter, and finally the parameter variable name.
[CmdletBinding()] function Install-Office [Parameter()] param([Parameter()] param([Parameter()] par Write-Host ‘I installed Office 2016.’ [string]$Version) ‘Wheeeeeeeeeeeeeeeeee
We’ve now generated a PowerShell function parameter, but what precisely happened?
Every parameter should include a parameter block, which is an optional but suggested component. It is “function plumbing,” similar to the param block, which prepares the parameter for adding further functionality. The kind of argument it is is defined on the second line.
We’ve opted to convert the Version option to a string in this situation. If an explicit type is defined, any value supplied to this argument will be “converted” to a string if it isn’t already one.
The kind is not required, although it is strongly recommended. Defining the sort of parameter it is will help you avoid a lot of unpleasant problems down the line. Have faith in me.
Now that you’ve established the option, you may use the Version argument in the Install-Office command to provide a version string, such as 2013. The value supplied to the Version parameter is also known as the arguments or values of parameters.
PS> Install-Office -Version 2013 I installed Office 2016. Yippee!
What exactly is going on here? You told it you wanted version 2013, yet it still says it’s installed version 2016. You must remember to rename the function’s code to a variable after adding an argument. When a parameter is supplied to a function, the variable is extended to the value that was passed.
Replace the static text 2016 with the Version parameter variable, changing the single quotes to double quotes to allow the variable to grow.
[CmdletBinding()] function Install-Office [Parameter()] param([Parameter()] param([Parameter()] par $Version) ([string]$Version) ([string]$Version) ( Host-Write “I downloaded and installed Office $Version. What a relief! “,,,,,,,,,,
As you can see, any value you provide the Version argument is sent into the method as the $Version variable.
Attribute for Mandatory Parameters
Remember how I said the [Parameter()] line was merely “function plumbing” that was required to get the function ready for further work? The extra labor I mentioned previously is adding parameter properties to a parameter.
A parameter does not have to be a variable placeholder. Parameter characteristics and parameter validity are concepts in PowerShell. The functionality of a parameter may be changed in a variety of ways using parameter attributes.
The Mandatory keyword, for example, is one of the most typical parameter properties you’ll use. You may call the Install-Office function without supplying the Version argument by default, and it will work perfectly. The parameter Version would be optional. Although the $Version variable within the code would not grow since there was no value, it would still execute.
When establishing a parameter, you’ll often want the user to utilize it all of the time. You’ll rely on the argument’s value somewhere in the function’s code, and if the parameter isn’t supplied, the function will fail. You wish to require the user to supply this parameter value to your function in certain instances. You’d want that parameter to be required.
Once you have the fundamental architecture in place, forcing users to utilize a parameter is straightforward. Within the parenthesis of the parameter, you must insert the term Mandatory. Once you’ve done that, calling the function without the argument will cause it to pause until a value is entered.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$Version ) Write-Host “I installed Office $Version. Yippee!” } PS> Install-Office cmdlet Install-Office at command pipeline position 1 Supply values for the following parameters: Version:
Until you supply a value for the Version argument, the function will wait. When you press Enter, PowerShell will run the function and then go on. If you specify a value for the argument, PowerShell will not ask you for it every time you run the command.
Validation Attributes for PowerShell Parameters
One of the most popular parameter features is to make a parameter obligatory, but you may also utilize parameter validation attributes. It’s always important in programming to limit user input as much as possible. Limiting the data that users (or even you!) may send to your functions or scripts can minimize redundant code in your function that must cater for a variety of scenarios.
Observational Learning
For example, I showed sending the number 2013 to the Install-Office method since I knew it would work. The code was written by myself! I’m guessing (never do this in code!) that anybody who understands anything would select either 2013 or 2016 as the version. What you think is plain to you may not be so evident to others.
If you want to be technical about versions, specifying the 2013 version as 15.0 and the 2016 version as 16.0 would be more correct if Microsoft was still using the versioning schema they used in the past. But what if, expecting they’ll give a version of 2013 or 2016, you’ve included code in the method that searches for files containing those versions or something else?
Here’s an example of when the $Version string could be used in a file path. It will fail or do something worse if someone passes a value that does not complete a folder name of Office2013 or Office2016, such as removing unexpected folders or changing items you didn’t account for.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$Version ) Get-ChildItem -Path “\SRV1InstallersOffice$Version” } PS> Install-Office -Version ‘15.0’ Get-ChildItem : Cannot find path ‘SRV1InstallersOffice15.0’ because it does not exist. At line:7 char:5 Get-ChildItem -Path “\SRV1InstallersOffice$Version” ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CategoryInfo : ObjectNotFound: (SRV1InstallersOffice15.0:String) [Get-ChildItem], ItemNotFoundExcep tion FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand
You may use PowerShell parameter validation to restrict the user’s input to what you anticipate.
Validation Attribute Using the ValidateSet Parameter
You may utilize a variety of parameter validation techniques. Run Get-Help about Functions Advanced Parameters for a complete list. In this case, the ValidateSet property would be the most appropriate.
You may use the ValidateSet validation property to define a list of values that can be used as parameter values. I’d want to make sure that the user may only provide these values since we’re only accounting for the strings 2013 or 2016. Otherwise, the function will instantly fail and tell them of the reason.
Under the original parameter keyword, you may add parameter validation characteristics. You can see an array of objects within the parentheses of the parameter property in this example; 2013 and 2016. A parameter validation property notifies PowerShell that only 2013 or 2016 are accepted options for Version. If you attempt to pass anything other than what’s in the set, you’ll get an error message stating that you only have a limited amount of possibilities.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateSet(‘2013′,’2016’)] [string]$Version ) Get-ChildItem -Path “\SRV1InstallersOffice$Version” } PS> Install-Office -Version 15.0 Install-Office : Cannot validate argument on parameter ‘Version’. The argument “15.0” does not belong to the set “2013,2016” specified by the ValidateSet attribute. Supply an argument that is in the set and then try the command again. At line:1 char:25 Install-Office -Version 15.0 ~~~~ CategoryInfo : InvalidData: (:) [Install-Office], ParameterBindingValidationException FullyQualifiedErrorId : ParameterArgumentValidationError,Install-Office
The ValidateSet property is a frequently used validation attribute. Check out the Functions Advanced Parameters help topic by executing Get-Help about Functions Advanced Parameters for a comprehensive list of all the ways parameter values may be controlled.
Sets of Parameters
Assume you only want specific PowerShell parameters to be used in conjunction with other parameters. Perhaps you’ve given the Install-Office function a Path argument. This route will install the installer in whatever version it is. You don’t want the user to utilize the Version option in this scenario.
You need Sets of Parameters.
Sets of parameters may be created that can only be used with other parameters from the same set. You may now get the path to the installer by using both the Version and the Path parameters in the code below.
[CmdletBinding()] function Install-Office [Parameter(Mandatory)] param([Parameter(Mandatory)] param([Parameter(Mand [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(Mandatory)], [string]$Version, [string]$Version, [string]$Version, [string] [string] Path $Path) Get-ChildItem -Path “SRV1InstallersOffice$Version” if ($Version) elseif ($Path) Get-ChildItem -Path $Path -Path -Path -Path -Path -Path -Path -Path -Path –
However, this creates a difficulty since the user might utilize both parameters. Furthermore, since both parameters are required, they will be obliged to utilize both even if it is not what you desire. To address this, we may group all of the parameters together in a parameter set like the one below.
[CmdletBinding()] function Install-Office [Parameter(Mandatory,ParameterSetName = ‘ByVersion’)] param([Parameter(Mandatory,ParameterSetName = ‘ByVersion’)] param([Parameter(Mand [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(Mandatory, parameterSetName = ‘ByPath’)], [string]$Version, [string]$Version, [string]$Version, [string]$Version, [string]$Version, [string]$ [string] Path $Path) Get-ChildItem -Path “SRV1InstallersOffice$Version” if ($Version) elseif ($Path) Get-ChildItem -Path $Path -Path -Path -Path -Path -Path -Path -Path -Path –
You may control groups of parameters together by specifying a parameter set name on each parameter.
Set of Default Parameters
What happens if a user attempts to run Install-Office without specifying any parameters? You’ll get a nice error notice if this isn’t accounted for.
There are no parameters configured.
You’ll need to establish a default parameter set in the CmdletBinding() section to correct this. Changing [CmdletBinding()] to [CmdletBinding(DefaultParameterSetName = ‘ByVersion’)] informs the function to choose a parameter set to use if no arguments were explicitly supplied.
Because it will be utilizing that parameter set, anytime you run Install-Office, it will request for the Version parameter.
Input to the Pipeline
You’ve been generating functions with a PowerShell parameter that can only be given using the standard -ParameterName Value syntax in the examples so far. However, as you’ve seen, PowerShell includes an easy pipeline that lets you transfer objects from one command to the next without having to use the “usual” syntax.
You “chain” instructions together using the pipe symbol | when using the pipeline, enabling a user to submit the output of a command like Get-Service to Start-Service as a shortcut for giving the Name argument to Start-Service.
Using a Loop the “Old” Way
You’re installing Office and have a Version argument in the custom function you’re dealing with. Let’s imagine you have a CSV file with a list of machine names in one row and the Office version that has to be installed on them in the second row. This is what the CSV file looks like:
PC1,2016,PC2,2013,PC3,2016,PC4,2016,PC5,2016,PC6,2016,PC7,2016,PC8,2016,PC9
On that machine, you’d want to install the version of Office that’s next to it.
First, you’ll need to add a ComputerName argument to the method so that each iteration of the function gets a distinct computer name. I’ve added a Write-Host instance to observe how the variables grow within the fictitious function, and I’ve built some pseudocode to mimic some code that may be in the fictional function.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter()] [string]$ComputerName ) <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” }
You may do this by reading the CSV file and giving the data for the computer name and version to the Install-Office function after you’ve added the ComputerName argument to the function.
$computers = Import-Csv -Path ‘C:ComputerOfficeVersions.csv’ foreach ($pc in $computers) { Install-Office -Version $_.Version -ComputerName $_.ComputerName }
Building Input to the Pipeline for Parameters
The “old” technique of reading the CSV rows and passing each row’s properties to the function is to use a loop to transfer each row’s properties to the function. Instead of using the foreach loop, you should utilize the pipeline in this section.
The function does not support the pipeline in its current state. It would seem logical to suppose that you could use the pipeline to feed each computer’s name and version to the function. We’re reading the CSV and handing it to Install-Office straight below, however this doesn’t work.
PS> Import-Csv -Path ‘C:ComputerOfficeVersions.csv’ | Install-Office
You may make all the assumptions you want, but that won’t make it work. When you know that Import-Csv sends the Version argument as an object property, you’ll see that we’re being requested for it. Why isn’t it functioning properly? Because you haven’t yet implemented pipeline support.
There are two kinds of Input to the Pipeline in a PowerShell function; ByValue (entire object) and ByPropertyName (a single object property). Which do you think is the best way to stitch together the output of Import-Csv to the input of Install-Office?
You could use the ByPropertyName function without any restructuring since Import-Csv already returns the properties Version and ComputerName because they’re columns in the CSV.
It’s a lot easier than you would expect to add pipeline support to a custom function. ValueFromPipeline or ValueFromPipelineByPropertyName are two keywords that indicate a parameter attribute.
You’ll use ValueFromPipelineByPropertyName in this example to link the ComputerName and Version properties returned from Import-Csv to the Version and ComputerName arguments of Install-Office.
Because we want to bind both of these arguments, you’ll need to add this keyword to each of them, as seen below, and then restart the function using the pipeline.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory,ValueFromPipelineByPropertyName)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [string]$ComputerName ) <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” }
That’s strange. It just processed the CSV’s final row. What exactly is going on? Because you skipped over a notion that isn’t necessary when generating functions without pipeline support, it just performed the function for the final row.
Remember to Include the Process Block!
When you need to create a function that involves pipeline support, you must include (at a minimum) an “embedded” block inside of your function called. process. This process block tells PowerShell that when receiving Input to the Pipeline, to run the function for every iteration. By default, it will only execute the last one.
Other blocks, such as begin and end, may theoretically be added, although scripters don’t utilize them nearly as often.
I’ll add a process block with the code inside to instruct PowerShell to run this function for every object that comes in.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory,ValueFromPipelineByPropertyName)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [string]$ComputerName ) process { <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” } }
The Version and ComputerName attributes received from Import-Csv were now provided to Install-Office and tied to the Version and ComputerName arguments.
Resources
Check out my blog article on PowerShell functions to learn more about how function parameters operate.
I also recommend that you have a look at my Pluralsight course, Building Advanced PowerShell Functions and Modules, for an in-depth look at PowerShell functions, function parameters, and PowerShell modules.
.ComputerName
One or more parameters, also known as arguments, may be used with any PowerShell command. You’re not creating proper PowerShell code if you don’t use PowerShell parameters in your PowerShell functions!
You’ll cover almost every aspect of generating and using PowerShell parameters or arguments in this tutorial!
This is an excerpt from PowerShell for SysAdmins, which I wrote. Check it out if you want to learn PowerShell or pick up some tips and techniques.
What Is the Purpose of a Parameter?
When you first start writing functions, you’ll have the choice of including or excluding parameters, as well as the behavior of those arguments.
Consider the following scenario: you have a function that installs Microsoft Office. Perhaps it secretly invokes the Office installation inside the method. For our purposes, it doesn’t important what the function performs. With the name of the function and the script block, the basic function looks like this.
## Run the quiet installer here using the function Install-Office
In this case, you just ran Install-Office without any options and it took care of everything.
Whether the Install-Office function had parameters or not made no difference. It didn’t seem to have any necessary arguments; otherwise, PowerShell wouldn’t have allowed us execute it without one.
When Should You Use PowerShell Parameters?
There are several versions of Office. It’s possible that you’ll need to set up Office 2013 and 2016. There is currently no method to indicate this. Every time you wanted to modify the behavior, you could edit the function’s code.
You might, for instance, construct two distinct methods to install various versions.
‘I installed Office 2013.’ function Install-Office2013 Write-Host ‘Wheeeeeeeeeeeeeeeeee ‘I installed Office 2016.’ function Install-Office2016 Write-Host ‘Wheeeeeeeeeeeeeeeeee
It’s possible to do this, but it’s not scalable. It compels you to build a new function for each new version of Office that is released. When you don’t have to, you’ll have to duplicate a lot of code.
Instead, you’ll need a means to modify the function’s functionality by passing in various values at runtime. How do you go about doing this?
Yep! Parameters, or as some refer to them, arguments.
You must add at least one argument to this method if we want to install multiple versions of Office without having to change the code every time.
Before you rapidly come up with a PowerShell parameter, you must first ask yourself, “What is the smallest modification or changes you foresee to be required in this function?”
Keep in mind that you must restart this function without modifying any of the code inside it. The parameter in this case is presumably obvious; you need to provide a Version argument. When you’ve got a function with tens of lines of code, though, the solution isn’t always obvious. It will always assist if you answer the question as accurately as possible.
So you’re aware that a Version argument is required. So, what’s next? You may now add one, but there are various ways to skin that cat, as with any excellent programming language.
Based on my almost decade of PowerShell knowledge, I’ll teach you the “optimal” approach to generate parameters in this lesson. Keep in mind, though, that this isn’t the only method to create a parameter.
Positional parameters are anything that exists. You may use these parameters to provide values to parameters without having to mention the parameter name. Positional criteria are useful, but they aren’t “best practice.” Why? Because they are more difficult to understand, particularly when a function has a lot of arguments.
Using PowerShell to Create a Simple Parameter
To add a parameter to a function, you’ll need two things: a param block and the parameter itself. The param keyword is followed by a set of parentheses to create a param block.
[CmdletBinding()] function Install-Office ‘I installed Office 2016.’ param() Write-Host ‘Wheeeeeeeeeeeeeeeeee
The function’s real functionality hasn’t altered at this time. We’ve just finished installing some piping in preparation for the first parameter.
You’ll now construct the parameter once the param block is in place. The method I recommend for creating a parameter starts with the parameter block, then the type of parameter, and finally the parameter variable name.
[CmdletBinding()] function Install-Office [Parameter()] param([Parameter()] param([Parameter()] par Write-Host ‘I installed Office 2016.’ [string]$Version) ‘Wheeeeeeeeeeeeeeeeee
We’ve now generated a PowerShell function parameter, but what precisely happened?
Every parameter should include a parameter block, which is an optional but suggested component. It is “function plumbing,” similar to the param block, which prepares the parameter for adding further functionality. The kind of argument it is is defined on the second line.
We’ve opted to convert the Version option to a string in this situation. If an explicit type is defined, any value supplied to this argument will be “converted” to a string if it isn’t already one.
The kind is not required, although it is strongly recommended. Defining the sort of parameter it is will help you avoid a lot of unpleasant problems down the line. Have faith in me.
Now that you’ve established the option, you may use the Version argument in the Install-Office command to provide a version string, such as 2013. The value supplied to the Version parameter is also known as the arguments or values of parameters.
PS> Install-Office -Version 2013 I installed Office 2016. Yippee!
What exactly is going on here? You told it you wanted version 2013, yet it still says it’s installed version 2016. You must remember to rename the function’s code to a variable after adding an argument. When a parameter is supplied to a function, the variable is extended to the value that was passed.
Replace the static text 2016 with the Version parameter variable, changing the single quotes to double quotes to allow the variable to grow.
[CmdletBinding()] function Install-Office [Parameter()] param([Parameter()] param([Parameter()] par $Version) ([string]$Version) ([string]$Version) ( Host-Write “I downloaded and installed Office $Version. What a relief! “,,,,,,,,,,
As you can see, any value you provide the Version argument is sent into the method as the $Version variable.
Attribute for Mandatory Parameters
Remember how I said the [Parameter()] line was merely “function plumbing” that was required to get the function ready for further work? The extra labor I mentioned previously is adding parameter properties to a parameter.
A parameter does not have to be a variable placeholder. Parameter characteristics and parameter validity are concepts in PowerShell. The functionality of a parameter may be changed in a variety of ways using parameter attributes.
The Mandatory keyword, for example, is one of the most typical parameter properties you’ll use. You may call the Install-Office function without supplying the Version argument by default, and it will work perfectly. The parameter Version would be optional. Although the $Version variable within the code would not grow since there was no value, it would still execute.
When establishing a parameter, you’ll often want the user to utilize it all of the time. You’ll rely on the argument’s value somewhere in the function’s code, and if the parameter isn’t supplied, the function will fail. You wish to require the user to supply this parameter value to your function in certain instances. You’d want that parameter to be required.
Once you have the fundamental architecture in place, forcing users to utilize a parameter is straightforward. Within the parenthesis of the parameter, you must insert the term Mandatory. Once you’ve done that, calling the function without the argument will cause it to pause until a value is entered.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$Version ) Write-Host “I installed Office $Version. Yippee!” } PS> Install-Office cmdlet Install-Office at command pipeline position 1 Supply values for the following parameters: Version:
Until you supply a value for the Version argument, the function will wait. When you press Enter, PowerShell will run the function and then go on. If you specify a value for the argument, PowerShell will not ask you for it every time you run the command.
Validation Attributes for PowerShell Parameters
One of the most popular parameter features is to make a parameter obligatory, but you may also utilize parameter validation attributes. It’s always important in programming to limit user input as much as possible. Limiting the data that users (or even you!) may send to your functions or scripts can minimize redundant code in your function that must cater for a variety of scenarios.
Observational Learning
For example, I showed sending the number 2013 to the Install-Office method since I knew it would work. The code was written by myself! I’m guessing (never do this in code!) that anybody who understands anything would select either 2013 or 2016 as the version. What you think is plain to you may not be so evident to others.
If you want to be technical about versions, specifying the 2013 version as 15.0 and the 2016 version as 16.0 would be more correct if Microsoft was still using the versioning schema they used in the past. But what if, expecting they’ll give a version of 2013 or 2016, you’ve included code in the method that searches for files containing those versions or something else?
Here’s an example of when the $Version string could be used in a file path. It will fail or do something worse if someone passes a value that does not complete a folder name of Office2013 or Office2016, such as removing unexpected folders or changing items you didn’t account for.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$Version ) Get-ChildItem -Path “\SRV1InstallersOffice$Version” } PS> Install-Office -Version ‘15.0’ Get-ChildItem : Cannot find path ‘SRV1InstallersOffice15.0’ because it does not exist. At line:7 char:5 Get-ChildItem -Path “\SRV1InstallersOffice$Version” ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CategoryInfo : ObjectNotFound: (SRV1InstallersOffice15.0:String) [Get-ChildItem], ItemNotFoundExcep tion FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand
You may use PowerShell parameter validation to restrict the user’s input to what you anticipate.
Validation Attribute Using the ValidateSet Parameter
You may utilize a variety of parameter validation techniques. Run Get-Help about Functions Advanced Parameters for a complete list. In this case, the ValidateSet property would be the most appropriate.
You may use the ValidateSet validation property to define a list of values that can be used as parameter values. I’d want to make sure that the user may only provide these values since we’re only accounting for the strings 2013 or 2016. Otherwise, the function will instantly fail and tell them of the reason.
Under the original parameter keyword, you may add parameter validation characteristics. You can see an array of objects within the parentheses of the parameter property in this example; 2013 and 2016. A parameter validation property notifies PowerShell that only 2013 or 2016 are accepted options for Version. If you attempt to pass anything other than what’s in the set, you’ll get an error message stating that you only have a limited amount of possibilities.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateSet(‘2013′,’2016’)] [string]$Version ) Get-ChildItem -Path “\SRV1InstallersOffice$Version” } PS> Install-Office -Version 15.0 Install-Office : Cannot validate argument on parameter ‘Version’. The argument “15.0” does not belong to the set “2013,2016” specified by the ValidateSet attribute. Supply an argument that is in the set and then try the command again. At line:1 char:25 Install-Office -Version 15.0 ~~~~ CategoryInfo : InvalidData: (:) [Install-Office], ParameterBindingValidationException FullyQualifiedErrorId : ParameterArgumentValidationError,Install-Office
The ValidateSet property is a frequently used validation attribute. Check out the Functions Advanced Parameters help topic by executing Get-Help about Functions Advanced Parameters for a comprehensive list of all the ways parameter values may be controlled.
Sets of Parameters
Assume you only want specific PowerShell parameters to be used in conjunction with other parameters. Perhaps you’ve given the Install-Office function a Path argument. This route will install the installer in whatever version it is. You don’t want the user to utilize the Version option in this scenario.
You need Sets of Parameters.
Sets of parameters may be created that can only be used with other parameters from the same set. You may now get the path to the installer by using both the Version and the Path parameters in the code below.
[CmdletBinding()] function Install-Office [Parameter(Mandatory)] param([Parameter(Mandatory)] param([Parameter(Mand [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(Mandatory)], [string]$Version, [string]$Version, [string]$Version, [string] [string] Path $Path) Get-ChildItem -Path “SRV1InstallersOffice$Version” if ($Version) elseif ($Path) Get-ChildItem -Path $Path -Path -Path -Path -Path -Path -Path -Path -Path –
However, this creates a difficulty since the user might utilize both parameters. Furthermore, since both parameters are required, they will be obliged to utilize both even if it is not what you desire. To address this, we may group all of the parameters together in a parameter set like the one below.
[CmdletBinding()] function Install-Office [Parameter(Mandatory,ParameterSetName = ‘ByVersion’)] param([Parameter(Mandatory,ParameterSetName = ‘ByVersion’)] param([Parameter(Mand [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(Mandatory, parameterSetName = ‘ByPath’)], [string]$Version, [string]$Version, [string]$Version, [string]$Version, [string]$Version, [string]$ [string] Path $Path) Get-ChildItem -Path “SRV1InstallersOffice$Version” if ($Version) elseif ($Path) Get-ChildItem -Path $Path -Path -Path -Path -Path -Path -Path -Path -Path –
You may control groups of parameters together by specifying a parameter set name on each parameter.
Set of Default Parameters
What happens if a user attempts to run Install-Office without specifying any parameters? You’ll get a nice error notice if this isn’t accounted for.
There are no parameters configured.
You’ll need to establish a default parameter set in the CmdletBinding() section to correct this. Changing [CmdletBinding()] to [CmdletBinding(DefaultParameterSetName = ‘ByVersion’)] informs the function to choose a parameter set to use if no arguments were explicitly supplied.
Because it will be utilizing that parameter set, anytime you run Install-Office, it will request for the Version parameter.
Input to the Pipeline
You’ve been generating functions with a PowerShell parameter that can only be given using the standard -ParameterName Value syntax in the examples so far. However, as you’ve seen, PowerShell includes an easy pipeline that lets you transfer objects from one command to the next without having to use the “usual” syntax.
You “chain” instructions together using the pipe symbol | when using the pipeline, enabling a user to submit the output of a command like Get-Service to Start-Service as a shortcut for giving the Name argument to Start-Service.
Using a Loop the “Old” Way
You’re installing Office and have a Version argument in the custom function you’re dealing with. Let’s imagine you have a CSV file with a list of machine names in one row and the Office version that has to be installed on them in the second row. This is what the CSV file looks like:
PC1,2016,PC2,2013,PC3,2016,PC4,2016,PC5,2016,PC6,2016,PC7,2016,PC8,2016,PC9
On that machine, you’d want to install the version of Office that’s next to it.
First, you’ll need to add a ComputerName argument to the method so that each iteration of the function gets a distinct computer name. I’ve added a Write-Host instance to observe how the variables grow within the fictitious function, and I’ve built some pseudocode to mimic some code that may be in the fictional function.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter()] [string]$ComputerName ) <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” }
You may do this by reading the CSV file and giving the data for the computer name and version to the Install-Office function after you’ve added the ComputerName argument to the function.
$computers = Import-Csv -Path ‘C:ComputerOfficeVersions.csv’ foreach ($pc in $computers) { Install-Office -Version $_.Version -ComputerName $_.ComputerName }
Building Input to the Pipeline for Parameters
The “old” technique of reading the CSV rows and passing each row’s properties to the function is to use a loop to transfer each row’s properties to the function. Instead of using the foreach loop, you should utilize the pipeline in this section.
The function does not support the pipeline in its current state. It would seem logical to suppose that you could use the pipeline to feed each computer’s name and version to the function. We’re reading the CSV and handing it to Install-Office straight below, however this doesn’t work.
PS> Import-Csv -Path ‘C:ComputerOfficeVersions.csv’ | Install-Office
You may make all the assumptions you want, but that won’t make it work. When you know that Import-Csv sends the Version argument as an object property, you’ll see that we’re being requested for it. Why isn’t it functioning properly? Because you haven’t yet implemented pipeline support.
There are two kinds of Input to the Pipeline in a PowerShell function; ByValue (entire object) and ByPropertyName (a single object property). Which do you think is the best way to stitch together the output of Import-Csv to the input of Install-Office?
You could use the ByPropertyName function without any restructuring since Import-Csv already returns the properties Version and ComputerName because they’re columns in the CSV.
It’s a lot easier than you would expect to add pipeline support to a custom function. ValueFromPipeline or ValueFromPipelineByPropertyName are two keywords that indicate a parameter attribute.
You’ll use ValueFromPipelineByPropertyName in this example to link the ComputerName and Version properties returned from Import-Csv to the Version and ComputerName arguments of Install-Office.
Because we want to bind both of these arguments, you’ll need to add this keyword to each of them, as seen below, and then restart the function using the pipeline.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory,ValueFromPipelineByPropertyName)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [string]$ComputerName ) <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” }
That’s strange. It just processed the CSV’s final row. What exactly is going on? Because you skipped over a notion that isn’t necessary when generating functions without pipeline support, it just performed the function for the final row.
Remember to Include the Process Block!
When you need to create a function that involves pipeline support, you must include (at a minimum) an “embedded” block inside of your function called. process. This process block tells PowerShell that when receiving Input to the Pipeline, to run the function for every iteration. By default, it will only execute the last one.
Other blocks, such as begin and end, may theoretically be added, although scripters don’t utilize them nearly as often.
I’ll add a process block with the code inside to instruct PowerShell to run this function for every object that comes in.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory,ValueFromPipelineByPropertyName)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [string]$ComputerName ) process { <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” } }
The Version and ComputerName attributes received from Import-Csv were now provided to Install-Office and tied to the Version and ComputerName arguments.
Resources
Check out my blog article on PowerShell functions to learn more about how function parameters operate.
I also recommend that you have a look at my Pluralsight course, Building Advanced PowerShell Functions and Modules, for an in-depth look at PowerShell functions, function parameters, and PowerShell modules.
.ComputerName
One or more parameters, also known as arguments, may be used with any PowerShell command. You’re not creating proper PowerShell code if you don’t use PowerShell parameters in your PowerShell functions!
You’ll cover almost every aspect of generating and using PowerShell parameters or arguments in this tutorial!
This is an excerpt from PowerShell for SysAdmins, which I wrote. Check it out if you want to learn PowerShell or pick up some tips and techniques.
What Is the Purpose of a Parameter?
When you first start writing functions, you’ll have the choice of including or excluding parameters, as well as the behavior of those arguments.
Consider the following scenario: you have a function that installs Microsoft Office. Perhaps it secretly invokes the Office installation inside the method. For our purposes, it doesn’t important what the function performs. With the name of the function and the script block, the basic function looks like this.
## Run the quiet installer here using the function Install-Office
In this case, you just ran Install-Office without any options and it took care of everything.
Whether the Install-Office function had parameters or not made no difference. It didn’t seem to have any necessary arguments; otherwise, PowerShell wouldn’t have allowed us execute it without one.
When Should You Use PowerShell Parameters?
There are several versions of Office. It’s possible that you’ll need to set up Office 2013 and 2016. There is currently no method to indicate this. Every time you wanted to modify the behavior, you could edit the function’s code.
You might, for instance, construct two distinct methods to install various versions.
‘I installed Office 2013.’ function Install-Office2013 Write-Host ‘Wheeeeeeeeeeeeeeeeee ‘I installed Office 2016.’ function Install-Office2016 Write-Host ‘Wheeeeeeeeeeeeeeeeee
It’s possible to do this, but it’s not scalable. It compels you to build a new function for each new version of Office that is released. When you don’t have to, you’ll have to duplicate a lot of code.
Instead, you’ll need a means to modify the function’s functionality by passing in various values at runtime. How do you go about doing this?
Yep! Parameters, or as some refer to them, arguments.
You must add at least one argument to this method if we want to install multiple versions of Office without having to change the code every time.
Before you rapidly come up with a PowerShell parameter, you must first ask yourself, “What is the smallest modification or changes you foresee to be required in this function?”
Keep in mind that you must restart this function without modifying any of the code inside it. The parameter in this case is presumably obvious; you need to provide a Version argument. When you’ve got a function with tens of lines of code, though, the solution isn’t always obvious. It will always assist if you answer the question as accurately as possible.
So you’re aware that a Version argument is required. So, what’s next? You may now add one, but there are various ways to skin that cat, as with any excellent programming language.
Based on my almost decade of PowerShell knowledge, I’ll teach you the “optimal” approach to generate parameters in this lesson. Keep in mind, though, that this isn’t the only method to create a parameter.
Positional parameters are anything that exists. You may use these parameters to provide values to parameters without having to mention the parameter name. Positional criteria are useful, but they aren’t “best practice.” Why? Because they are more difficult to understand, particularly when a function has a lot of arguments.
Using PowerShell to Create a Simple Parameter
To add a parameter to a function, you’ll need two things: a param block and the parameter itself. The param keyword is followed by a set of parentheses to create a param block.
[CmdletBinding()] function Install-Office ‘I installed Office 2016.’ param() Write-Host ‘Wheeeeeeeeeeeeeeeeee
The function’s real functionality hasn’t altered at this time. We’ve just finished installing some piping in preparation for the first parameter.
You’ll now construct the parameter once the param block is in place. The method I recommend for creating a parameter starts with the parameter block, then the type of parameter, and finally the parameter variable name.
[CmdletBinding()] function Install-Office [Parameter()] param([Parameter()] param([Parameter()] par Write-Host ‘I installed Office 2016.’ [string]$Version) ‘Wheeeeeeeeeeeeeeeeee
We’ve now generated a PowerShell function parameter, but what precisely happened?
Every parameter should include a parameter block, which is an optional but suggested component. It is “function plumbing,” similar to the param block, which prepares the parameter for adding further functionality. The kind of argument it is is defined on the second line.
We’ve opted to convert the Version option to a string in this situation. If an explicit type is defined, any value supplied to this argument will be “converted” to a string if it isn’t already one.
The kind is not required, although it is strongly recommended. Defining the sort of parameter it is will help you avoid a lot of unpleasant problems down the line. Have faith in me.
Now that you’ve established the option, you may use the Version argument in the Install-Office command to provide a version string, such as 2013. The value supplied to the Version parameter is also known as the arguments or values of parameters.
PS> Install-Office -Version 2013 I installed Office 2016. Yippee!
What exactly is going on here? You told it you wanted version 2013, yet it still says it’s installed version 2016. You must remember to rename the function’s code to a variable after adding an argument. When a parameter is supplied to a function, the variable is extended to the value that was passed.
Replace the static text 2016 with the Version parameter variable, changing the single quotes to double quotes to allow the variable to grow.
[CmdletBinding()] function Install-Office [Parameter()] param([Parameter()] param([Parameter()] par $Version) ([string]$Version) ([string]$Version) ( Host-Write “I downloaded and installed Office $Version. What a relief! “,,,,,,,,,,
As you can see, any value you provide the Version argument is sent into the method as the $Version variable.
Attribute for Mandatory Parameters
Remember how I said the [Parameter()] line was merely “function plumbing” that was required to get the function ready for further work? The extra labor I mentioned previously is adding parameter properties to a parameter.
A parameter does not have to be a variable placeholder. Parameter characteristics and parameter validity are concepts in PowerShell. The functionality of a parameter may be changed in a variety of ways using parameter attributes.
The Mandatory keyword, for example, is one of the most typical parameter properties you’ll use. You may call the Install-Office function without supplying the Version argument by default, and it will work perfectly. The parameter Version would be optional. Although the $Version variable within the code would not grow since there was no value, it would still execute.
When establishing a parameter, you’ll often want the user to utilize it all of the time. You’ll rely on the argument’s value somewhere in the function’s code, and if the parameter isn’t supplied, the function will fail. You wish to require the user to supply this parameter value to your function in certain instances. You’d want that parameter to be required.
Once you have the fundamental architecture in place, forcing users to utilize a parameter is straightforward. Within the parenthesis of the parameter, you must insert the term Mandatory. Once you’ve done that, calling the function without the argument will cause it to pause until a value is entered.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$Version ) Write-Host “I installed Office $Version. Yippee!” } PS> Install-Office cmdlet Install-Office at command pipeline position 1 Supply values for the following parameters: Version:
Until you supply a value for the Version argument, the function will wait. When you press Enter, PowerShell will run the function and then go on. If you specify a value for the argument, PowerShell will not ask you for it every time you run the command.
Validation Attributes for PowerShell Parameters
One of the most popular parameter features is to make a parameter obligatory, but you may also utilize parameter validation attributes. It’s always important in programming to limit user input as much as possible. Limiting the data that users (or even you!) may send to your functions or scripts can minimize redundant code in your function that must cater for a variety of scenarios.
Observational Learning
For example, I showed sending the number 2013 to the Install-Office method since I knew it would work. The code was written by myself! I’m guessing (never do this in code!) that anybody who understands anything would select either 2013 or 2016 as the version. What you think is plain to you may not be so evident to others.
If you want to be technical about versions, specifying the 2013 version as 15.0 and the 2016 version as 16.0 would be more correct if Microsoft was still using the versioning schema they used in the past. But what if, expecting they’ll give a version of 2013 or 2016, you’ve included code in the method that searches for files containing those versions or something else?
Here’s an example of when the $Version string could be used in a file path. It will fail or do something worse if someone passes a value that does not complete a folder name of Office2013 or Office2016, such as removing unexpected folders or changing items you didn’t account for.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$Version ) Get-ChildItem -Path “\SRV1InstallersOffice$Version” } PS> Install-Office -Version ‘15.0’ Get-ChildItem : Cannot find path ‘SRV1InstallersOffice15.0’ because it does not exist. At line:7 char:5 Get-ChildItem -Path “\SRV1InstallersOffice$Version” ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CategoryInfo : ObjectNotFound: (SRV1InstallersOffice15.0:String) [Get-ChildItem], ItemNotFoundExcep tion FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand
You may use PowerShell parameter validation to restrict the user’s input to what you anticipate.
Validation Attribute Using the ValidateSet Parameter
You may utilize a variety of parameter validation techniques. Run Get-Help about Functions Advanced Parameters for a complete list. In this case, the ValidateSet property would be the most appropriate.
You may use the ValidateSet validation property to define a list of values that can be used as parameter values. I’d want to make sure that the user may only provide these values since we’re only accounting for the strings 2013 or 2016. Otherwise, the function will instantly fail and tell them of the reason.
Under the original parameter keyword, you may add parameter validation characteristics. You can see an array of objects within the parentheses of the parameter property in this example; 2013 and 2016. A parameter validation property notifies PowerShell that only 2013 or 2016 are accepted options for Version. If you attempt to pass anything other than what’s in the set, you’ll get an error message stating that you only have a limited amount of possibilities.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateSet(‘2013′,’2016’)] [string]$Version ) Get-ChildItem -Path “\SRV1InstallersOffice$Version” } PS> Install-Office -Version 15.0 Install-Office : Cannot validate argument on parameter ‘Version’. The argument “15.0” does not belong to the set “2013,2016” specified by the ValidateSet attribute. Supply an argument that is in the set and then try the command again. At line:1 char:25 Install-Office -Version 15.0 ~~~~ CategoryInfo : InvalidData: (:) [Install-Office], ParameterBindingValidationException FullyQualifiedErrorId : ParameterArgumentValidationError,Install-Office
The ValidateSet property is a frequently used validation attribute. Check out the Functions Advanced Parameters help topic by executing Get-Help about Functions Advanced Parameters for a comprehensive list of all the ways parameter values may be controlled.
Sets of Parameters
Assume you only want specific PowerShell parameters to be used in conjunction with other parameters. Perhaps you’ve given the Install-Office function a Path argument. This route will install the installer in whatever version it is. You don’t want the user to utilize the Version option in this scenario.
You need Sets of Parameters.
Sets of parameters may be created that can only be used with other parameters from the same set. You may now get the path to the installer by using both the Version and the Path parameters in the code below.
[CmdletBinding()] function Install-Office [Parameter(Mandatory)] param([Parameter(Mandatory)] param([Parameter(Mand [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(Mandatory)], [string]$Version, [string]$Version, [string]$Version, [string] [string] Path $Path) Get-ChildItem -Path “SRV1InstallersOffice$Version” if ($Version) elseif ($Path) Get-ChildItem -Path $Path -Path -Path -Path -Path -Path -Path -Path -Path –
However, this creates a difficulty since the user might utilize both parameters. Furthermore, since both parameters are required, they will be obliged to utilize both even if it is not what you desire. To address this, we may group all of the parameters together in a parameter set like the one below.
[CmdletBinding()] function Install-Office [Parameter(Mandatory,ParameterSetName = ‘ByVersion’)] param([Parameter(Mandatory,ParameterSetName = ‘ByVersion’)] param([Parameter(Mand [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(Mandatory, parameterSetName = ‘ByPath’)], [string]$Version, [string]$Version, [string]$Version, [string]$Version, [string]$Version, [string]$ [string] Path $Path) Get-ChildItem -Path “SRV1InstallersOffice$Version” if ($Version) elseif ($Path) Get-ChildItem -Path $Path -Path -Path -Path -Path -Path -Path -Path -Path –
You may control groups of parameters together by specifying a parameter set name on each parameter.
Set of Default Parameters
What happens if a user attempts to run Install-Office without specifying any parameters? You’ll get a nice error notice if this isn’t accounted for.
There are no parameters configured.
You’ll need to establish a default parameter set in the CmdletBinding() section to correct this. Changing [CmdletBinding()] to [CmdletBinding(DefaultParameterSetName = ‘ByVersion’)] informs the function to choose a parameter set to use if no arguments were explicitly supplied.
Because it will be utilizing that parameter set, anytime you run Install-Office, it will request for the Version parameter.
Input to the Pipeline
You’ve been generating functions with a PowerShell parameter that can only be given using the standard -ParameterName Value syntax in the examples so far. However, as you’ve seen, PowerShell includes an easy pipeline that lets you transfer objects from one command to the next without having to use the “usual” syntax.
You “chain” instructions together using the pipe symbol | when using the pipeline, enabling a user to submit the output of a command like Get-Service to Start-Service as a shortcut for giving the Name argument to Start-Service.
Using a Loop the “Old” Way
You’re installing Office and have a Version argument in the custom function you’re dealing with. Let’s imagine you have a CSV file with a list of machine names in one row and the Office version that has to be installed on them in the second row. This is what the CSV file looks like:
PC1,2016,PC2,2013,PC3,2016,PC4,2016,PC5,2016,PC6,2016,PC7,2016,PC8,2016,PC9
On that machine, you’d want to install the version of Office that’s next to it.
First, you’ll need to add a ComputerName argument to the method so that each iteration of the function gets a distinct computer name. I’ve added a Write-Host instance to observe how the variables grow within the fictitious function, and I’ve built some pseudocode to mimic some code that may be in the fictional function.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter()] [string]$ComputerName ) <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” }
You may do this by reading the CSV file and giving the data for the computer name and version to the Install-Office function after you’ve added the ComputerName argument to the function.
$computers = Import-Csv -Path ‘C:ComputerOfficeVersions.csv’ foreach ($pc in $computers) { Install-Office -Version $_.Version -ComputerName $_.ComputerName }
Building Input to the Pipeline for Parameters
The “old” technique of reading the CSV rows and passing each row’s properties to the function is to use a loop to transfer each row’s properties to the function. Instead of using the foreach loop, you should utilize the pipeline in this section.
The function does not support the pipeline in its current state. It would seem logical to suppose that you could use the pipeline to feed each computer’s name and version to the function. We’re reading the CSV and handing it to Install-Office straight below, however this doesn’t work.
PS> Import-Csv -Path ‘C:ComputerOfficeVersions.csv’ | Install-Office
You may make all the assumptions you want, but that won’t make it work. When you know that Import-Csv sends the Version argument as an object property, you’ll see that we’re being requested for it. Why isn’t it functioning properly? Because you haven’t yet implemented pipeline support.
There are two kinds of Input to the Pipeline in a PowerShell function; ByValue (entire object) and ByPropertyName (a single object property). Which do you think is the best way to stitch together the output of Import-Csv to the input of Install-Office?
You could use the ByPropertyName function without any restructuring since Import-Csv already returns the properties Version and ComputerName because they’re columns in the CSV.
It’s a lot easier than you would expect to add pipeline support to a custom function. ValueFromPipeline or ValueFromPipelineByPropertyName are two keywords that indicate a parameter attribute.
You’ll use ValueFromPipelineByPropertyName in this example to link the ComputerName and Version properties returned from Import-Csv to the Version and ComputerName arguments of Install-Office.
Because we want to bind both of these arguments, you’ll need to add this keyword to each of them, as seen below, and then restart the function using the pipeline.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory,ValueFromPipelineByPropertyName)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [string]$ComputerName ) <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” }
That’s strange. It just processed the CSV’s final row. What exactly is going on? Because you skipped over a notion that isn’t necessary when generating functions without pipeline support, it just performed the function for the final row.
Remember to Include the Process Block!
When you need to create a function that involves pipeline support, you must include (at a minimum) an “embedded” block inside of your function called. process. This process block tells PowerShell that when receiving Input to the Pipeline, to run the function for every iteration. By default, it will only execute the last one.
Other blocks, such as begin and end, may theoretically be added, although scripters don’t utilize them nearly as often.
I’ll add a process block with the code inside to instruct PowerShell to run this function for every object that comes in.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory,ValueFromPipelineByPropertyName)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [string]$ComputerName ) process { <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” } }
The Version and ComputerName attributes received from Import-Csv were now provided to Install-Office and tied to the Version and ComputerName arguments.
Resources
Check out my blog article on PowerShell functions to learn more about how function parameters operate.
I also recommend that you have a look at my Pluralsight course, Building Advanced PowerShell Functions and Modules, for an in-depth look at PowerShell functions, function parameters, and PowerShell modules.
.ComputerName
One or more parameters, also known as arguments, may be used with any PowerShell command. You’re not creating proper PowerShell code if you don’t use PowerShell parameters in your PowerShell functions!
You’ll cover almost every aspect of generating and using PowerShell parameters or arguments in this tutorial!
This is an excerpt from PowerShell for SysAdmins, which I wrote. Check it out if you want to learn PowerShell or pick up some tips and techniques.
What Is the Purpose of a Parameter?
When you first start writing functions, you’ll have the choice of including or excluding parameters, as well as the behavior of those arguments.
Consider the following scenario: you have a function that installs Microsoft Office. Perhaps it secretly invokes the Office installation inside the method. For our purposes, it doesn’t important what the function performs. With the name of the function and the script block, the basic function looks like this.
## Run the quiet installer here using the function Install-Office
In this case, you just ran Install-Office without any options and it took care of everything.
Whether the Install-Office function had parameters or not made no difference. It didn’t seem to have any necessary arguments; otherwise, PowerShell wouldn’t have allowed us execute it without one.
When Should You Use PowerShell Parameters?
There are several versions of Office. It’s possible that you’ll need to set up Office 2013 and 2016. There is currently no method to indicate this. Every time you wanted to modify the behavior, you could edit the function’s code.
You might, for instance, construct two distinct methods to install various versions.
‘I installed Office 2013.’ function Install-Office2013 Write-Host ‘Wheeeeeeeeeeeeeeeeee ‘I installed Office 2016.’ function Install-Office2016 Write-Host ‘Wheeeeeeeeeeeeeeeeee
It’s possible to do this, but it’s not scalable. It compels you to build a new function for each new version of Office that is released. When you don’t have to, you’ll have to duplicate a lot of code.
Instead, you’ll need a means to modify the function’s functionality by passing in various values at runtime. How do you go about doing this?
Yep! Parameters, or as some refer to them, arguments.
You must add at least one argument to this method if we want to install multiple versions of Office without having to change the code every time.
Before you rapidly come up with a PowerShell parameter, you must first ask yourself, “What is the smallest modification or changes you foresee to be required in this function?”
Keep in mind that you must restart this function without modifying any of the code inside it. The parameter in this case is presumably obvious; you need to provide a Version argument. When you’ve got a function with tens of lines of code, though, the solution isn’t always obvious. It will always assist if you answer the question as accurately as possible.
So you’re aware that a Version argument is required. So, what’s next? You may now add one, but there are various ways to skin that cat, as with any excellent programming language.
Based on my almost decade of PowerShell knowledge, I’ll teach you the “optimal” approach to generate parameters in this lesson. Keep in mind, though, that this isn’t the only method to create a parameter.
Positional parameters are anything that exists. You may use these parameters to provide values to parameters without having to mention the parameter name. Positional criteria are useful, but they aren’t “best practice.” Why? Because they are more difficult to understand, particularly when a function has a lot of arguments.
Using PowerShell to Create a Simple Parameter
To add a parameter to a function, you’ll need two things: a param block and the parameter itself. The param keyword is followed by a set of parentheses to create a param block.
[CmdletBinding()] function Install-Office ‘I installed Office 2016.’ param() Write-Host ‘Wheeeeeeeeeeeeeeeeee
The function’s real functionality hasn’t altered at this time. We’ve just finished installing some piping in preparation for the first parameter.
You’ll now construct the parameter once the param block is in place. The method I recommend for creating a parameter starts with the parameter block, then the type of parameter, and finally the parameter variable name.
[CmdletBinding()] function Install-Office [Parameter()] param([Parameter()] param([Parameter()] par Write-Host ‘I installed Office 2016.’ [string]$Version) ‘Wheeeeeeeeeeeeeeeeee
We’ve now generated a PowerShell function parameter, but what precisely happened?
Every parameter should include a parameter block, which is an optional but suggested component. It is “function plumbing,” similar to the param block, which prepares the parameter for adding further functionality. The kind of argument it is is defined on the second line.
We’ve opted to convert the Version option to a string in this situation. If an explicit type is defined, any value supplied to this argument will be “converted” to a string if it isn’t already one.
The kind is not required, although it is strongly recommended. Defining the sort of parameter it is will help you avoid a lot of unpleasant problems down the line. Have faith in me.
Now that you’ve established the option, you may use the Version argument in the Install-Office command to provide a version string, such as 2013. The value supplied to the Version parameter is also known as the arguments or values of parameters.
PS> Install-Office -Version 2013 I installed Office 2016. Yippee!
What exactly is going on here? You told it you wanted version 2013, yet it still says it’s installed version 2016. You must remember to rename the function’s code to a variable after adding an argument. When a parameter is supplied to a function, the variable is extended to the value that was passed.
Replace the static text 2016 with the Version parameter variable, changing the single quotes to double quotes to allow the variable to grow.
[CmdletBinding()] function Install-Office [Parameter()] param([Parameter()] param([Parameter()] par $Version) ([string]$Version) ([string]$Version) ( Host-Write “I downloaded and installed Office $Version. What a relief! “,,,,,,,,,,
As you can see, any value you provide the Version argument is sent into the method as the $Version variable.
Attribute for Mandatory Parameters
Remember how I said the [Parameter()] line was merely “function plumbing” that was required to get the function ready for further work? The extra labor I mentioned previously is adding parameter properties to a parameter.
A parameter does not have to be a variable placeholder. Parameter characteristics and parameter validity are concepts in PowerShell. The functionality of a parameter may be changed in a variety of ways using parameter attributes.
The Mandatory keyword, for example, is one of the most typical parameter properties you’ll use. You may call the Install-Office function without supplying the Version argument by default, and it will work perfectly. The parameter Version would be optional. Although the $Version variable within the code would not grow since there was no value, it would still execute.
When establishing a parameter, you’ll often want the user to utilize it all of the time. You’ll rely on the argument’s value somewhere in the function’s code, and if the parameter isn’t supplied, the function will fail. You wish to require the user to supply this parameter value to your function in certain instances. You’d want that parameter to be required.
Once you have the fundamental architecture in place, forcing users to utilize a parameter is straightforward. Within the parenthesis of the parameter, you must insert the term Mandatory. Once you’ve done that, calling the function without the argument will cause it to pause until a value is entered.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$Version ) Write-Host “I installed Office $Version. Yippee!” } PS> Install-Office cmdlet Install-Office at command pipeline position 1 Supply values for the following parameters: Version:
Until you supply a value for the Version argument, the function will wait. When you press Enter, PowerShell will run the function and then go on. If you specify a value for the argument, PowerShell will not ask you for it every time you run the command.
Validation Attributes for PowerShell Parameters
One of the most popular parameter features is to make a parameter obligatory, but you may also utilize parameter validation attributes. It’s always important in programming to limit user input as much as possible. Limiting the data that users (or even you!) may send to your functions or scripts can minimize redundant code in your function that must cater for a variety of scenarios.
Observational Learning
For example, I showed sending the number 2013 to the Install-Office method since I knew it would work. The code was written by myself! I’m guessing (never do this in code!) that anybody who understands anything would select either 2013 or 2016 as the version. What you think is plain to you may not be so evident to others.
If you want to be technical about versions, specifying the 2013 version as 15.0 and the 2016 version as 16.0 would be more correct if Microsoft was still using the versioning schema they used in the past. But what if, expecting they’ll give a version of 2013 or 2016, you’ve included code in the method that searches for files containing those versions or something else?
Here’s an example of when the $Version string could be used in a file path. It will fail or do something worse if someone passes a value that does not complete a folder name of Office2013 or Office2016, such as removing unexpected folders or changing items you didn’t account for.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$Version ) Get-ChildItem -Path “\SRV1InstallersOffice$Version” } PS> Install-Office -Version ‘15.0’ Get-ChildItem : Cannot find path ‘SRV1InstallersOffice15.0’ because it does not exist. At line:7 char:5 Get-ChildItem -Path “\SRV1InstallersOffice$Version” ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CategoryInfo : ObjectNotFound: (SRV1InstallersOffice15.0:String) [Get-ChildItem], ItemNotFoundExcep tion FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand
You may use PowerShell parameter validation to restrict the user’s input to what you anticipate.
Validation Attribute Using the ValidateSet Parameter
You may utilize a variety of parameter validation techniques. Run Get-Help about Functions Advanced Parameters for a complete list. In this case, the ValidateSet property would be the most appropriate.
You may use the ValidateSet validation property to define a list of values that can be used as parameter values. I’d want to make sure that the user may only provide these values since we’re only accounting for the strings 2013 or 2016. Otherwise, the function will instantly fail and tell them of the reason.
Under the original parameter keyword, you may add parameter validation characteristics. You can see an array of objects within the parentheses of the parameter property in this example; 2013 and 2016. A parameter validation property notifies PowerShell that only 2013 or 2016 are accepted options for Version. If you attempt to pass anything other than what’s in the set, you’ll get an error message stating that you only have a limited amount of possibilities.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateSet(‘2013′,’2016’)] [string]$Version ) Get-ChildItem -Path “\SRV1InstallersOffice$Version” } PS> Install-Office -Version 15.0 Install-Office : Cannot validate argument on parameter ‘Version’. The argument “15.0” does not belong to the set “2013,2016” specified by the ValidateSet attribute. Supply an argument that is in the set and then try the command again. At line:1 char:25 Install-Office -Version 15.0 ~~~~ CategoryInfo : InvalidData: (:) [Install-Office], ParameterBindingValidationException FullyQualifiedErrorId : ParameterArgumentValidationError,Install-Office
The ValidateSet property is a frequently used validation attribute. Check out the Functions Advanced Parameters help topic by executing Get-Help about Functions Advanced Parameters for a comprehensive list of all the ways parameter values may be controlled.
Sets of Parameters
Assume you only want specific PowerShell parameters to be used in conjunction with other parameters. Perhaps you’ve given the Install-Office function a Path argument. This route will install the installer in whatever version it is. You don’t want the user to utilize the Version option in this scenario.
You need Sets of Parameters.
Sets of parameters may be created that can only be used with other parameters from the same set. You may now get the path to the installer by using both the Version and the Path parameters in the code below.
[CmdletBinding()] function Install-Office [Parameter(Mandatory)] param([Parameter(Mandatory)] param([Parameter(Mand [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(Mandatory)], [string]$Version, [string]$Version, [string]$Version, [string] [string] Path $Path) Get-ChildItem -Path “SRV1InstallersOffice$Version” if ($Version) elseif ($Path) Get-ChildItem -Path $Path -Path -Path -Path -Path -Path -Path -Path -Path –
However, this creates a difficulty since the user might utilize both parameters. Furthermore, since both parameters are required, they will be obliged to utilize both even if it is not what you desire. To address this, we may group all of the parameters together in a parameter set like the one below.
[CmdletBinding()] function Install-Office [Parameter(Mandatory,ParameterSetName = ‘ByVersion’)] param([Parameter(Mandatory,ParameterSetName = ‘ByVersion’)] param([Parameter(Mand [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(Mandatory, parameterSetName = ‘ByPath’)], [string]$Version, [string]$Version, [string]$Version, [string]$Version, [string]$Version, [string]$ [string] Path $Path) Get-ChildItem -Path “SRV1InstallersOffice$Version” if ($Version) elseif ($Path) Get-ChildItem -Path $Path -Path -Path -Path -Path -Path -Path -Path -Path –
You may control groups of parameters together by specifying a parameter set name on each parameter.
Set of Default Parameters
What happens if a user attempts to run Install-Office without specifying any parameters? You’ll get a nice error notice if this isn’t accounted for.
There are no parameters configured.
You’ll need to establish a default parameter set in the CmdletBinding() section to correct this. Changing [CmdletBinding()] to [CmdletBinding(DefaultParameterSetName = ‘ByVersion’)] informs the function to choose a parameter set to use if no arguments were explicitly supplied.
Because it will be utilizing that parameter set, anytime you run Install-Office, it will request for the Version parameter.
Input to the Pipeline
You’ve been generating functions with a PowerShell parameter that can only be given using the standard -ParameterName Value syntax in the examples so far. However, as you’ve seen, PowerShell includes an easy pipeline that lets you transfer objects from one command to the next without having to use the “usual” syntax.
You “chain” instructions together using the pipe symbol | when using the pipeline, enabling a user to submit the output of a command like Get-Service to Start-Service as a shortcut for giving the Name argument to Start-Service.
Using a Loop the “Old” Way
You’re installing Office and have a Version argument in the custom function you’re dealing with. Let’s imagine you have a CSV file with a list of machine names in one row and the Office version that has to be installed on them in the second row. This is what the CSV file looks like:
PC1,2016,PC2,2013,PC3,2016,PC4,2016,PC5,2016,PC6,2016,PC7,2016,PC8,2016,PC9
On that machine, you’d want to install the version of Office that’s next to it.
First, you’ll need to add a ComputerName argument to the method so that each iteration of the function gets a distinct computer name. I’ve added a Write-Host instance to observe how the variables grow within the fictitious function, and I’ve built some pseudocode to mimic some code that may be in the fictional function.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter()] [string]$ComputerName ) <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” }
You may do this by reading the CSV file and giving the data for the computer name and version to the Install-Office function after you’ve added the ComputerName argument to the function.
$computers = Import-Csv -Path ‘C:ComputerOfficeVersions.csv’ foreach ($pc in $computers) { Install-Office -Version $_.Version -ComputerName $_.ComputerName }
Building Input to the Pipeline for Parameters
The “old” technique of reading the CSV rows and passing each row’s properties to the function is to use a loop to transfer each row’s properties to the function. Instead of using the foreach loop, you should utilize the pipeline in this section.
The function does not support the pipeline in its current state. It would seem logical to suppose that you could use the pipeline to feed each computer’s name and version to the function. We’re reading the CSV and handing it to Install-Office straight below, however this doesn’t work.
PS> Import-Csv -Path ‘C:ComputerOfficeVersions.csv’ | Install-Office
You may make all the assumptions you want, but that won’t make it work. When you know that Import-Csv sends the Version argument as an object property, you’ll see that we’re being requested for it. Why isn’t it functioning properly? Because you haven’t yet implemented pipeline support.
There are two kinds of Input to the Pipeline in a PowerShell function; ByValue (entire object) and ByPropertyName (a single object property). Which do you think is the best way to stitch together the output of Import-Csv to the input of Install-Office?
You could use the ByPropertyName function without any restructuring since Import-Csv already returns the properties Version and ComputerName because they’re columns in the CSV.
It’s a lot easier than you would expect to add pipeline support to a custom function. ValueFromPipeline or ValueFromPipelineByPropertyName are two keywords that indicate a parameter attribute.
You’ll use ValueFromPipelineByPropertyName in this example to link the ComputerName and Version properties returned from Import-Csv to the Version and ComputerName arguments of Install-Office.
Because we want to bind both of these arguments, you’ll need to add this keyword to each of them, as seen below, and then restart the function using the pipeline.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory,ValueFromPipelineByPropertyName)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [string]$ComputerName ) <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” }
That’s strange. It just processed the CSV’s final row. What exactly is going on? Because you skipped over a notion that isn’t necessary when generating functions without pipeline support, it just performed the function for the final row.
Remember to Include the Process Block!
When you need to create a function that involves pipeline support, you must include (at a minimum) an “embedded” block inside of your function called. process. This process block tells PowerShell that when receiving Input to the Pipeline, to run the function for every iteration. By default, it will only execute the last one.
Other blocks, such as begin and end, may theoretically be added, although scripters don’t utilize them nearly as often.
I’ll add a process block with the code inside to instruct PowerShell to run this function for every object that comes in.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory,ValueFromPipelineByPropertyName)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [string]$ComputerName ) process { <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” } }
The Version and ComputerName attributes received from Import-Csv were now provided to Install-Office and tied to the Version and ComputerName arguments.
Resources
Check out my blog article on PowerShell functions to learn more about how function parameters operate.
I also recommend that you have a look at my Pluralsight course, Building Advanced PowerShell Functions and Modules, for an in-depth look at PowerShell functions, function parameters, and PowerShell modules.
.ComputerName
One or more parameters, also known as arguments, may be used with any PowerShell command. You’re not creating proper PowerShell code if you don’t use PowerShell parameters in your PowerShell functions!
You’ll cover almost every aspect of generating and using PowerShell parameters or arguments in this tutorial!
This is an excerpt from PowerShell for SysAdmins, which I wrote. Check it out if you want to learn PowerShell or pick up some tips and techniques.
What Is the Purpose of a Parameter?
When you first start writing functions, you’ll have the choice of including or excluding parameters, as well as the behavior of those arguments.
Consider the following scenario: you have a function that installs Microsoft Office. Perhaps it secretly invokes the Office installation inside the method. For our purposes, it doesn’t important what the function performs. With the name of the function and the script block, the basic function looks like this.
## Run the quiet installer here using the function Install-Office
In this case, you just ran Install-Office without any options and it took care of everything.
Whether the Install-Office function had parameters or not made no difference. It didn’t seem to have any necessary arguments; otherwise, PowerShell wouldn’t have allowed us execute it without one.
When Should You Use PowerShell Parameters?
There are several versions of Office. It’s possible that you’ll need to set up Office 2013 and 2016. There is currently no method to indicate this. Every time you wanted to modify the behavior, you could edit the function’s code.
You might, for instance, construct two distinct methods to install various versions.
‘I installed Office 2013.’ function Install-Office2013 Write-Host ‘Wheeeeeeeeeeeeeeeeee ‘I installed Office 2016.’ function Install-Office2016 Write-Host ‘Wheeeeeeeeeeeeeeeeee
It’s possible to do this, but it’s not scalable. It compels you to build a new function for each new version of Office that is released. When you don’t have to, you’ll have to duplicate a lot of code.
Instead, you’ll need a means to modify the function’s functionality by passing in various values at runtime. How do you go about doing this?
Yep! Parameters, or as some refer to them, arguments.
You must add at least one argument to this method if we want to install multiple versions of Office without having to change the code every time.
Before you rapidly come up with a PowerShell parameter, you must first ask yourself, “What is the smallest modification or changes you foresee to be required in this function?”
Keep in mind that you must restart this function without modifying any of the code inside it. The parameter in this case is presumably obvious; you need to provide a Version argument. When you’ve got a function with tens of lines of code, though, the solution isn’t always obvious. It will always assist if you answer the question as accurately as possible.
So you’re aware that a Version argument is required. So, what’s next? You may now add one, but there are various ways to skin that cat, as with any excellent programming language.
Based on my almost decade of PowerShell knowledge, I’ll teach you the “optimal” approach to generate parameters in this lesson. Keep in mind, though, that this isn’t the only method to create a parameter.
Positional parameters are anything that exists. You may use these parameters to provide values to parameters without having to mention the parameter name. Positional criteria are useful, but they aren’t “best practice.” Why? Because they are more difficult to understand, particularly when a function has a lot of arguments.
Using PowerShell to Create a Simple Parameter
To add a parameter to a function, you’ll need two things: a param block and the parameter itself. The param keyword is followed by a set of parentheses to create a param block.
[CmdletBinding()] function Install-Office ‘I installed Office 2016.’ param() Write-Host ‘Wheeeeeeeeeeeeeeeeee
The function’s real functionality hasn’t altered at this time. We’ve just finished installing some piping in preparation for the first parameter.
You’ll now construct the parameter once the param block is in place. The method I recommend for creating a parameter starts with the parameter block, then the type of parameter, and finally the parameter variable name.
[CmdletBinding()] function Install-Office [Parameter()] param([Parameter()] param([Parameter()] par Write-Host ‘I installed Office 2016.’ [string]$Version) ‘Wheeeeeeeeeeeeeeeeee
We’ve now generated a PowerShell function parameter, but what precisely happened?
Every parameter should include a parameter block, which is an optional but suggested component. It is “function plumbing,” similar to the param block, which prepares the parameter for adding further functionality. The kind of argument it is is defined on the second line.
We’ve opted to convert the Version option to a string in this situation. If an explicit type is defined, any value supplied to this argument will be “converted” to a string if it isn’t already one.
The kind is not required, although it is strongly recommended. Defining the sort of parameter it is will help you avoid a lot of unpleasant problems down the line. Have faith in me.
Now that you’ve established the option, you may use the Version argument in the Install-Office command to provide a version string, such as 2013. The value supplied to the Version parameter is also known as the arguments or values of parameters.
PS> Install-Office -Version 2013 I installed Office 2016. Yippee!
What exactly is going on here? You told it you wanted version 2013, yet it still says it’s installed version 2016. You must remember to rename the function’s code to a variable after adding an argument. When a parameter is supplied to a function, the variable is extended to the value that was passed.
Replace the static text 2016 with the Version parameter variable, changing the single quotes to double quotes to allow the variable to grow.
[CmdletBinding()] function Install-Office [Parameter()] param([Parameter()] param([Parameter()] par $Version) ([string]$Version) ([string]$Version) ( Host-Write “I downloaded and installed Office $Version. What a relief! “,,,,,,,,,,
As you can see, any value you provide the Version argument is sent into the method as the $Version variable.
Attribute for Mandatory Parameters
Remember how I said the [Parameter()] line was merely “function plumbing” that was required to get the function ready for further work? The extra labor I mentioned previously is adding parameter properties to a parameter.
A parameter does not have to be a variable placeholder. Parameter characteristics and parameter validity are concepts in PowerShell. The functionality of a parameter may be changed in a variety of ways using parameter attributes.
The Mandatory keyword, for example, is one of the most typical parameter properties you’ll use. You may call the Install-Office function without supplying the Version argument by default, and it will work perfectly. The parameter Version would be optional. Although the $Version variable within the code would not grow since there was no value, it would still execute.
When establishing a parameter, you’ll often want the user to utilize it all of the time. You’ll rely on the argument’s value somewhere in the function’s code, and if the parameter isn’t supplied, the function will fail. You wish to require the user to supply this parameter value to your function in certain instances. You’d want that parameter to be required.
Once you have the fundamental architecture in place, forcing users to utilize a parameter is straightforward. Within the parenthesis of the parameter, you must insert the term Mandatory. Once you’ve done that, calling the function without the argument will cause it to pause until a value is entered.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$Version ) Write-Host “I installed Office $Version. Yippee!” } PS> Install-Office cmdlet Install-Office at command pipeline position 1 Supply values for the following parameters: Version:
Until you supply a value for the Version argument, the function will wait. When you press Enter, PowerShell will run the function and then go on. If you specify a value for the argument, PowerShell will not ask you for it every time you run the command.
Validation Attributes for PowerShell Parameters
One of the most popular parameter features is to make a parameter obligatory, but you may also utilize parameter validation attributes. It’s always important in programming to limit user input as much as possible. Limiting the data that users (or even you!) may send to your functions or scripts can minimize redundant code in your function that must cater for a variety of scenarios.
Observational Learning
For example, I showed sending the number 2013 to the Install-Office method since I knew it would work. The code was written by myself! I’m guessing (never do this in code!) that anybody who understands anything would select either 2013 or 2016 as the version. What you think is plain to you may not be so evident to others.
If you want to be technical about versions, specifying the 2013 version as 15.0 and the 2016 version as 16.0 would be more correct if Microsoft was still using the versioning schema they used in the past. But what if, expecting they’ll give a version of 2013 or 2016, you’ve included code in the method that searches for files containing those versions or something else?
Here’s an example of when the $Version string could be used in a file path. It will fail or do something worse if someone passes a value that does not complete a folder name of Office2013 or Office2016, such as removing unexpected folders or changing items you didn’t account for.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$Version ) Get-ChildItem -Path “\SRV1InstallersOffice$Version” } PS> Install-Office -Version ‘15.0’ Get-ChildItem : Cannot find path ‘SRV1InstallersOffice15.0’ because it does not exist. At line:7 char:5 Get-ChildItem -Path “\SRV1InstallersOffice$Version” ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CategoryInfo : ObjectNotFound: (SRV1InstallersOffice15.0:String) [Get-ChildItem], ItemNotFoundExcep tion FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand
You may use PowerShell parameter validation to restrict the user’s input to what you anticipate.
Validation Attribute Using the ValidateSet Parameter
You may utilize a variety of parameter validation techniques. Run Get-Help about Functions Advanced Parameters for a complete list. In this case, the ValidateSet property would be the most appropriate.
You may use the ValidateSet validation property to define a list of values that can be used as parameter values. I’d want to make sure that the user may only provide these values since we’re only accounting for the strings 2013 or 2016. Otherwise, the function will instantly fail and tell them of the reason.
Under the original parameter keyword, you may add parameter validation characteristics. You can see an array of objects within the parentheses of the parameter property in this example; 2013 and 2016. A parameter validation property notifies PowerShell that only 2013 or 2016 are accepted options for Version. If you attempt to pass anything other than what’s in the set, you’ll get an error message stating that you only have a limited amount of possibilities.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateSet(‘2013′,’2016’)] [string]$Version ) Get-ChildItem -Path “\SRV1InstallersOffice$Version” } PS> Install-Office -Version 15.0 Install-Office : Cannot validate argument on parameter ‘Version’. The argument “15.0” does not belong to the set “2013,2016” specified by the ValidateSet attribute. Supply an argument that is in the set and then try the command again. At line:1 char:25 Install-Office -Version 15.0 ~~~~ CategoryInfo : InvalidData: (:) [Install-Office], ParameterBindingValidationException FullyQualifiedErrorId : ParameterArgumentValidationError,Install-Office
The ValidateSet property is a frequently used validation attribute. Check out the Functions Advanced Parameters help topic by executing Get-Help about Functions Advanced Parameters for a comprehensive list of all the ways parameter values may be controlled.
Sets of Parameters
Assume you only want specific PowerShell parameters to be used in conjunction with other parameters. Perhaps you’ve given the Install-Office function a Path argument. This route will install the installer in whatever version it is. You don’t want the user to utilize the Version option in this scenario.
You need Sets of Parameters.
Sets of parameters may be created that can only be used with other parameters from the same set. You may now get the path to the installer by using both the Version and the Path parameters in the code below.
[CmdletBinding()] function Install-Office [Parameter(Mandatory)] param([Parameter(Mandatory)] param([Parameter(Mand [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(Mandatory)], [string]$Version, [string]$Version, [string]$Version, [string] [string] Path $Path) Get-ChildItem -Path “SRV1InstallersOffice$Version” if ($Version) elseif ($Path) Get-ChildItem -Path $Path -Path -Path -Path -Path -Path -Path -Path -Path –
However, this creates a difficulty since the user might utilize both parameters. Furthermore, since both parameters are required, they will be obliged to utilize both even if it is not what you desire. To address this, we may group all of the parameters together in a parameter set like the one below.
[CmdletBinding()] function Install-Office [Parameter(Mandatory,ParameterSetName = ‘ByVersion’)] param([Parameter(Mandatory,ParameterSetName = ‘ByVersion’)] param([Parameter(Mand [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(Mandatory, parameterSetName = ‘ByPath’)], [string]$Version, [string]$Version, [string]$Version, [string]$Version, [string]$Version, [string]$ [string] Path $Path) Get-ChildItem -Path “SRV1InstallersOffice$Version” if ($Version) elseif ($Path) Get-ChildItem -Path $Path -Path -Path -Path -Path -Path -Path -Path -Path –
You may control groups of parameters together by specifying a parameter set name on each parameter.
Set of Default Parameters
What happens if a user attempts to run Install-Office without specifying any parameters? You’ll get a nice error notice if this isn’t accounted for.
There are no parameters configured.
You’ll need to establish a default parameter set in the CmdletBinding() section to correct this. Changing [CmdletBinding()] to [CmdletBinding(DefaultParameterSetName = ‘ByVersion’)] informs the function to choose a parameter set to use if no arguments were explicitly supplied.
Because it will be utilizing that parameter set, anytime you run Install-Office, it will request for the Version parameter.
Input to the Pipeline
You’ve been generating functions with a PowerShell parameter that can only be given using the standard -ParameterName Value syntax in the examples so far. However, as you’ve seen, PowerShell includes an easy pipeline that lets you transfer objects from one command to the next without having to use the “usual” syntax.
You “chain” instructions together using the pipe symbol | when using the pipeline, enabling a user to submit the output of a command like Get-Service to Start-Service as a shortcut for giving the Name argument to Start-Service.
Using a Loop the “Old” Way
You’re installing Office and have a Version argument in the custom function you’re dealing with. Let’s imagine you have a CSV file with a list of machine names in one row and the Office version that has to be installed on them in the second row. This is what the CSV file looks like:
PC1,2016,PC2,2013,PC3,2016,PC4,2016,PC5,2016,PC6,2016,PC7,2016,PC8,2016,PC9
On that machine, you’d want to install the version of Office that’s next to it.
First, you’ll need to add a ComputerName argument to the method so that each iteration of the function gets a distinct computer name. I’ve added a Write-Host instance to observe how the variables grow within the fictitious function, and I’ve built some pseudocode to mimic some code that may be in the fictional function.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter()] [string]$ComputerName ) <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” }
You may do this by reading the CSV file and giving the data for the computer name and version to the Install-Office function after you’ve added the ComputerName argument to the function.
$computers = Import-Csv -Path ‘C:ComputerOfficeVersions.csv’ foreach ($pc in $computers) { Install-Office -Version $_.Version -ComputerName $_.ComputerName }
Building Input to the Pipeline for Parameters
The “old” technique of reading the CSV rows and passing each row’s properties to the function is to use a loop to transfer each row’s properties to the function. Instead of using the foreach loop, you should utilize the pipeline in this section.
The function does not support the pipeline in its current state. It would seem logical to suppose that you could use the pipeline to feed each computer’s name and version to the function. We’re reading the CSV and handing it to Install-Office straight below, however this doesn’t work.
PS> Import-Csv -Path ‘C:ComputerOfficeVersions.csv’ | Install-Office
You may make all the assumptions you want, but that won’t make it work. When you know that Import-Csv sends the Version argument as an object property, you’ll see that we’re being requested for it. Why isn’t it functioning properly? Because you haven’t yet implemented pipeline support.
There are two kinds of Input to the Pipeline in a PowerShell function; ByValue (entire object) and ByPropertyName (a single object property). Which do you think is the best way to stitch together the output of Import-Csv to the input of Install-Office?
You could use the ByPropertyName function without any restructuring since Import-Csv already returns the properties Version and ComputerName because they’re columns in the CSV.
It’s a lot easier than you would expect to add pipeline support to a custom function. ValueFromPipeline or ValueFromPipelineByPropertyName are two keywords that indicate a parameter attribute.
You’ll use ValueFromPipelineByPropertyName in this example to link the ComputerName and Version properties returned from Import-Csv to the Version and ComputerName arguments of Install-Office.
Because we want to bind both of these arguments, you’ll need to add this keyword to each of them, as seen below, and then restart the function using the pipeline.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory,ValueFromPipelineByPropertyName)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [string]$ComputerName ) <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” }
That’s strange. It just processed the CSV’s final row. What exactly is going on? Because you skipped over a notion that isn’t necessary when generating functions without pipeline support, it just performed the function for the final row.
Remember to Include the Process Block!
When you need to create a function that involves pipeline support, you must include (at a minimum) an “embedded” block inside of your function called. process. This process block tells PowerShell that when receiving Input to the Pipeline, to run the function for every iteration. By default, it will only execute the last one.
Other blocks, such as begin and end, may theoretically be added, although scripters don’t utilize them nearly as often.
I’ll add a process block with the code inside to instruct PowerShell to run this function for every object that comes in.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory,ValueFromPipelineByPropertyName)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [string]$ComputerName ) process { <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” } }
The Version and ComputerName attributes received from Import-Csv were now provided to Install-Office and tied to the Version and ComputerName arguments.
Resources
Check out my blog article on PowerShell functions to learn more about how function parameters operate.
I also recommend that you have a look at my Pluralsight course, Building Advanced PowerShell Functions and Modules, for an in-depth look at PowerShell functions, function parameters, and PowerShell modules.
.ComputerName
One or more parameters, also known as arguments, may be used with any PowerShell command. You’re not creating proper PowerShell code if you don’t use PowerShell parameters in your PowerShell functions!
You’ll cover almost every aspect of generating and using PowerShell parameters or arguments in this tutorial!
This is an excerpt from PowerShell for SysAdmins, which I wrote. Check it out if you want to learn PowerShell or pick up some tips and techniques.
What Is the Purpose of a Parameter?
When you first start writing functions, you’ll have the choice of including or excluding parameters, as well as the behavior of those arguments.
Consider the following scenario: you have a function that installs Microsoft Office. Perhaps it secretly invokes the Office installation inside the method. For our purposes, it doesn’t important what the function performs. With the name of the function and the script block, the basic function looks like this.
## Run the quiet installer here using the function Install-Office
In this case, you just ran Install-Office without any options and it took care of everything.
Whether the Install-Office function had parameters or not made no difference. It didn’t seem to have any necessary arguments; otherwise, PowerShell wouldn’t have allowed us execute it without one.
When Should You Use PowerShell Parameters?
There are several versions of Office. It’s possible that you’ll need to set up Office 2013 and 2016. There is currently no method to indicate this. Every time you wanted to modify the behavior, you could edit the function’s code.
You might, for instance, construct two distinct methods to install various versions.
‘I installed Office 2013.’ function Install-Office2013 Write-Host ‘Wheeeeeeeeeeeeeeeeee ‘I installed Office 2016.’ function Install-Office2016 Write-Host ‘Wheeeeeeeeeeeeeeeeee
It’s possible to do this, but it’s not scalable. It compels you to build a new function for each new version of Office that is released. When you don’t have to, you’ll have to duplicate a lot of code.
Instead, you’ll need a means to modify the function’s functionality by passing in various values at runtime. How do you go about doing this?
Yep! Parameters, or as some refer to them, arguments.
You must add at least one argument to this method if we want to install multiple versions of Office without having to change the code every time.
Before you rapidly come up with a PowerShell parameter, you must first ask yourself, “What is the smallest modification or changes you foresee to be required in this function?”
Keep in mind that you must restart this function without modifying any of the code inside it. The parameter in this case is presumably obvious; you need to provide a Version argument. When you’ve got a function with tens of lines of code, though, the solution isn’t always obvious. It will always assist if you answer the question as accurately as possible.
So you’re aware that a Version argument is required. So, what’s next? You may now add one, but there are various ways to skin that cat, as with any excellent programming language.
Based on my almost decade of PowerShell knowledge, I’ll teach you the “optimal” approach to generate parameters in this lesson. Keep in mind, though, that this isn’t the only method to create a parameter.
Positional parameters are anything that exists. You may use these parameters to provide values to parameters without having to mention the parameter name. Positional criteria are useful, but they aren’t “best practice.” Why? Because they are more difficult to understand, particularly when a function has a lot of arguments.
Using PowerShell to Create a Simple Parameter
To add a parameter to a function, you’ll need two things: a param block and the parameter itself. The param keyword is followed by a set of parentheses to create a param block.
[CmdletBinding()] function Install-Office ‘I installed Office 2016.’ param() Write-Host ‘Wheeeeeeeeeeeeeeeeee
The function’s real functionality hasn’t altered at this time. We’ve just finished installing some piping in preparation for the first parameter.
You’ll now construct the parameter once the param block is in place. The method I recommend for creating a parameter starts with the parameter block, then the type of parameter, and finally the parameter variable name.
[CmdletBinding()] function Install-Office [Parameter()] param([Parameter()] param([Parameter()] par Write-Host ‘I installed Office 2016.’ [string]$Version) ‘Wheeeeeeeeeeeeeeeeee
We’ve now generated a PowerShell function parameter, but what precisely happened?
Every parameter should include a parameter block, which is an optional but suggested component. It is “function plumbing,” similar to the param block, which prepares the parameter for adding further functionality. The kind of argument it is is defined on the second line.
We’ve opted to convert the Version option to a string in this situation. If an explicit type is defined, any value supplied to this argument will be “converted” to a string if it isn’t already one.
The kind is not required, although it is strongly recommended. Defining the sort of parameter it is will help you avoid a lot of unpleasant problems down the line. Have faith in me.
Now that you’ve established the option, you may use the Version argument in the Install-Office command to provide a version string, such as 2013. The value supplied to the Version parameter is also known as the arguments or values of parameters.
PS> Install-Office -Version 2013 I installed Office 2016. Yippee!
What exactly is going on here? You told it you wanted version 2013, yet it still says it’s installed version 2016. You must remember to rename the function’s code to a variable after adding an argument. When a parameter is supplied to a function, the variable is extended to the value that was passed.
Replace the static text 2016 with the Version parameter variable, changing the single quotes to double quotes to allow the variable to grow.
[CmdletBinding()] function Install-Office [Parameter()] param([Parameter()] param([Parameter()] par $Version) ([string]$Version) ([string]$Version) ( Host-Write “I downloaded and installed Office $Version. What a relief! “,,,,,,,,,,
As you can see, any value you provide the Version argument is sent into the method as the $Version variable.
Attribute for Mandatory Parameters
Remember how I said the [Parameter()] line was merely “function plumbing” that was required to get the function ready for further work? The extra labor I mentioned previously is adding parameter properties to a parameter.
A parameter does not have to be a variable placeholder. Parameter characteristics and parameter validity are concepts in PowerShell. The functionality of a parameter may be changed in a variety of ways using parameter attributes.
The Mandatory keyword, for example, is one of the most typical parameter properties you’ll use. You may call the Install-Office function without supplying the Version argument by default, and it will work perfectly. The parameter Version would be optional. Although the $Version variable within the code would not grow since there was no value, it would still execute.
When establishing a parameter, you’ll often want the user to utilize it all of the time. You’ll rely on the argument’s value somewhere in the function’s code, and if the parameter isn’t supplied, the function will fail. You wish to require the user to supply this parameter value to your function in certain instances. You’d want that parameter to be required.
Once you have the fundamental architecture in place, forcing users to utilize a parameter is straightforward. Within the parenthesis of the parameter, you must insert the term Mandatory. Once you’ve done that, calling the function without the argument will cause it to pause until a value is entered.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$Version ) Write-Host “I installed Office $Version. Yippee!” } PS> Install-Office cmdlet Install-Office at command pipeline position 1 Supply values for the following parameters: Version:
Until you supply a value for the Version argument, the function will wait. When you press Enter, PowerShell will run the function and then go on. If you specify a value for the argument, PowerShell will not ask you for it every time you run the command.
Validation Attributes for PowerShell Parameters
One of the most popular parameter features is to make a parameter obligatory, but you may also utilize parameter validation attributes. It’s always important in programming to limit user input as much as possible. Limiting the data that users (or even you!) may send to your functions or scripts can minimize redundant code in your function that must cater for a variety of scenarios.
Observational Learning
For example, I showed sending the number 2013 to the Install-Office method since I knew it would work. The code was written by myself! I’m guessing (never do this in code!) that anybody who understands anything would select either 2013 or 2016 as the version. What you think is plain to you may not be so evident to others.
If you want to be technical about versions, specifying the 2013 version as 15.0 and the 2016 version as 16.0 would be more correct if Microsoft was still using the versioning schema they used in the past. But what if, expecting they’ll give a version of 2013 or 2016, you’ve included code in the method that searches for files containing those versions or something else?
Here’s an example of when the $Version string could be used in a file path. It will fail or do something worse if someone passes a value that does not complete a folder name of Office2013 or Office2016, such as removing unexpected folders or changing items you didn’t account for.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$Version ) Get-ChildItem -Path “\SRV1InstallersOffice$Version” } PS> Install-Office -Version ‘15.0’ Get-ChildItem : Cannot find path ‘SRV1InstallersOffice15.0’ because it does not exist. At line:7 char:5 Get-ChildItem -Path “\SRV1InstallersOffice$Version” ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CategoryInfo : ObjectNotFound: (SRV1InstallersOffice15.0:String) [Get-ChildItem], ItemNotFoundExcep tion FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand
You may use PowerShell parameter validation to restrict the user’s input to what you anticipate.
Validation Attribute Using the ValidateSet Parameter
You may utilize a variety of parameter validation techniques. Run Get-Help about Functions Advanced Parameters for a complete list. In this case, the ValidateSet property would be the most appropriate.
You may use the ValidateSet validation property to define a list of values that can be used as parameter values. I’d want to make sure that the user may only provide these values since we’re only accounting for the strings 2013 or 2016. Otherwise, the function will instantly fail and tell them of the reason.
Under the original parameter keyword, you may add parameter validation characteristics. You can see an array of objects within the parentheses of the parameter property in this example; 2013 and 2016. A parameter validation property notifies PowerShell that only 2013 or 2016 are accepted options for Version. If you attempt to pass anything other than what’s in the set, you’ll get an error message stating that you only have a limited amount of possibilities.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateSet(‘2013′,’2016’)] [string]$Version ) Get-ChildItem -Path “\SRV1InstallersOffice$Version” } PS> Install-Office -Version 15.0 Install-Office : Cannot validate argument on parameter ‘Version’. The argument “15.0” does not belong to the set “2013,2016” specified by the ValidateSet attribute. Supply an argument that is in the set and then try the command again. At line:1 char:25 Install-Office -Version 15.0 ~~~~ CategoryInfo : InvalidData: (:) [Install-Office], ParameterBindingValidationException FullyQualifiedErrorId : ParameterArgumentValidationError,Install-Office
The ValidateSet property is a frequently used validation attribute. Check out the Functions Advanced Parameters help topic by executing Get-Help about Functions Advanced Parameters for a comprehensive list of all the ways parameter values may be controlled.
Sets of Parameters
Assume you only want specific PowerShell parameters to be used in conjunction with other parameters. Perhaps you’ve given the Install-Office function a Path argument. This route will install the installer in whatever version it is. You don’t want the user to utilize the Version option in this scenario.
You need Sets of Parameters.
Sets of parameters may be created that can only be used with other parameters from the same set. You may now get the path to the installer by using both the Version and the Path parameters in the code below.
[CmdletBinding()] function Install-Office [Parameter(Mandatory)] param([Parameter(Mandatory)] param([Parameter(Mand [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(Mandatory)], [string]$Version, [string]$Version, [string]$Version, [string] [string] Path $Path) Get-ChildItem -Path “SRV1InstallersOffice$Version” if ($Version) elseif ($Path) Get-ChildItem -Path $Path -Path -Path -Path -Path -Path -Path -Path -Path –
However, this creates a difficulty since the user might utilize both parameters. Furthermore, since both parameters are required, they will be obliged to utilize both even if it is not what you desire. To address this, we may group all of the parameters together in a parameter set like the one below.
[CmdletBinding()] function Install-Office [Parameter(Mandatory,ParameterSetName = ‘ByVersion’)] param([Parameter(Mandatory,ParameterSetName = ‘ByVersion’)] param([Parameter(Mand [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(Mandatory, parameterSetName = ‘ByPath’)], [string]$Version, [string]$Version, [string]$Version, [string]$Version, [string]$Version, [string]$ [string] Path $Path) Get-ChildItem -Path “SRV1InstallersOffice$Version” if ($Version) elseif ($Path) Get-ChildItem -Path $Path -Path -Path -Path -Path -Path -Path -Path -Path –
You may control groups of parameters together by specifying a parameter set name on each parameter.
Set of Default Parameters
What happens if a user attempts to run Install-Office without specifying any parameters? You’ll get a nice error notice if this isn’t accounted for.
There are no parameters configured.
You’ll need to establish a default parameter set in the CmdletBinding() section to correct this. Changing [CmdletBinding()] to [CmdletBinding(DefaultParameterSetName = ‘ByVersion’)] informs the function to choose a parameter set to use if no arguments were explicitly supplied.
Because it will be utilizing that parameter set, anytime you run Install-Office, it will request for the Version parameter.
Input to the Pipeline
You’ve been generating functions with a PowerShell parameter that can only be given using the standard -ParameterName Value syntax in the examples so far. However, as you’ve seen, PowerShell includes an easy pipeline that lets you transfer objects from one command to the next without having to use the “usual” syntax.
You “chain” instructions together using the pipe symbol | when using the pipeline, enabling a user to submit the output of a command like Get-Service to Start-Service as a shortcut for giving the Name argument to Start-Service.
Using a Loop the “Old” Way
You’re installing Office and have a Version argument in the custom function you’re dealing with. Let’s imagine you have a CSV file with a list of machine names in one row and the Office version that has to be installed on them in the second row. This is what the CSV file looks like:
PC1,2016,PC2,2013,PC3,2016,PC4,2016,PC5,2016,PC6,2016,PC7,2016,PC8,2016,PC9
On that machine, you’d want to install the version of Office that’s next to it.
First, you’ll need to add a ComputerName argument to the method so that each iteration of the function gets a distinct computer name. I’ve added a Write-Host instance to observe how the variables grow within the fictitious function, and I’ve built some pseudocode to mimic some code that may be in the fictional function.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter()] [string]$ComputerName ) <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” }
You may do this by reading the CSV file and giving the data for the computer name and version to the Install-Office function after you’ve added the ComputerName argument to the function.
$computers = Import-Csv -Path ‘C:ComputerOfficeVersions.csv’ foreach ($pc in $computers) { Install-Office -Version $_.Version -ComputerName $_.ComputerName }
Building Input to the Pipeline for Parameters
The “old” technique of reading the CSV rows and passing each row’s properties to the function is to use a loop to transfer each row’s properties to the function. Instead of using the foreach loop, you should utilize the pipeline in this section.
The function does not support the pipeline in its current state. It would seem logical to suppose that you could use the pipeline to feed each computer’s name and version to the function. We’re reading the CSV and handing it to Install-Office straight below, however this doesn’t work.
PS> Import-Csv -Path ‘C:ComputerOfficeVersions.csv’ | Install-Office
You may make all the assumptions you want, but that won’t make it work. When you know that Import-Csv sends the Version argument as an object property, you’ll see that we’re being requested for it. Why isn’t it functioning properly? Because you haven’t yet implemented pipeline support.
There are two kinds of Input to the Pipeline in a PowerShell function; ByValue (entire object) and ByPropertyName (a single object property). Which do you think is the best way to stitch together the output of Import-Csv to the input of Install-Office?
You could use the ByPropertyName function without any restructuring since Import-Csv already returns the properties Version and ComputerName because they’re columns in the CSV.
It’s a lot easier than you would expect to add pipeline support to a custom function. ValueFromPipeline or ValueFromPipelineByPropertyName are two keywords that indicate a parameter attribute.
You’ll use ValueFromPipelineByPropertyName in this example to link the ComputerName and Version properties returned from Import-Csv to the Version and ComputerName arguments of Install-Office.
Because we want to bind both of these arguments, you’ll need to add this keyword to each of them, as seen below, and then restart the function using the pipeline.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory,ValueFromPipelineByPropertyName)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [string]$ComputerName ) <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” }
That’s strange. It just processed the CSV’s final row. What exactly is going on? Because you skipped over a notion that isn’t necessary when generating functions without pipeline support, it just performed the function for the final row.
Remember to Include the Process Block!
When you need to create a function that involves pipeline support, you must include (at a minimum) an “embedded” block inside of your function called. process. This process block tells PowerShell that when receiving Input to the Pipeline, to run the function for every iteration. By default, it will only execute the last one.
Other blocks, such as begin and end, may theoretically be added, although scripters don’t utilize them nearly as often.
I’ll add a process block with the code inside to instruct PowerShell to run this function for every object that comes in.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory,ValueFromPipelineByPropertyName)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [string]$ComputerName ) process { <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” } }
The Version and ComputerName attributes received from Import-Csv were now provided to Install-Office and tied to the Version and ComputerName arguments.
Resources
Check out my blog article on PowerShell functions to learn more about how function parameters operate.
I also recommend that you have a look at my Pluralsight course, Building Advanced PowerShell Functions and Modules, for an in-depth look at PowerShell functions, function parameters, and PowerShell modules.
.Com
Building Input to the Pipeline for Parameters
The “old” technique of reading the CSV rows and passing each row’s properties to the function is to use a loop to transfer each row’s properties to the function. Instead of using the foreach loop, you should utilize the pipeline in this section.
The function does not support the pipeline in its current state. It would seem logical to suppose that you could use the pipeline to feed each computer’s name and version to the function. We’re reading the CSV and handing it to Install-Office straight below, however this doesn’t work.
PS> Import-Csv -Path ‘C:ComputerOfficeVersions.csv’ | Install-Office
You may make all the assumptions you want, but that won’t make it work. When you know that Import-Csv sends the Version argument as an object property, you’ll see that we’re being requested for it. Why isn’t it functioning properly? Because you haven’t yet implemented pipeline support.
There are two kinds of Input to the Pipeline in a PowerShell function; ByValue (entire object) and ByPropertyName (a single object property). Which do you think is the best way to stitch together the output of Import-Csv to the input of Install-Office?
You could use the ByPropertyName function without any restructuring since Import-Csv already returns the properties Version and ComputerName because they’re columns in the CSV.
It’s a lot easier than you would expect to add pipeline support to a custom function. ValueFromPipeline or ValueFromPipelineByPropertyName are two keywords that indicate a parameter attribute.
You’ll use ValueFromPipelineByPropertyName in this example to link the ComputerName and Version properties returned from Import-Csv to the Version and ComputerName arguments of Install-Office.
Because we want to bind both of these arguments, you’ll need to add this keyword to each of them, as seen below, and then restart the function using the pipeline.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory,ValueFromPipelineByPropertyName)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [string]$ComputerName ) <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” }
That’s strange. It just processed the CSV’s final row. What exactly is going on? Because you skipped over a notion that isn’t necessary when generating functions without pipeline support, it just performed the function for the final row.
Remember to Include the Process Block!
When you need to create a function that involves pipeline support, you must include (at a minimum) an “embedded” block inside of your function called. process. This process block tells PowerShell that when receiving Input to the Pipeline, to run the function for every iteration. By default, it will only execute the last one.
Other blocks, such as begin and end, may theoretically be added, although scripters don’t utilize them nearly as often.
I’ll add a process block with the code inside to instruct PowerShell to run this function for every object that comes in.
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory,ValueFromPipelineByPropertyName)] [ValidateSet(‘2013′,’2016’)] [string]$Version, [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [string]$ComputerName ) process { <# ## Connect to the remote with some code here Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Do stuff to install the version of office on this computer Start-Process -FilePath ‘msiexec.exe’ -ArgumentList ‘C:SetupOffice{0}.msi’ -f $using:Version } #> Write-Host “I am installing Office version [$Version] on computer [$ComputerName]” } }
The Version and ComputerName attributes received from Import-Csv were now provided to Install-Office and tied to the Version and ComputerName arguments.
Resources
Check out my blog article on PowerShell functions to learn more about how function parameters operate.
I also recommend that you have a look at my Pluralsight course, Building Advanced PowerShell Functions and Modules, for an in-depth look at PowerShell functions, function parameters, and PowerShell modules.
PowerShell parameters are a way to pass in arguments to PowerShell scripts. Parameters can be validated by using the “powershell parameter validation” command.
Frequently Asked Questions
What are PowerShell parameters?
A: PowerShell parameters are additional arguments that you can use in your cmdlet. They allow for greater control over the parameters of commands, especially when they require multiple inputs or extra input from outside sources.
What are the common parameters in PowerShell?
A: The common parameters in PowerShell are the default options that you set when creating a new PowerShell session. Parameters can be added to your scripts, but they cannot replace the defaults.
How do you use parameters in PowerShell?
A: A parameter is a variable that you pass in order to programmatically control the flow of execution. There are many parameters available, each with their own specific use depending on what programming language it originated from.
Related Tags
- powershell parameters list
- powershell parameter(position)
- powershell parameter examples
- powershell mandatory parameter
- powershell param block