Book Image

PowerShell: Automating Administrative Tasks

By : Michael Shepard, Chendrayan Venkatesan, Sherif Talaat, Brenton J.W. Blawat
Book Image

PowerShell: Automating Administrative Tasks

By: Michael Shepard, Chendrayan Venkatesan, Sherif Talaat, Brenton J.W. Blawat

Overview of this book

Are you tired of managing Windows administrative tasks manually and are looking to automate the entire process? If yes, then this is the right course for you. This learning path starts your PowerShell journey and will help you automate the administration of the Windows operating system and applications that run on Windows. It will get you up and running with PowerShell, taking you from the basics of installation to writing scripts and performing web server automation. You will explore the PowerShell environment and discover how to use cmdlets, functions, and scripts to automate Windows systems. The next installment of the course focuses on gaining concrete knowledge of Windows PowerShell scripting to perform professional-level scripting. The techniques here are packed with PowerShell scripts and sample C# code to automate tasks. You will use .NET classes in PowerShell and C# to manage Exchange Online. In the final section, you will delve into real-world examples to learn how to simplify the management of your Windows environment. You will get to grips with PowerShell’s advanced functions and how to most effectively administer your system. This Learning Path combines some of the best that Packt has to offer in one complete, curated package. It includes content from the following Packt products: [*] Getting Started with PowerShell by Michael Shepard [*] Windows PowerShell for .Net Developers Second Edition by Chendrayan Venkatesan and Sherif Talaat [*] Mastering Windows PowerShell Scripting by Brenton J.W. Blawat
Table of Contents (6 chapters)

Chapter 7. Functions

In the previous chapter, we learned about packaging a set of commands in a script. In this chapter, we will look at using functions to provide the same kind of containment. We will discuss the differences between functions and scripts, and drill down more into parameters. The specific topics covered in this chapter include the following:

  • Defining functions
  • Comparing scripts and functions
  • Executing functions
  • Comment-based help
  • Function output
  • Parameters
  • Default values

Another kind of container

Scripts are a simple way to reuse your commands. Scripts are file-based, so there is an inherent limitation—you can only have one script per file. This may not seem like a problem, but when you have hundreds or even thousands of pieces of code, you may want to be able to group them together and not have so many files on the disk.

Comparing scripts and functions

The primary difference between a function and a script is that a script is a file, whereas a function is contained within a file. Consider the MdAndGo.ps1 script from Chapter 6, Scripts:

Comparing scripts and functions

As a function, this could be written like this:

Comparing scripts and functions

You can see from this example that the functions are defined using the function keyword. Comparing these two examples, you can also see that the body of the function (the scriptblock after the function name) is identical to the contents of the script. Practically speaking, anything you can do in a script, you can do in a function also. If this is true (it is true), why would we want to use functions?

The first reason is simple convenience. If I write a dozen bits of code pertaining to a particular subject area, I would want these bits of code to be in one place. If I use scripts, I can put all of these in the same folder, and this gives a bit of coherence. On the other hand, if I use functions, I can put all of these functions in the same file. Since they're all in the same file, I can edit the file all at once and make the changes to the functions more easily. Seeing the functions in the same file can help to understand the relationships between the code, and into repeated code or possibilities for refactoring.

Second, functions are exposed in PowerShell via a PSDrive called Function:. I can see all of the functions that have been loaded into my current session using cmdlets such as Get-ChildItem, or its commonly used alias dir:

Comparing scripts and functions

Tip

PSProviders and PSDrives

PowerShell uses PSProviders and PSDrives to provide access to hierarchical data sources using the same cmdlets that you would use to access filesystems. The Function: drive, mentioned here, holds all the functions loaded into the current session. Other PSDrives include the registry drives (HKLM: and HCKU:), the Variable: drive, the ENV: drive of environment variables, and the Cert: drive that provides access to the certificate store.

Executing and calling functions

As seen in figure in the Comparing scripts and functions section, defining a function involves using the function keyword and surrounding the code you want with a scriptblock. Executing this function statement simply adds the function definition to the current PowerShell session. To subsequently call the function, you use the function name like you would a cmdlet name, and supply parameters and arguments just like you would for a cmdlet or script.

Tip

Storing the MdAndGo function in a file called MdAndGo.ps1 can be confusing because we're using the same name for the two things. You dot-source the ps1 file to load the function into the session, then you can execute this function. Dot-sourcing the file doesn't run the function. If we had written this as a script, on the other hand, we could have executed the logic of the script without dot-sourcing.

Executing and calling functions

Tip

Warning!

A common mistake of new PowerShell users is to call a function using parentheses to surround the parameters and commas to separate them. When you do this, PowerShell thinks you are creating an array and passes the array as an argument to the first parameter.

Naming conventions

Looking at the list of functions in the Function: drive, you will see some odd ones, such as CD.., CD\, and A:. These are created to help the command-line experience and are not typical functions. The names of these functions, though, show an interesting difference between functions and scripts, since it should be clear that you can't have a file named A:.ps1. Function naming, in general, should follow the same naming convention as cmdlets, namely Verb-Noun, where the verb is from the verb list returned by Get-Verb and the noun is a singular noun that consistently identifies what kinds of things are being referred to. Our MdAndGo function, for example, should have been named according to this standard. One reasonable name would be Set-NewFolderLocation.

Comment-based help

Simply defining a function creates a very basic help entry. Here's the default help for the MdAndGo function that we wrote earlier in the chapter:

Comment-based help

We haven't given PowerShell enough to build a useful help entry, but fortunately for us there is a simple way to include help content through comments. This feature in PowerShell is called comment-based help and is documented thoroughly in the about_comment_based_help help topic. For our purposes, we will add enough information to show that we're able to affect the help output, but we won't go into much detail.

With comment-based help, you will include a specially formatted comment immediately before the function. Keywords in the comment indicate where the help system should get the text for different areas in the help topic. Here's what our function looks like with an appropriate comment:

Comment-based help

After executing the file to create the function, we can test the comment-based help using get-help MdAndGo, just as before:

Comment-based help

Since we included examples, we can either use the –Examples switch or use the really handy –ShowWindow switch:

Comment-based help

Note that we didn't need to number our examples or include the C:\PS> prompt. These are added automatically by the help system.

Parameters revisited

Just like scripts, functions can take parameters. Remember that parameters are placeholders, which let you vary the execution of the function (or script) based on the value that is passed to the parameter (called the argument). The MdAndGo function that we wrote earlier had a single parameter, but you can use as many parameters as you want. For instance, if you had a process that copied items from one folder to another and you wanted to write a function to make sure that both the source and destination folders already exist, you could write a function like this:

Parameters revisited

It should be clear from this example that parameters in the Param() statement are separated by commas. Remember that you don't use commas to separate the arguments you pass when you call the function. Also, the Param() statement needs to be the first statement in the function.

Typed parameters

Something else you might have noticed in this example is that we have specified that both the parameters have to be strings. It's hard to test this, since anything in the command-line is already a string. Let's write a function to output the average of two integers:

Typed parameters

Tip

Here, we've used [int] to specify that the $a and $b parameters are integers. Specifying a type allows us to make certain assumptions about the values in these variables, namely, that they are numeric and it makes sense to average them. Other common types used in PowerShell are [String] and [DateTime].

The function works if you pass numeric arguments, but passing strings (such as orange and apple) causes an error. If you read the error message, you can see that PowerShell tried to figure out how to change "orange" into a number, but couldn't come up with a transformation that works. PowerShell has a very versatile parameter binding mechanism that attempts a number of methods to transform the arguments you pass into the type of parameters you have specified.

Switches

Besides using types like string and int, you can also use the switch type, which indicates a parameter that is either present or absent. We've seen switch parameters in cmdlets, such as the –Recurse switch parameter for Get-ChildItem. Remember that you don't specify an argument to a switch parameter. You either supply the parameter (such as –Recurse) or you don't. Switch parameters allow for easy on/off or true/false input. The parameter can be tested easily in an if statement like this:

Switches

Typed parameters

Something else you might have noticed in this example is that we have specified that both the parameters have to be strings. It's hard to test this, since anything in the command-line is already a string. Let's write a function to output the average of two integers:

Typed parameters

Tip

Here, we've used [int] to specify that the $a and $b parameters are integers. Specifying a type allows us to make certain assumptions about the values in these variables, namely, that they are numeric and it makes sense to average them. Other common types used in PowerShell are [String] and [DateTime].

The function works if you pass numeric arguments, but passing strings (such as orange and apple) causes an error. If you read the error message, you can see that PowerShell tried to figure out how to change "orange" into a number, but couldn't come up with a transformation that works. PowerShell has a very versatile parameter binding mechanism that attempts a number of methods to transform the arguments you pass into the type of parameters you have specified.

Switches

Besides using types like string and int, you can also use the switch type, which indicates a parameter that is either present or absent. We've seen switch parameters in cmdlets, such as the –Recurse switch parameter for Get-ChildItem. Remember that you don't specify an argument to a switch parameter. You either supply the parameter (such as –Recurse) or you don't. Switch parameters allow for easy on/off or true/false input. The parameter can be tested easily in an if statement like this:

Switches

Switches

Besides using types like string and int, you can also use the switch type, which indicates a parameter that is either present or absent. We've seen switch parameters in cmdlets, such as the –Recurse switch parameter for Get-ChildItem. Remember that you don't specify an argument to a switch parameter. You either supply the parameter (such as –Recurse) or you don't. Switch parameters allow for easy on/off or true/false input. The parameter can be tested easily in an if statement like this:

Switches

Default values for parameters

You can also supply default values for parameters in the Param() statement. If no argument is given for a parameter that has a default, the default value is used for this parameter. Here's a simple example:

Default values for parameters

Using parameters is a powerful technique to make your functions applicable to more situations. This kind of flexibility is the key to write reusable functions.

Output

The functions that I've shown so far in this chapter have either explicitly used the return keyword, or used write-host to output text to the console instead of returning the data. However, the mechanism that PowerShell uses for function output is much more complex for several reasons.

The first complication is that the return keyword is completely optional. Consider the following four functions:

Output

We can see that they all output the same value (100) by adding them all together:

Output

The reason that these functions do the same thing is that PowerShell uses the concept of streams to deal with data coming out of a function. Data that we consider "output" uses the output stream. Any value that isn't used somehow is written to the output stream, as is a value given in a return statement. A value can be used in many ways. Some common ways of using a value are:

  • Assigning the value to a variable
  • Using the value as a parameter to a function, script, or cmdlet
  • Using the value in an expression
  • Casting the value to [void] or piping it to Out-Null

    Tip

    [void] is a special type that indicates that there is no actual value. Casting a value to [void] is essentially throwing away the value.

The last function, Test-Output4, shows that we can explicitly write to the output stream using write-output.

Another interesting feature of the output stream is that the values are placed in the output stream immediately, rather than waiting for the function to finish before outputting everything. If you consider running DIR C:\ -recurse, you can imagine that you would probably want to see the output before the whole process is complete, so this makes sense.

Functions are not limited to a single output value, either. As I stated earlier, any value that isn't used is output from the function. Thus, the following functions output two values and four values, respectively:

Output

The output from these strange functions illustrates another property of PowerShell functions, namely, they can output more than one kind of object.

In practice, your functions will generally output one kind of object, but if you're not careful, you might accidentally forget to use a value somewhere and that value will become part of the output. This is a source of great confusion to PowerShell beginners, and can even cause people who have been using PowerShell to scratch their heads from time to time. The key is to be careful, and if in doubt, use Get-Member to see what kind of objects you get out of your function.

Summary

The functions in PowerShell are powerful and extremely useful to create flexible, reusable code. Parameters, including switch parameters, allow you to tailor the execution of a function to meet your needs. PowerShell functions behave differently from functions in many other languages, potentially outputting multiple objects of different types and at different times.

In the next chapter, we will look at PowerShell modules, a feature added in PowerShell 2.0 to help gather functions and scripts into libraries.

For further reading

  • Get-Help about_Functions
  • Get-Help about_Parameters
  • Get-Help about_Return
  • Get-Help about_Comment_Based_Help
  • Get-Help about_Providers
  • Get-Help about_Core_Commands