Windows Power Shell Simple Commands. Powerful Administration

Don Jones

This column is based on a prerelease version of Windows PowerShell. All information herein is subject to change.

It's been a long time coming, but Windows PowerShell is almost ready to launch. That means it's time for Windows administrators to start taking notice. Windows PowerShell offers what is perhaps the easiest and most flexible way to automate a wide variety of administrative tasks, making you more efficient and more effective in your work.

But even more importantly, Microsoft is building the graphical administrative consoles of products like Exchange Server 2007 and System Center 2007 on top of Windows PowerShell™. This means you'll be able to perform almost any administrative task from within Windows PowerShell. Microsoft plans to do the same with the administrative capabilities of more products over time. Thus, Windows PowerShell could eventually become the first all-purpose tool for administering nearly any Microsoft server product. To help you get started, I'll discuss Windows PowerShell regularly in this new column. Be sure to download a copy of the software.

Power and Simplicity

As the name implies, Windows PowerShell is a shell, not unlike the Command Prompt (Cmd.exe) that's been around since Windows NT® 3.1. Cmd.exe isn't going away, but with the availability of Windows PowerShell, there are few reasons to continue using Cmd.exe.

Using Windows PowerShell isn't all that different than using Cmd.exe—except that Windows PowerShell is, well, more powerful. Like Cmd.exe, Windows PowerShell has a built-in scripting language, although it is much more flexible than Cmd.exe's primitive batch language. How flexible? With Windows PowerShell, you can automate extremely complex tasks with a language that includes only about a half-dozen built-in keywords.

Now that I've mentioned scripting, I should probably touch upon security. Windows PowerShell benefits from what Microsoft has learned about security over the past decade plus. By default, Windows PowerShell won't run scripts; it can only be used interactively to run individual commands. If you do enable scripting, you can direct Windows PowerShell to run only digitally signed scripts. All of this is to help ensure that Windows PowerShell doesn't become the next VBScript—a great language that has been frequently misused to create malicious scripts. VBScript isn't going away either, but you'll likely find that Windows PowerShell is easier to use for many different tasks.

You can do most anything with Windows PowerShell that you would have done with Cmd.exe. For example, you can run ipconfig and you'll get the same familiar output. But Windows PowerShell introduces a whole new set of commands that aren't external executable files. These cmdlets (pronounced "command-lets") are built right into Windows PowerShell. (For a look at some of the most useful cmdlets to get you started using Windows PowerShell, see the sidebar "Top 10 Cmdlets to Start Using Immediately".)

Top 10 Cmdlets to Start Using Immediately

  • Get-Command retrieves a list of all available cmdlets.
  • Get-Help displays help information about cmdlets and concepts.
  • Get-WMIObject retrieves management information by using WMI.
  • Get-EventLog retrieves Windows event logs.
  • Get-Process retrieves a single or list of active processes.
  • Get-Service retrieves a Windows service.
  • Get-Content reads in text files, treating each line as a child object.
  • Add-Content appends content to a text file.
  • Copy-Item copies files, folders, and other objects.
  • Get-Acl retrieves access control lists (ACLs).

For a complete list of cmdlets that ship with Windows PowerShell, go to windowssdk.msdn.microsoft.com/en-us/library/ms714408.aspx.

All cmdlets are named with a standard verb-noun format, making them easy to learn and remember. For instance, running the Get-Command cmdlet will list all the available cmdlets. Perhaps the most useful cmdlet for an administrator is Get-WMIObject. Say you want to find out what service pack Server2 is running. Simply run:

Get-WMIObject Win32_OperatingSystem –Property ServicePackMajorVersion
 –Computer Server2

To discover this same information using VBScript, you would have to write several lines of code. Other cmdlets let you work with services (Start-Service, Stop-Service, and so forth), processes (Stop-Process and others), files (Rename-Item, Copy-Item, Remove-Item, Move-Item, for example), and much more. Many of these cmdlets even have shortcut names, called aliases. In the case of Get-WMIObject, you can just type gwmi. Running Get-Alias will provide you with a list of these shortcut names.

Why Object Oriented Matters

Built on the Microsoft® .NET Framework, Windows PowerShell is completely object-oriented. Usually, only a software developer would get excited about that, but in this case, the object orientation results in a huge time savings for administrators. This is because administrators can now work directly with rich objects right within a text-based shell. Consider this example:

Get-Process | Sort-Object pm –desc | Select-Object –first 10

This is just a single line with three different cmdlets separated by pipes (more on this in a moment). The first cmdlet retrieves all running processes and then passes, or pipes, those objects to Sort-Object. This second cmdlet sorts on each process object's pm (meaning Physical Memory) property, placing them into descending order. The sorted collection of process objects is then piped to Select-Object, which picks the first 10 and displays them. The result? This simple line displays the top 10 physical memory consumers on the machine, as shown in Figure 1. This is an extremely efficient way to take a quick look when doing some troubleshooting.

Figure 1 Using a Simple Cmdlet to Troubleshoot

Figure 1 Using a Simple Cmdlet to Troubleshoot (Click the image for a larger view)

The use of pipes (the vertical line character typically located on the backslash key on U.S. keyboards) is an essential part of what makes Windows PowerShell so capable. Using this character, you can pass (or pipe) objects from one cmdlet to another, allowing each to further refine the results, format them for display, and so on. This mechanism works because every cmdlet returns one or more objects, rather than pure text, giving subsequent cmdlets the full object to work with.

The use of objects in Windows PowerShell is pervasive, all the way down to its variables. And you don't have to declare variables up front; you can just start using them by placing a dollar sign ($) before a variable name. While it's not required, you can also tell Windows PowerShell what type of data you want to put into the variable. That lets Windows PowerShell map the variable to one of the extremely powerful .NET Framework types, giving you a lot of additional built-in functionality. For example, suppose you want to prompt for a computer name and retrieve the service pack version from that computer, but you don't know if the person typing in the computer name will include two backslashes (such as \\Server2). Since you know that the Get-WMIObject cmdlet doesn't need the backslashes, you can save the computer name into a String variable and use the Replace method to replace backslashes with empty strings, as shown here:

[string]$c = Read-Host "Enter computer name"
$c = $c.Replace("\","")
    Get-WMIObject Win32_OperatingSystem
    –Property  ServicePackMajorVersion
    –Computer $c

The value for the –Computer parameter has been provided in the $c variable. That variable was initially created as a string, so it picked up all the functionality of the .NET Framework String type, including the Replace method. Of course, learning about all of these capabilities will take a while, but you should find them easy to pick up through examples. Windows PowerShell itself helps to simplify learning. For example, if you type $c = $c. (don't forget the period mark) and press Tab, Windows PowerShell will display Clone(), the first method of the String type. If you keep pressing Tab, Windows PowerShell will cycle through all the available methods. Essentially, when you do this, Windows PowerShell is showing you what it knows how to do with a String!

Here's a tougher task. Read a list of computer names from a file, with one name per line, and show each computer's service pack number. In VBScript, this task would require a dozen or more lines of code. In Cmd.exe, you would have to use a complicated batch file. In Windows PowerShell, this task takes just one line:

Get-Content "c:\computers.txt" | foreach  
{ $_; gwmi Win32_OperatingSystem -prop 
ServicePackMajorVersion -comp $_ }

The Get-Content cmdlet reads the contents of C:\Computers.txt. Each line of the file becomes an object in its own right. This collection of objects—computer names, that is—is piped to the foreach command, which is really just an alias for the ForEach-Object cmdlet. The commands inside the curly braces are repeated once for each object that is piped in—for this example, that means they run once for each computer name. The special $_ variable will contain the current object (that is, the current computer name).

Inside the curly braces are actually two commands: The first simply displays the current computer name by outputting the contents of $_. The second is the now-familiar gwmi. The result is a list of service pack versions for every computer listed in the file. All this was done with one relatively straightforward line of commands.

Notice that the –Property and –Computer parameter names have been abbreviated. Windows PowerShell only requires enough to uniquely distinguish the parameter names.

Readability and Reuse

Writing a single line of commands and parameters, however, doesn't help with readability. Windows PowerShell lets you break this into something more readable, which you can type directly into the shell without ever writing a script. Here's how it might look:

PS C:\> $names = get-content "c:\computers.txt"
PS C:\> foreach ($name in $names) {
>> $name
>> gwmi Win32_OperatingSystem -prop ServicePackMajorVersion -comp $name
>> }
>>

This time, the contents of the file are stored in the variable $names. This example still uses foreach, but it isn't being input through a pipeline, so you have to tell it which collection of objects to loop through and what variable to store each object in—the part that says ($name in $names). Everything else is pretty much the same, and as soon as you hit Enter, the code is executed and the results displayed.

If you want to use this same code repeatedly, you can simply make a function out of it. Once again, type this directly into the shell:

PS C:\> function Get-ServicePacks ($file) {
>> $names = get-content $file
>> foreach ($name in $names) {
>> $name
>> gwmi win32_operatingsystem -prop servicepackmajorversion -comp $name
>> }
>> }
>>

As you can see, not much has actually changed. This simply encloses the previous example in a function named Get-ServicePacks (in keeping with the Windows PowerShell verb-noun naming convention). The function now has an input parameter named $file, which has been substituted in the Get-Content cmdlet so that a different file can be specified when the function is run. Now that the function is defined, you can simply run it by calling its name, almost like a cmdlet, and passing the input parameter:

PS C:\> Get-ServicePacks c:\computers.txt

Figure 2 shows the results.

The downside here is that this function will only exist for as long as that instance of Windows PowerShell is running. Once you close the shell, the function vanishes. You can copy the function into your Windows PowerShell profile, which is a sort of auto-run script that executes each time Windows PowerShell starts. Doing so would make the function available in every Windows PowerShell window you open. Or, if you want, you can make the function into a standalone script, which you can then execute simply by typing the script's path and file name.

Figure 2 Results of Running the Get-ServicePacks Function

Figure 2 Results of Running the Get-ServicePacks Function

The World is a File (or Folder)

There's more to Windows PowerShell than just functions and cmdlets. Let's look at file management as a quick example of what else is in store. You're probably more than familiar with drive and folder navigation in Cmd.exe—type C: to switch to the C drive, and type cd \test to change into the C:\Test folder. Windows PowerShell works in exactly the same way, although cd is just an alias for the Set-Location cmdlet.

Try running Get-PSDrive, the cmdlet that lists all available drives. In addition to the usual C:, D:, and perhaps A: drives, you'll also find one named Cert, another named Env, and others named HKCU and HKLM. Windows PowerShell actually exposes many different types of storage resources as "drives," making things like the local certificate store, environment variables, and registry available through a familiar file-like navigational interface.

Change to the HKEY_LOCAL_MACHINE registry hive by typing Set-Location HKLM: (or cd hklm: if you prefer the shortcut) and hitting Enter. Then run cd software\microsoft to change into the SOFTWARE\Microsoft key. You can use dir—an alias for the Get-ChildItem cmdlet—to list the sub-keys in this portion of the registry. If you want to remove a key, use del to delete it as if the key were a file or folder. (Be very careful though—serious problems might occur if you remove required keys or modify the registry incorrectly.)

All of this flexibility comes from providers, which map resources (like the registry and certificate store) into a format that looks like a file system. Microsoft plans to extend Windows PowerShell through additional providers, giving you the ability to, for example, navigate an Exchange Server store as if it were a file system. This is a very important technique in that it takes the huge variety of repositories used by Windows and makes them all appear identically, plus it makes them all manageable through a system of commands and techniques that you're already familiar with.

Designed for Safety

I've already mentioned that Windows PowerShell was designed with security and safety in mind. The primary security feature in Windows PowerShell is its execution policy. By default, this policy is set to Restricted, which you can verify by running the Get-ExecutionPolicy cmdlet. In Restricted mode, scripts don't run. Period. Since that's the default mode, Windows PowerShell can't be used to run scripts out of the box.

You can specify other modes using the Set-ExecutionPolicy cmdlet. I personally prefer the RemoteSigned mode. This allows local scripts (but not remote scripts) to run without being digitally signed, providing the easiest way to develop and test scripts. The AllSigned mode doesn't run any scripts unless they've been digitally signed, using a certificate issued by a trusted publisher. Finally, the Unrestricted policy runs anything. I don't recommend this at all, as it opens up Windows PowerShell to run malicious scripts that may find their way onto your computer. Note that the execution policy can also be governed by Group Policy, which overrides any local settings. (Set-ExecutionPolicy will warn you if a Group Policy setting is overriding your local settings.)

In addition, Windows PowerShell won't run scripts from the current directory unless you specify the path. This is designed to prevent command hijacking. Say someone creates a script named IPConfig.ps1 (PS1 is the file-name extension for Windows PowerShell script files). If files could be run out of the current folder, there would be a risk that you might type ipconfig and run this user created script when, in fact, you were expecting to run the Windows program Ipconfig.exe. Since Windows PowerShell does not run scripts out of the current folder, this mistake can't happen. If you do want to run a script out of the current folder, just specify the path: .\myscript, for example. The explicit reference to the current folder ensures that you know you're running a script and not a shell command.

Windows PowerShell also has features that make experimentation safer. For example, consider (but please do not try) this frightening combination:

Get-Process | Stop-Process

The Get-Process cmdlet creates a collection of process objects and pipes them to the Stop-Process cmdlet—which will indeed stop them! The result is a blue screen STOP error after about five seconds as critical Windows processes are killed off. But you can see what will happen, without actually letting it happen, by adding the very convenient –Whatif parameter:

Get-process | Stop-Process -Whatif

Running this in Windows PowerShell results in a bunch of statements that tell you what the cmdlets would have done without actually letting them do it. The online help system in Windows PowerShell (accessible by using the help alias) doesn't yet document the –Whatif parameter, but keep it in mind. It's a great tool for testing scripts and cmdlets to verify their results without actually doing anything potentially harmful or disruptive.

Wrap-Up

Among the features that didn't make it into this version of Windows PowerShell, perhaps the most significant is support for Active Directory® Services Interface (ADSI). While Windows PowerShell can utilize the very robust .NET classes that work with Active Directory and other directory services, it doesn't yet have a convenient Get-ADSIObject cmdlet. The result is that it is a bit difficult to work with directory objects.

Also, Windows PowerShell often offers a number of different ways to perform the same task. That's good, but it can make learning Windows PowerShell more confusing since for any given task you might see a half-dozen different examples of how to do it.

All of that will settle over time and the Windows PowerShell team will continue to add features and capabilities to the product. To stay in the loop, visit the team's blog.

Don Jones is the founder of ScriptingAnswers.com, and the coauthor of Windows PowerShell: TFM (SAPIEN Press, 2006). Contact him at don@scriptinganswers.com.

© 2008 Microsoft Corporation and CMP Media, LLC. All rights reserved; reproduction in part or in whole without permission is prohibited.