This post is part of the Second Wednesday Demo Session, Click here for more info about additional demo posts.
If you have a set of commands or code that you want to reuse multiple times in a script or in your session you can place the code in a function.
This allows you to call the function instead of typing the same piece of code over and over again.
Just as an example, we’ll use Get-Process and display the Process Name and Company property.
PS > $Name = "powershell"
PS > $process = Get-Process $Name
PS > Write-Host ProcessName: $Process.Name
ProcessName: powershell
PS > Write-Host Company: $Process.Company
Company: Microsoft Corporation
Instead of typing the lines above everytime we want to check the Name and Company of a process we can place the code in a function.
function Get-Company([string]$Name) {
$process = Get-Process $Name
Write-Host ProcessName: $Process.Name
Write-Host Company: $Process.Company
}
Now we can use the function instead.
PS > Get-Company -Name powershell
ProcessName: powershell
Company: Microsoft Corporation
Calling a function in PowerShell is similar to calling one of the built-in cmdlets. You type the functions name followed by one or more parameters.
You can also use Get-Help to find out additional information about a function, just as you do with CmdLets.
PS > Get-Help Get-Company
Get-Company [[-Name] ]
CmdLets in powershell usually have a set of common parameters such as Verbose, Debug, OutVariable and so on. You can add these additional parameters by using the [CmdletBinding()] attribute.
function Get-Company {
[cmdletbinding()]
param([string]$Name)
$process = Get-Process $Name
Write-Host ProcessName: $Process.Name
Write-Host Company: $Process.Company
}
If we use the Get-Help CmdLet additional parameters are available in our function.
PS > Get-Help Get-Company
Get-Company [[-Name] ] [-Verbose] [-Debug] [-ErrorAction ]
[-WarningAction ] [-ErrorVariable ] [-WarningVariable ]
[-OutVariable ] [-OutBuffer ]
It’s also possible to set a parameter as mandatory. Here’s an example.
function Get-Company {
param(
[Parameter(Mandatory = $true)]
[string]$Name
)
$process = Get-Process $Name
Write-Host ProcessName: $Process.Name
Write-Host Company: $Process.Company
}
If we call the function without a value to the Name parameter we are automatically prompted for it.
PS > Get-Company
cmdlet Get-Company at command pipeline position 1
Supply values for the following parameters:
Name: powershell
ProcessName: powershell
Company: Microsoft Corporation
If we want to validate the input to a parameter we can use the [ValidateSet()] attribute. Here’s an example.
function Get-Company {
param(
[Parameter(Mandatory = $true)]
[ValidateSet("powershell","notepad")]
[string]$Name
)
$process = Get-Process $Name
Write-Host ProcessName: $Process.Name
Write-Host Company: $Process.Company
}
The example above only accepts “powershell” or “notepad” as input.
PS > Get-Company -Name powershell
ProcessName: powershell
Company: Microsoft Corporation
If we type anything else an error occurs.
PS > Get-Company -Name calc
Get-Company : Cannot validate argument on parameter 'Name'. The argument "calc" does
not belong to the set "powershell,notepad" specified by the ValidateSet attribute.
Supply an argument that is in the set and then try the command again.
At line:1 char:18
+ Get-Company -Name <<<< calc
+ CategoryInfo : InvalidData: (:) [Get-Company], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Get-Company
We can also position the parameter. This applies when we provide parameter values without specifying the parameter.
function Get-Company {
param(
[Parameter(Position = 0)]
[string]$Name
)
$process = Get-Process $Name
Write-Host ProcessName: $Process.Name
Write-Host Company: $Process.Company
}
PS > Get-Company powershell
ProcessName: powershell
Company: Microsoft Corporation
We can also let the function accept input from a pipeline by using ValueFromPipeLine.
function Get-Company {
param(
[Parameter(
Position = 0,
ValueFromPipeLine= $true)]
[string]$Name
)
$process = Get-Process $Name
Write-Host ProcessName: $Process.Name
Write-Host Company: $Process.Company
}
Now we can use a pipeline when calling the function.
PS > "powershell" | Get-Company
ProcessName: powershell
Company: Microsoft Corporation
What if we send two inputs through the pipeline?
PS > "powershell","winword" | Get-Company
ProcessName: WINWORD
Company: Microsoft Corporation
Note that only the last input value was returned by the function. Adding a Process block solves this.
function Get-Company {
param(
[Parameter(
Position = 0,
ValueFromPipeLine= $true)]
[string]$Name
)
Process {
$process = Get-Process $Name
Write-Host ProcessName: $Process.Name
Write-Host Company: $Process.Company
}
}
Now we can send additional values through the pipeline.
PS > "powershell","winword" | Get-Company
ProcessName: powershell
Company: Microsoft Corporation
ProcessName: WINWORD
Company: Microsoft Corporation
We can also use propertynames from an object passed through a pipeline. Here's an example.
function Get-Company {
param(
[Parameter(
ValueFromPipeLineByPropertyName= $true)]
[string]$Name,
[Parameter(
ValueFromPipeLineByPropertyName= $true)]
[string]$Company
)
Process {
Write-Host ProcessName: $Name
Write-Host Company: $Company
}
}
Now we can use Get-Process and pipe the objects to our function as shown below.
PS > Get-Process powershell | Get-Company
ProcessName: powershell
Company: Microsoft Corporation
PS > Get-Process powershell,winword | Get-Company
ProcessName: powershell
Company: Microsoft Corporation
ProcessName: WINWORD
Company: Microsoft Corporation
In an earlier example we took a look at how to add common parameters to a function, but how do we use them?
Well, it turns out that advanced functions in powershell give us access to the $pscmdlet automatic variable.
The variable exposes support for Verbose, Whatif and more. The function below calls the variable and displays it's properties.
function Get-PSCmdLet {
[cmdletbinding()]
param()
$psCmdLet
}
If we call the function, the variable properties are displayed.
PS > Get-PSCmdLet
ParameterSetName : __AllParameterSets
MyInvocation : System.Management.Automation.Invoc
InvokeCommand : System.Management.Automation.Comma
Host : System.Management.Automation.Inter
SessionState : System.Management.Automation.Sessi
Events : System.Management.Automation.PSLoc
JobRepository : System.Management.Automation.JobRe
InvokeProvider : System.Management.Automation.Provi
Stopping : False
CommandRuntime : Get-PSCmdLet
CurrentPSTransaction :
CommandOrigin : Runspace
If we pipe the object to Get-Member we'll see all Methods and Properties available on the object.
PS > Get-PSCmdLet | Get-Member
TypeName: System.Management.Automation.PSScriptCmdlet
Name MemberType Definition
---- ---------- ----------
CurrentProviderLocation Method System.Managem
Dispose Method System.Void Di
Equals Method bool Equals(Sy
GetDynamicParameters Method System.Object
GetHashCode Method int GetHashCod
GetResolvedProviderPathFromPSPath Method System.Collect
GetResourceString Method string GetReso
GetType Method type GetType()
GetUnresolvedProviderPathFromPSPath Method string GetUnre
GetVariableValue Method System.Object
Invoke Method System.Collect
ShouldContinue Method bool ShouldCon
ShouldProcess Method bool ShouldPro
ThrowTerminatingError Method System.Void Th
ToString Method string ToStrin
TransactionAvailable Method bool Transacti
WriteCommandDetail Method System.Void Wr
WriteDebug Method System.Void Wr
WriteError Method System.Void Wr
WriteObject Method System.Void Wr
WriteProgress Method System.Void Wr
WriteVerbose Method System.Void Wr
WriteWarning Method System.Void Wr
CommandOrigin Property System.Managem
CommandRuntime Property System.Managem
CurrentPSTransaction Property System.Managem
Events Property System.Managem
Host Property System.Managem
InvokeCommand Property System.Managem
InvokeProvider Property System.Managem
JobRepository Property System.Managem
MyInvocation Property System.Managem
ParameterSetName Property System.String
SessionState Property System.Managem
Stopping Property System.Boolean
Now let's see how we can use this in our function. Say we want to write to the Verbose message stream. We can do this by simply using the WriteVerbose() method as shown below.
function Get-Company {
[cmdletbinding()]
param([string]$Name)
$process = Get-Process $Name
$psCmdLet.WriteVerbose("ProcessName: $($Process.Name)")
$psCmdLet.WriteVerbose("Company: $($Process.Company)")
}
When we call the function nothing seems to happen.
PS > Get-Company -Name powershell
But if we add the -Verbose parameter the verbose messages are returned.
PS > Get-Company -Name powershell -Verbose
VERBOSE: ProcessName: powershell
VERBOSE: Company: Microsoft Corporation