Windows PowerShell is a scripting language that allows for centralized and automated management of Windows environments, providing functions like automation, looping, and debugging. It is a powerful tool for administrators to manage Windows systems efficiently.
Windows 7 includes new management tools to automate and centrally administer Windows 7 machines. The two major additions are Windows PowerShell 2.0 and improvements to the Group Policy settings. These tools are introduced in this section but Chapter 6, “Networking and Mobility,” goes much more in-depth on how to use the tools for easier management of Windows environments.
Windows PowerShell is a scripting language that supports multiple functions such as automation, looping, branching, functions, debugging, exception handling, etc. It allows for the centralized and automated management of entire Windows environments. Think of PowerShell as the older command prompt on steroids. It is a real shell with scripting capabilities. Windows PowerShell is also a scripting engine that alongside Windows PowerShell Integrated Scripting Environment (PowerShell ISE) can be used to easily write, debug, and execute scripts. PowerShell is a great new tool for managing Windows environments and should be considered by all administrators.
Group Policy has also been improved to include almost every single Windows 7 setting there is. This allows for policy set by OU in the Active Directory to manage your entire environment from a centralized location. Group Policy has traditionally been used to enforce policies but with Group Policy Preferences, administrators can now select how settings are applied. IE 8 Group Policy has been added and supports over 1,000 group policies for IE configurations in your environment, a great way to enforce security settings.
Powershell is a very powerful management language used with Windows system. Windows Powershell is a combination command-line shell and scripting language. Powershell allows access to COM and WMI managementcomponents. This greatly expands the potential of the Powershell language.
Powershell is one of the main tools used for managing Windows systems. In fact, many Windows management consoles are actually built on top of Powershell. Powershell includes a hosting API that can be used by GUI applications to access Powershell functionality.
Powershell commands can be executed as cmdlets, Powershell scripts, Powershell functions, and standalone executables. The Powershell process will launch cmdlets within the Powershell process. Standalone executables will be launched as a different process. As Windows moves forward, there will be an increasing reliance on Powershell. It's important that you understand how to use it to manage and administer your systems. As we go through this book we will periodically reference different Powershell commands than may be useful to you.
To start at the very beginning, we answer the most basic question, “What is PowerShell?” PowerShell is a command-based shell and scripting language built on the .NET framework with incredible power and versatility designed especially for system administration. The latest version as of this writing is version 2.0 and it was officially released on October 28, 2009. We say shell and scripting language because both components can be used separately. At its fundamental level, it is a command-based shell in a console-like interface that will allow you to simply issue it commands and call its powerful scripting language to accomplish what tasks you need it to do. In this interactive use, you directly issue commands to the console and it performs the desired tasks. The other powerful part of PowerShell that takes full advantage of the underlying scripting language is the ability to run longer or complex instructions from a script. This allows you to automate some of the tasks you might need to perform. The final piece which is brand new to version 2.0 is the ability to run commands on one or more computers from a single computer running PowerShell. These abilities together should allow you to tackle almost any job without problem.
Another aspect of PowerShell you need to become familiar with is its command syntax. The commands PowerShell uses are called cmdlets. It is through these simple instructions that you will be performing all the common system administration tasks. cmdlets all share a similar syntax composed of a verb and a noun in a verb-noun format. As an example, we will use one of the most helpful commands, get-help. The get-help cmdlet is an easy example. Typing Get-help followed by the subject or command you want to get help about will get you more information about it. It will either give you the help you need or give you a list of subjects so that you can narrow it down to which one you need (Table 11.1).
Table 11.1. Getting Help Is Easy
cmdlet
Description
Get-Help about
Will list all the subjects that start with about
Get-Help about_scripts
Will get the specific help file about_scripts
Get-Help *about*
Will give you a list of all the subjects that have “about” anywhere in them
You will get a chance to try some cmdlets of your own later on in this chapter, but for now, it is enough that you simply see what they look like if you have not before.
As you can see, the main goal of Windows PowerShell is to give the administrator more control over the system and make it easier to perform the required tasks. It increases the administrator's bag of tools by adding automation and remoting and their associated advantages. Imagine only having to write a command once and then being able to run it multiple times on different machines. This eliminates the need to run the same thing over and over by simply having PowerShell let you run it multiple times. It also eliminates some of the human error aspects since you only need to write it once and therefore only check it once. In this chapter, we will cover many of the new commands for Windows Server 2008 R2 and try to give you a feel for them and what they are supposed to do.
What is new in PowerShell V2
Version 2.0 of Windows PowerShell was officially released on October 28, 2009 after much anticipation by the PowerShell community. It introduces a large amount of new features that were not available before. It stays true to its purpose of making system administration easier by adding remoting, debugging tools and other things to the administrator's new shiny toolkit.
Of all the new features in Windows PowerShell version 2.0, the one feature that seems to elicit cheers and get the most attention is the new remote management or remoting feature. PowerShell supports interactive remoting where you connect directly and issue commands, fan-in (1 pc to many) and fan out (many pcs to 1). It allows the administrator to run scripts on one or many remote computers with a single command. Gone are the days of tediously going to different computers and running the same script on each of them one at a time. Gone are the days of taking your preferred script and copying it from machine to machine. Now, all you have to do is establish a session to the other computer or computers you want to run the script on and then issue the commands. From running a single cmdlet to a whole script, it is that simple. PowerShell 2.0 does have to be installed on the computers you are running the scripts on, the originating one and the target ones.
Another new feature sorely lacking from the earlier versions of PowerShell is the ability to run background jobs. This new ability of PowerShell version 2.0 to run background jobs allows you to asynchronously run other jobs or processes in the background while you continue to work on your session. You issue the command for the background job and immediately get the prompt back so that you can continue to work while the background job executes. These background jobs can be run on your local machine or on a remote computer.
Windows PowerShell 2.0 has also included new script debugging capabilities. Included now is the built-in capability to debug your scripts. It allows you to set breakpoints, step through code, monitor variable values, and more. It also allows you to decide what actions to take when the break point is hit. These capabilities give you the tools necessary to debug all your scripts with relative ease.
Another new feature is that of eventing. Eventing is the new capability of PowerShell to handle and react to events. It now includes an infrastructure that not only can listen for system and application events and act based on them. In addition, you can now create your own events which allow you to set up a chain of actions based on what your scripts do.
The new version also comes with a large number of new cmdlets. PowerShell version 2.0 introduces a little over 100 new cmdlets with numerous capabilities. All these cmdlets range from all the new functionalities mentioned earlier to powerful logging tools and mail-sending cmdlets.
PowerShell 2.0 also supports script internalization. This allows PowerShell to display messages in multiple languages. In addition to the regular get-help command and man command at the command line, PowerShell includes online help via a parameter to the get-help command. Using get-help, the command to get help on and then the online parameter opens a browser for you with the command help in Microsoft TechNet. Another new feature is the support of Modules. A module is a package that includes Windows PowerShell commands like cmdlets, providers, aliases, and variables. PowerShell now lets you create these modules and organize your commands so that you can share them with others. PowerShell version 2.0 has also included a lot of advanced functions. These functions behave just like cmdlets but they are fully written in the Windows PowerShell scripting language as opposed to C#.
Another new feature that PowerShell 2.0 has brought is the new Integrated Scripting Environment or ISE. The PowerShell ISE lets you run commands and scripts in an integrated development environment (IDE). It allows you to edit and debug your scripts with many options to figure out what is wrong and where it is going wrong like most graphical interfaces. It makes writing cmdlets easier with multiline editing and line and column numbers. It includes color coding like most graphical interfaces which makes it very easy for the user to write and execute their own commands by immediately showing them if they have made any syntax errors. It has several panes or windows to make it easier to see what is going on. One pane to write your command(s), one to show your scripts, and one to show you the output or results of your commands or scripts. In essence, it makes it really easy to get started with PowerShell by giving the user the tools to create what they want quickly and easily.
Back with Windows Server 2003, Microsoft introduced a new scripting language called Windows PowerShell. PowerShell has gone from an add-on to a core part of the Windows Management Tools. Starting with Windows 7 and Windows Server 2008 R2, every management tool that has a user interface to work with also has a set of PowerShell Cmdlets, which can be used to automate the management of that system. The current policy at Microsoft with regard to PowerShell is that the PowerShell Cmdlets are written first, and then the management interface is written to use the PowerShell Cmdlets. This way, they can be sure that you can script every function that can be done in the user interface.
Tip
If you administrate an Exchange 2007 Server, you will notice that the Exchange team has included many Cmdlets that do not have a UI version making the PowerShell Cmdlets essential to managing an Exchange 2007 implementation. Hopefully, this won't become the case for every Microsoft application as this would be a very steep learning curve for many people. However, it is something to keep in mind, and if you can't find a way to do it in the UI, check PowerShell because there might be a way to do it through script.
The initial implementation of Windows PowerShell was very rough in that the editor was Windows Notepad. The implementation of Windows PowerShell included with Windows 7 is much more user friendly. It includes a nice three-pane editor, as shown in Figure 7.19, which gives you a command window at the bottom that allows for tab completion of your commands. The middle pane of the application shows the results of the run command, and the top pane of the application is the script which you are editing.
FIGURE 7.19. New Windows PowerShell Editor
Cmdlets
There are hundreds of Cmdlets available in Windows PowerShell, so it is not possible to review all of them here. Cmdlets are small commands that can be called from PowerShell either by themselves or with other commands. To get a list of the available Cmdlets on your system, run get-help * in Windows PowerShell and it will output a list of the available Cmdlets, as well as any aliases setup on your system. Microsoft has created, by default, several dozen aliases so that normal DOS commands will work within PowerShell. As an example, there is no PowerShell command called “dir”; however, there is an alias called “dir,” which points to the Get-ChildItem Cmdlet, so that when you type dir into PowerShell, you get back results that you are looking for. You will also find alias for *nix commands as well, such as the command ls, which is also mapped to the Get-ChildItem Cmdlet.
PowerShell Cmdlets are small applications, which can be run from the Windows PowerShell interface. PowerShell Cmdlets are loaded within modules, which can be loaded manually by using the Import-Module Cmdlet. These PowerShell Cmdlets can perform all the management functions of the Windows management interface.
Some Cmdlets return data from the system, whereas other Cmdlets make changes to the system. You can combine these in a single command by using the pipe symbol (|) between the commands. For example, you could use a Windows PowerShell command to query the computer for a list of the network shares on the computer, and then change the comment on the network share all within a single command.
The most important Cmdlet that you can send objects to is the Where Cmdlet. This allows you to filter the output from another Cmdlet. As an example, you could use the WmiObject Cmdlet to query the list of network shares from the current computer. In order to return only the administrative shares, you would pipe the output from the WmiObject Cmdlet to the Where Cmdlet looking for names that end in a dollar sign as shown in Figure 7.20.
FIGURE 7.20. Using Windows PowerShell to Query for Hidden Network Shares on the Local Server
However, if you take this same command and change the – like command to – notlike command, it will return any network shares on the server that aren't administrative network shares. When run against our sample domain control, you'll get an output similar to the one shown in Figure 7.21.
FIGURE 7.21. Using Windows PowerShell to Query for Nonhidden Network Shares on the Local Server
Remote Management
With the introduction of Windows PowerShell 2, which was introduced with Windows 7 and Windows Server 2008 R2, you now have the ability to run PowerShell Cmdlets against remote machines. Until this time, Windows PowerShell Cmdlets could only be run against the same machine in which the PowerShell script was run. This is done by adding the – computername parameter to the first command as shown in Figure 7.22.
FIGURE 7.22. Accessing a Remote Computer's Network Shares through PowerShell
Integrated Scripting Environment
The initial version of Windows PowerShell included the shell environment as shown in figures such as Figure 7.22. Windows Server 2008 R2 and Windows 7 introduced the Integrated Scripting Environment, which provides you with a more user-friendly interface as shown as Figure 7.23. The top of the interface allows you to open, save, and run the scripts. The middle window shows any output from the script that you are currently running. The lower part of the window is an immediate run window, which allows you to run commands in a more interactive application like the prior PowerShell V1 application. This lower window includes tab completion of both the command and the parameters for the command.
FIGURE 7.23. Windows PowerShell Interactive Scripting Environment
Windows Management Infrastructure (WMI), Windows Script Host(WSH), and Windows PowerShell are all native Windows scripting interfaces that can be used to query information from the Registry. Providing an introduction to any of these facilities is beyond the scope of this book but suffices to say that a great deal of useful information can be found at the Windows Script Center [2]. Essentially, I'm mentioning these scripting interfaces here for completeness, although I have never used them myself.
With the introduction of Windows PowerShell 2, which was introduced with Windows 7 and Windows Server 2008 R2, you now have the ability to run PowerShell Cmdlets against remote machines. Until this time, Windows PowerShell Cmdlets could only be run against the same machine in which the PowerShell script was run. This is done by adding the – computername parameter to the first command as shown in Figure 7.22.
FIGURE 7.22. Accessing a Remote Computer's Network Shares through PowerShell
Cmdlets are .NET classes and not standalone executables. They are the smallest unit of execution in PowerShell. While each cmdlet has a specific purpose (such as to move a mailbox or change a property of a mailbox), you can “assemble” shell commands together to automate common management processes through scripts. The default version of PowerShell that you might run on a Windows server does not include any of the commands required to work with Exchange data as any application that wants to use PowerShell has to load its command set into the basic shell. Behind the scenes, when you start the Exchange Management Shell (EMS), PowerShell starts the core shell and then loads the set of Exchange commands through a snap-in (an assembly of commands) using the command:
The Get-PSSnapin command returns the list of snap-ins that PowerShell has loaded on top of the core shell. You can use the Remove-PSSnapin command to remove a snap-in from the core.
The latest version of PowerShell and some additional information is available at http://www.microsoft.com/windowsserver2003/technologies/management/powershell/default.mspx. If you run Windows 2003, you have to install PowerShell on a server before you can install Exchange 2007, if only because Microsoft built EMC on top of PowerShell. Windows 2008 includes PowerShell as part of the basic operating system. You can run PowerShell and EMC on Windows XP or Vista4 workstations, so you can use these workstations to manage Exchange through the shell or the GUI, as long as you also install PowerShell and the Exchange management components on your workstation.
Microsoft announced the “Community Technology Preview” of version 2.0 of PowerShell in November 2007. This version offers some exciting new features such as remote scripting and even the prospect of a GUI, but the Exchange development group has not yet qualified PowerShell 2.0 against Exchange 2007 SP1. Early reports are that most things work on Windows 2003 and that everything seems to work on Windows 2008, but you can never tell exactly what will happen when you combine pieces of software together that the engineers (from all relevant development groups) had not tested comprehensively. The current plan of record is that Microsoft will use PowerShell 2.0 for the next major release of Exchange but that it is unlikely that they will upgrade Exchange 2007 (in any service pack) to take advantage of the new features in PowerShell 2.0. For this reason, you should not install PowerShell 2.0 on an Exchange server (except for your personal testing) until Microsoft ships the final code for PowerShell 2.0 and the Exchange team verifies that it works properly with Exchange 2007. All of the examples in this book use PowerShell 1.0.
Figure 4-1 shows the list of PowerShell snap-ins generated by the Get-PSSnapin command on an Exchange 2007 server. You might have other snap-ins listed if you install other applications that support PowerShell, such as the Virtual Machine snap-in or those from third party software vendors that extend the capabilities of PowerShell, such as PowerGadgets.
Figure 4-1. Listing the set of snap-ins loaded into PowerShell
The Get-Process command (and its shorthand alias, ps) is a good example of one of the standard sets of commands available to work with Windows. This command lists all of the active processes running on a server. You can then group the output so that you know the company who generated the software that is running in the processes. For instance, here is the result of scanning the processes active on an Exchange 2007 server. You can see components from the hardware provider (HP and Compaq), anti-virus (Symantec), and base Windows (Microsoft), among others.
You can also find information about the top processes that are consuming most CPU on a server. For example:
The output from running this command on an Exchange 2007 server is shown in Figure 4-2 where we can see three interesting processes appear in the list. The first is the Exchange log replay service, so we know that this server uses log replication to protect its databases. The second is the Microsoft Search service, which generates the content indexes for databases on the server. The last is the process that synchronizes Active Directory content from a hub transport server to an Edge server. Because of the presence of the second process, we know that this server is a multi-role server that supports both the mailbox and hub roles. The server must therefore be running LCR. We know this because CCR only runs on a clustered mailbox server that can't support the hub transport role, so this particular server can't synchronize with an Edge server. LCR and CCR are explored further in Chapter 9. Of course, as we'll learn when we get into the details of PowerShell syntax, you could isolate the Exchange processes that run on a server by including a filter through the Where command:
Figure 4-2. Output from PS command
Getting into the details of how you can build filters and the properties that you can use is a stretch for now, given what we have covered for PowerShell so far. Moving on, ps is an alias or shorthand for the Get-Process command. You can define aliases for your favorite PowerShell commands to cut down on the amount of typing you have to do or rely on the default set.5 The Get-Alias command returns a list of all of the currently defined aliases, including the ones that PowerShell creates for you, such as ps and commands that you will use very often when you view Exchange data, such as ft for Format-Table. You can create a handy text file that contains all the aliases with this command:
Another example of using a PowerShell command to work with Windows is how to return the current status for the set of services that composes an Exchange server:
As we will see later on in this chapter when we discuss how to use PowerShell to test different aspects of Exchange 2007, the Test-ServerHealth command provides another way to discover whether all the Exchange services are running. In Chapter 2, we met an example of how PowerShell is able to query the Active Directory and how to extract a list of Global Catalog servers. Here's an example of how to retrieve information about the current Active Directory forest by loading information into a variable that you can subsequently interrogate to retrieve precise data. It is a good thing to record this information regularly so that you know exactly the exact state of the forest.
It's somewhat frustrating to have to work with Active Directory objects in such a clunky manner (through their .NET classes). Basic Windows command-line utilities such as dsmod and dsadd are available, but unfortunately, Microsoft did not include a PowerShell snap-in for Active Directory in Windows 2008 and we will have to wait for them to do the work before a complete PowerShell ecosystem is available for Exchange.
You can use PowerShell to launch applications that are associated with a particular file extension. For example, assuming that Word is installed on a system, here's how you can launch Word to edit a specified document:
You can even ask PowerShell to count the number of words in a Word document:
Another interesting way to use PowerShell is when you want to interrogate the Event Log to check for a particular event. For example, here's how to look through the Application Event Log for event 701, which is generated by the Store after it finishes a background defragmentation run against a database. You can use the same technique for any event. The command shown here may seem complex initially, but you will soon get used to combining the Where command (to apply a filter on a data set), the Select command (to pick out records to work with), and the Format-List command (to format output data in a list) as you work with Exchange data.
We can also output the details of the event that Exchange provides so that you can see the name of the database that is processed. In this instance, we are more specific that the Source of the events that we want to see is ESE (the Store database engine) rather than other parts of Exchange (like the transport engine) that generate the same event. In addition, we trim the output of the message property to remove a blank line that you'd see otherwise.
Installing .NET Framework 2.0 and Windows PowerShell
As is the case with any other Exchange 2007 Server role, you also need to install both the .NET Framework 2.0 component as well as Windows PowerShell, which we showed you how to do in Chapter 3.
As an introduction to PowerShell scripting, we will start with the traditional Hello World script. In order to get started, we again need a few components. Depending on the specific Microsoft operating system we are using, we may or may not already have PowerShell installed. In Windows 7 or Windows Server 2008 R2 or later, PowerShell is already installed. For other versions, with a minimum of Windows XP SP2 being required, PowerShell can be downloaded from Microsoft.4 All the PowerShell examples in this chapter were developed on Windows 7 SP1.
We also need a text editor of some variety. The simplest to use is Notepad, which ships with Windows. If we are using PowerShell 2.0, as Windows 7 ships with, we also have access to the PowerShell Integrated Script Editor (ISE). ISE can be accessed by running powershell_ise.exe, or by right-clicking and choosing Edit on a .ps1 file. We will be using ISE as an editor for the rest of our PowerShell examples.
TIP
If we have gained remote access to a Microsoft system, and are working solely from the command line, creating or editing a file may be a bit of a problem. On older Microsoft OSes, Windows XP and earlier, we have access to the edit command, which provides us with a handy text editor that will work from the command line. On newer versions, this goes away. The closest that we can use to get to a text editor is the copy con command. We invoke it with copy con, then the filename, such as copy con test.txt. This will allow us to create, but not edit, a multiline file, pressing Ctrl-z then Enter when we are finished.
Next, we will need to wrestle with the system security settings a bit in order to get them to relax enough to allow us to run our own scripts. If we run a PowerShell script before doing this, we will just get an error and it will refuse to run. In order to make this change on Windows 7, we will need to navigate to All Programs | Accessories | Windows PowerShell on the Start menu, then right-click on the Windows PowerShell shortcut and choose Run as Administrator.
WARNING
By changing the execution policy for PowerShell scripts to be more permissive than the defaults, we are opening a vulnerability on our systems! Although we are allowing the minimum permissions we can use in order to work with PowerShell scripts, this is still a security hole that the bad guys could potentially use to attack us. This is relatively unlikely to happen with this particular setting, but caveat scriptor. For more details on this setting, Microsoft has additional information on the various options we can use.5
This will open a PowerShell shell with administrative privileges so that we can make the required changes. In this window, we need to type Set-ExecutionPolicy RemoteSigned. This will set our execution policy for PowerShell to allow us to run any scripts we might create, and any scripts we download signed by a trusted publisher.
We can see what the permission setting exchange should look like in Figure 1.9. We should now be ready to create our HelloWorld script.
FIGURE 1.9. Changing Permissions in PowerShell
Hello World
One of the simplest ways to create our script is to create a file called HelloWorld.PS1, then right-click on it and choose Edit. This will open the PowerShell ISE, as shown in Figure 1.10.
FIGURE 1.10. PowerShell ISE
In the top window, right next to the 1, we will want to paste the following code:
Write-Output "Hello World"
That's all there is to it. After saving the file, we can either run our code manually now by opening a PowerShell shell, navigating to it, and then running HelloWorld.PS1, or run it by clicking on the green triangle (10th from the left) in the toolbar of ISE. In ISE, we will see the output from our script execution in the middle window of the interface. Write-Output is one of the PowerShell cmdlets we discussed earlier in this section, and it contains all the necessary functionality to print our statement. Also notice that, unlike our example in bash, we did not need to use anything like a shebang in order to indicate the interpreter we needed to use. In Windows, this function is handled through the use of the file extension .PS1, which indicates that the script should be handled by PowerShell.
Variables
Variables under PowerShell are, again, very similar to what we might find under bash. By default, variables have no type, meaning that a given variable can contain text or numeric data. Variables are always addressed as $<variable name>, whether assigning data to them or extracting data from them. Let's look at a quick variable example and a new cmdlet:
$processes = Get-Process powershell_ise
$processes
In this case, we are invoking the Get-Process cmdlet in order to get the process information for the ISE application we are using to develop our scripts. Then we are taking the returned data from the cmdlet and storing it in the $processes variable. On the next line, we are echoing out the contents of $processes. If everything was successful, we should see output similar to this:
PS C:\> C:\variables.ps1
Handles
NPM(K)
PM(K)
WS(K)
VM(M)
CPU(s)
Id
ProcessName
-------
------
-----
-----
-----
------
--
-----------
601
61
142208
121788
865
9.39
3012
powershell_ise
The return from the cmdlet we echoed contains the information on the handles, nonpaged memory, paged memory, working set, virtual memory, CPU usage, process ID, and process name of the process on which we had requested information. Also notice that the formatted output of the cmdlet survives being stored in a variable and echoed out again.
We can also do local and global variable scoping in PowerShell. We will also talk about functions in PowerShell, as this provides us with a nice demonstration of how to deal with variable scope.
function hello{
$LOCAL:name = "whoever you are"
write-output "hello there "$name
}
$name = $args[0]
Write-output "hello there "$name
hello
write-output "hello there "$name
The first item we find in our script is the function we will use to demonstrate the scope of our variable. Functions in PowerShell are very similar to those we discussed when we went over the same topic in bash. The first line of the function is simply the function tag, the name by which we want to refer to the function, in this case hello, and the opening curly bracket, {, to start the function. On the first line inside the function, we can see the line creating our local variable. This variable, created with the local keyword, will only exist inside the scope of our function, and the function will keep its own copy of the contents, regardless of what we name it. Here, we set the contents of $LOCAL:name to the string whoever you are. On the next line, we echo out a greeting and the contents of the variable, then close up the function with }.
In the main body of the script, we take in an argument from the command line, $args[0], and place the contents of it into $name. Notice that this is the same variable name we used in our function, but without the LOCAL tag to set the variable scope, this implicitly makes the variable global in scope. Next, we echo out our greeting and the contents of $name, run the function, and echo the greeting and variable contents again. We can see from the output in Figure 1.11 that, even though we changed the contents of our local copy of $name in the function, we did not change the contents of the same variable in the main body of the script, due to the difference in variable scopes.
FIGURE 1.11. Differing Variable Scopes
Arguments
We can work with arguments in PowerShell in a very simple fashion. To expand on our process script in order to use an argument, we can do the following:
$processes = Get-Process $args
$processes
In order to run this script, we need to supply an argument containing the name of the process for which we will retrieve the information. In this case, we will use the explorer process as an argument, so we will run ./arguments.ps1 explorer. We should see output similar to Figure 1.12, showing us information for at least a couple of explorer processes.
FIGURE 1.12. Output from explorer.ps1
When we take in an argument in PowerShell, it is stored in an array called $args. We can think of an array as a variable with multiple storage compartments called elements, each of them individually addressable. With an array we can use a single data structure to store multiple pieces of information, adding, changing, or deleting them as we need to, without necessarily affecting any of the data we don't care to change.
In order to address the first element in the array, which holds our first (and only) argument, the name of the process on which we want to retrieve information, we could either refer directly to the first element in the array $args[0], or simply refer to the entire contents of the array with $args. If we wanted to refer to further arguments, we would just need to add a number, such as $args[1], which would refer to the second argument, $args[2] for the third argument, and so on.
If we wanted to modify our code to pull in information on multiple processes, we would change our script to:
$processes = Get-Process $args[0], $args[1]
$processes
Running the script as .\arguments.ps1 explorer winword would then return us information on both processes (likely more than one in the case of explorer).
Control statements
Just as we discussed in the first part of the chapter when we talked about control statements in bash, we can find the same in PowerShell and indeed in almost all high-level programming languages. We will discuss some examples of both conditionals and looping functions in PowerShell in this section.
Conditionals
Conditional statements in PowerShell follow many of the same lines as we might find in any of a number of other languages. We will look at if else statements and switches here.
If else statements are a slight variation on the if statements we can find in most languages. We can add an if statement to our earlier process example to add a little intelligence to it:
$processes = Get-Process
If ($processes –match "winword"){
write-output "Microsoft Word is running"
}else{
write-output "Microsoft Word is not running"
}
In this case, we run the same code we did previously to dump the information on all the running processes. We then set up our if statement to match against the output of Get-Process in order to look for the string winword, which is the process name for Microsoft Word. The –match operator uses a regular expression to search for the string we give it, and is a good choice in this particular instance, as it keeps us from having to parse through the entire process listing manually for the string we want.
Based on whether we get a match or not, we can then determine whether the process is running and provide the appropriate output to the user.
Switch statements function along the same general lines as an if statement, but enable us to configure a more complex set of actions based on the conditions we find. We can make our process checker a bit more capable by using a switch:
$processes = Get-Process
switch -regex ($processes){
"winword" {write-output "Microsoft Word is running"}
"explorer" {write-output "Explorer is running"}
"vlc" {write-output "VLC is running"}
}
Now we can look for multiple processes in one go. Here we used the same cmdlet to dump out the process listing, and instead of being limited by the if statement, we built a more complicated set of conditions using the switch statement. We set up the first line of the switch on line 2 of the script, and configure our matching to use a regular expression (regex) and match against the contents of $processes, the variable where we stored the process listing.
In the body of our switch statement, we set up a series of lines, each for a different potential match. On each line, our very simple regex will check the line for the name of our process. Interestingly, since our variable $processes is holding multiple lines of text, the switch will attempt to match each line in the variable against each case in the switch. This is actually handy, since, as we can see in Figure 1.13, some processes do have more than one instance running.
FIGURE 1.13. Output from the Switch Example
Looping
There are a number of looping structures we can use in PowerShell and we will look at a couple of the more common and useful possibilities here. Looping in PowerShell follows much the same format as looping in most programming languages. One of the simplest looping constructs we can look at is the for loop:
We would want to run this script with .\looping.ps1codingforpentesters.com. Let's have a look at what we did here. First we set up the beginning of our for loop, for ($counter=0; counter –lt 10; $counter++). So, this line initializes $counter with zero. This will be the variable that keeps track of how many times our for loop has executed. Next, we evaluate $counter to make sure it is still less than 10 with –lt. If this is true, we will continue; if not, we will stop right here. Lastly, we increment the value in $counter by 1. Next, we can find the body of our loop enclosed in braces, {}.
Inside the loop we are doing a very nice little bit of .NET work to call the ping function provided there. As PowerShell is a native Microsoft tool, it is fully capable of taking advantage of Microsoft's .NET development framework and all the goodies that come with it. In this particular case, we are instantiating an object to use for ping, running a ping against the host name or IP provided by our first argument, and sleeping for five seconds. After we finish sleeping, we will go back to the top of the loop, repeating this for a total of 10 times through. In the output, we will see the results of our pings display each time we execute the loop.
We can also use another construct called a foreach loop, like so:
Here, we are doing things a bit differently. In this case, we want to ping more than one machine with our pinging routine. We would feed this in from the command line in the form of arguments, or we could pull it in from a file, but in this case we will use an array to hold the names of our hosts.
The first line in our script sets up and populates the array. We can see that this is very similar to setting up and populating a variable, with a little additional information to indicate we want it to be an array, in the form of the @. We also need to put parentheses around our list of elements and separate each of them with a comma.
After constructing the array, we set up the foreach loop. Here, we say foreach ($device in $devices). This means that for each item in our array called $devices, we should be doing something on each individual element, which we refer to as $device. We could have used any variable name to hold the contents of each element as we process through them, such as $i or $monkey; it really makes no difference what we call it.
The only other change from our previous pinging routine is to change the target of our ping.send to $device, in order to match our foreach configuration.
The initial version of Windows PowerShell included the shell environment as shown in figures such as Figure 7.22. Windows Server 2008 R2 and Windows 7 introduced the Integrated Scripting Environment, which provides you with a more user-friendly interface as shown as Figure 7.23. The top of the interface allows you to open, save, and run the scripts. The middle window shows any output from the script that you are currently running. The lower part of the window is an immediate run window, which allows you to run commands in a more interactive application like the prior PowerShell V1 application. This lower window includes tab completion of both the command and the parameters for the command.
FIGURE 7.23. Windows PowerShell Interactive Scripting Environment