We are going well with the PowerShell tutorial, we saw how to use credentials and how to work with variables, array, and hash tables.
In the last post, we discussed with the overview of working with script blocks.
In this post, we are going to dig deeper into those with understanding how to design and convert it into functions.
In this post we are going to cover the below topics:
- Script vs functions
- Pass parameters to a script
- Convert to functions
- Best practices for designing functions
You can accomplish many tasks in PowerShell by typing a command and pressing Enter in the shell console.
We expect when you are starting up writing commands on the PowerShell console and getting the required output.
But you hand off a task to someone else and need to make sure that it’s done exactly as planned.
That’s where scripts come in—and it’s also where functions come in.
SCRIPT OR FUNCTION?
Suppose you have some task, perhaps one requiring a handful of commands in order to complete.
A script is a convenient way of packaging those commands together.
Rather than typing the commands manually, you paste them into a script and PowerShell runs them in order whenever you run that script.
Following best practices, you’d give that script a cmdlet-like name, such as Get-ServerDetails.ps1
Script blocks
# A script block without parameters
{
"Something is happening here
}
# Executing a script block
({
"Something is happening here
}).Invoke()
# Or use the ampersand
& {
"Something is happening here
}
# With parameters
$scriptBlockArgs = {
# Built-in - not recommended
"$($args[0]) is happening here"
}
# Better
$scriptBlockParam = {
param
(
[string]
$TheThing
)
# Built-in - not recommended
"$TheThing is happening here"
}
$scriptBlockArgs.Invoke('Something')
# Named parameters are possible
$scriptBlockParam.Invoke('Something')
& $scriptBlockParam -TheThing SomeThing
Consider the below example or command which is executed in PowerShell console.
Get-WmiObject –Class Win32_LogicalDisk –Filter "DriveType=3"
-ComputerName venkat |
Select-Object –Property DeviceID,@{Name='ComputerName';
Expression={$_.PSComputerName}},volume,FreeSpace
This command uses WMI to retrieve all instances of the Win32_LogicalDisk class from a given computer. It limits the results to drives having a DriveType of 3, which specifies local, fixed disks.
Using Param to convert it to script.
#define param
Param{
[String]$ComputerName,
[int]$driveType=3
}
Get-WmiObject –Class Win32_LogicalDisk –Filter "DriveType=$driveType"
-ComputerName $ComputerName |
Select-Object –Property DeviceID,@{Name='ComputerName';
Expression={$_.PSComputerName}},volume,FreeSpace
Note
- you define a Param() section. This section must use parentheses—given PowerShell’s other syntax, it can be tempting to use braces, but here they’ll cause an error message.
Converting to script
Save the above code as Get-Details.ps1
Running the script
PS C:\> .\Get-Details.ps1 -computerName localhost
DeviceID ComputerName Size FreeSpace
-------- ------------ ---- ---------
C: Venkat 42842786562 3246185652
PS C:\> .\Get-DiskInfo.ps1 -computerName localhost |
>> Where-object { $_.FreeSpace -gt 500 } |
>> Format-List -Property *
DeviceID : C:
ComputerName : venkat
Size : 42842786562
FreeSpace : 3246185652
The example piped the output of the script to Where-Object and then to Format-List, changing the output.
This result may seem obvious to you, but it impresses the heck out of us! Basically, this little script is behaving exactly like a real PowerShell command.
Working with function
Function declaration
Function Where-LessSpace {
Param(
[int]$ LessSpace
)
BEGIN {}
PROCESS {
If ((100 * ($_.FreeSpace / $_.Size) –lt $ LessSpace)) {
Write-Output $_
}
}
END {}
}
Trying to convert the above script
#define param
Function Get-DiskDetails{
Param{
[String]$ComputerName,
[int]$driveType=3
}
Get-WmiObject –Class Win32_LogicalDisk –Filter "DriveType=$driveType"
-ComputerName $ComputerName |
Select-Object –Property DeviceID,@{Name='ComputerName';
Expression={$_.PSComputerName}},volume,FreeSpace
}
How it works?
- PowerShell sees the function declaration and loads the function into memory for later use. It creates an entry in the Function: drive that lets it remember the function’s name, parameters, and contents.
- When you try to use the function, PowerShell will tab-complete its name and parameter names for you.
Best Practice to Follow while Function design (My Way!)
In order to always support all common parameters, it is recommended to include a CmdletBinding attribute in your cmdlet, even if you do not want to use any parameters. This gives the executing user full control over error handling and output.
Additionally, always use the proper verbs to express what your function does. Get-Verb shows plenty of approved verbs that will make your cmdlet easier to find:
#bad
Function foo{
“Try something”
}
#Good
Function foo
{
“Try something”
}
Using the CmdletBinding attribute is very easy and provides many benefits.
function foo
{
[CmdletBinding()]
param ( )
'Try something'
}
- If your function uses parameters, always add the type to them
- Add parameter comments close to the actual parameter
- consider adding help messages.
param
(
# A comma-separated list of VM names to provision
[Parameter(
Mandatory,
HelpMessage = 'Please enter a comma-separated list of VM names'
)]
Conclusion:
Scripts and functions are the basis for creating complex, repeatable automation in your environment. In this post, we’ve touched on the basics. We’ll continue doing that over the next few post so that you can build scripts and functions that are truly consistent with PowerShell’s overall philosophy and operational techniques.As a reference, we’ll repeat our Scripting Output Rules:
- If you run a command and don’t capture its output into a variable, then the output of that the command goes into the pipeline and becomes the output of the script.
- Whatever you put into the pipeline by using Write-Output will also become the output of your script.
- Output one kind of object, and one kind only.
Remember these three rules when you’re creating your scripts and you’ll minimize problems with your output.
EmoticonEmoticon