With great PowerShell comes great responsibility
Power to the Shell
PowerShell allows administrators to gather information, change Registry parameters, work with Active Directory, manipulate services, look at events, work with Windows Management Instrumentation (WMI), and much more. In this introduction to PowerShell [1], I outline some of the more common problems that administrators face and how to solve them with PowerShell. You won't need programming skills, nor do you need to be adept at scripting of any kind. PowerShell handles the complex tasks behind the scenes by allowing you to work with a series of shortcut commands and parameters (switches) in the foreground.
The strength of PowerShell lies in its simplicity. The cmdlets are easy to learn, easy to use, and easy to extend into an essential toolset for which there is no equivalent. The excitement of PowerShell comes from the ability to manage other systems remotely with cmdlets. PowerShell bestows great power on Windows Administrators – power that, used wisely, will save time, effort, and frustration in environments where hundreds or thousands of systems require attention. PowerShell is in a state of flux; it evolves with each iteration of Microsoft's operating systems. Windows 7 and Windows Server 2008 use PowerShell 2.0, which I use here. Windows 8 and Windows Server 8 will use the new PowerShell 3.0, to be released soon.
To begin, I introduce PowerShell information retrieval to you via the "Get" commands (cmdlets), which allow you to look at system information in a non-destructive way without changing anything. Think of the Get cmdlets as information browsing. Then, I show you how to retrieve information and change it. Finally, I teach you how to control services, processes, and commands on your local system and on remote systems.
Starting the PowerShell Environment
PowerShell cmdlets aren't available in a standard CMD window. To invoke PowerShell, open a CMD window and type powershell
. You should see a response similar to this:
C:\Users\khess>powershell Windows PowerShell Copyright (C) 2009 Microsoft Corporation. All rights reserved. PS C:\Users\khess>
The PS
tells you that you are in PowerShell and that PowerShell is ready to receive instructions in the form of cmdlets, which are shortcut names for programs that do your administrative bidding.
Before going further, you should check your PowerShell version by typing $Host
at the PS prompt: (you can also retrieve this information with the Get-Host
cmdlet):
PS C:\Users\khess> $Host Name : ConsoleHost Version : 2.0 ...
If the Version parameter doesn't display 2.0, point your browser to the Microsoft support pages [2], scroll down, and download the appropriate components for your system. Once you've downloaded and installed the PowerShell 2.0 components, check your version again to be sure that you have PowerShell 2.0 installed.
PowerShell Syntax
All cmdlets have the following syntax:
PS> Verb-Noun [-Switch <string[]>]
Switches and strings extend the power of the cmdlets but are optional. For example, the cmdlet to list the running processes on your system, Get-Process
, is in the Verb-Noun format. The cmdlets are always shown with uppercase and lowercase words, but they're not really case sensitive. Case is used for illustrative purposes only.
Cmdlets have the form, Verb-Noun, and nouns are always singular. Get-Processes
, although grammatically satisfying, is syntactically incorrect in PowerShell. When you type Get-Process
at the PS prompt, you should see a table listing all of your system's running processes that looks similar to Task Manager's Processes tab.
PowerShell on Your Local System
Before touching production systems with PowerShell cmdlets or scripts, you should practice and test with your local workstation. A virtual machine would be an even better sandbox in which to test. Never test on production systems.
Next, look at the Get-Process
cmdlet output. Is it in the format you want? If not, you can change it by passing parameters to switches that the cmdlet interprets then returns your changes to you. The default Get-Process
cmdlet reports system process information in the format shown in Listing 1.
Listing 1: Default Get-Process Format
01 Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName 02 ------- ------ ----- ----- ----- ------ -- ----------- 03 81 9 2392 3076 76 0.12 3996 AmIcoSinglun64 04 74 9 1464 2568 42 0.09 2172 armsvc 05 195 10 2380 2984 62 0.50 1164 atieclxx 06 102 9 2024 2380 34 0.08 960 atiesrxx 07 118 9 15648 15772 50 2664 audiodg 08 1337 528 42548 61444 284 2,611.69 1540 09 ...
Processes display in descending alphabetical order by ProcessName, but this is not that useful to an administrator who needs to know which process is consuming too much CPU time, for which you need to sort by CPU consumption. If you use Linux, the top
command is the equivalent of what you want to see here. To sort by CPU time, issue the cmdlet:
PS> Get-Process | Sort-Object -Descending -Property CPU
This example shows PowerShell syntax and piping from one cmdlet to another. To use this more advanced cmdlet syntax, you have to begin by writing out the desired results from your script: For example, "list processes (easy) in order of highest CPU usage to lowest."
With PowerShell, you can pipe the output of one cmdlet into the input of another cmdlet. If you're also a Unix or Linux shell scripter, then you know the power of the pipe. If not, you'll discover it with PowerShell.
However, before you can effectively pipe the output of one cmdlet into another, you need to know what the output is and how you want it configured when piped to the other cmdlet. You also need to know what your options are for filtering output from both cmdlets. An extensive Help system is available to assist you, which is outlined in more detail in the "PowerShell Help System" section later in this article.
You aren't limited to processes on your system; you can also look at the status of services with a command such as:
PS> Get-Service
In Listing 2, you can see that the Get-Service
cmdlet returns a table displaying the Status, Name, and DisplayName of all the services on your system. Although this is useful information, if you want to look at a single service or a group of related services, a display of all services is a bit cumbersome.
Listing 2: Get-Service
01 PS> Get-Service 02 Status Name DisplayName 03 ------ ---- ----------- 04 Running AdobeARMservice Adobe Acrobat Update Service 05 Stopped AeLookupSvc Application Experience 06 Stopped ALG Application Layer Gateway Service 07 Running AMD External Ev... AMD External Events Utility 08 Stopped AppIDSvc Application Identity 09 Stopped Appinfo Application Information
Notice in Listing 3 that I use the -Name
switch to filter on the service name. If you try -DisplayName
in your filter, when you enter -Status
, an error is returned from the system because Status is not a parameter you can manipulate directly. It's a bit advanced at this point to explain why, so I'll tell you that, when you find such a property, you'll have to use a workaround.
Listing 3: Filtering with Get-Service
01 PS> Get-Service -Name wmi* 02 Status Name DisplayName 03 ------ ---- ----------- 04 Stopped wmiApSrv WMI Performance Adapter
One workaround is to use Sort-Object
. For example, to see a list of all services and their statuses, filtered by status, enter:
PS> Get-Service | Sort-Object Status
Listing 4 shows the result.
Listing 4: Filtering by Status
01 PS> Get-Service | Sort-Object Status 02 Status Name DisplayName 03 ------ ---- ----------- 04 Stopped QWAVE Quality Windows Audio Video Experience 05 Stopped QBFCService Intuit QuickBooks FCS 06 Stopped RasMan Remote Access Connection Manager 07 Stopped RasAuto Remote Access Auto Connection Manager 08 Stopped WwanSvc WWAN AutoConfig
PowerShell on Remote Systems
Using PowerShell on a local system is interesting and useful, but the real power of PowerShell is using it with remote systems. The beauty of PowerShell is that you don't have to RDP or connect remotely in any direct way to run PowerShell commands on a remote system to which you have access: You just have to specify the remote computer's name with the -ComputerName
switch and the name of the remote system. If you do not specify a computer name, the command defaults to the local system.
For example, to see a list of running processes on a remote system, you can enter:
PS> Get-Process -ComputerName <Remote System Name>
My remote Windows server name is XenApp1, so I'd use that with the -ComputerName
switch. To view services or a subset of running services, you can run a similar cmdlet. In Listing 5, I wanted to see all services related to the 2X service.
Listing 5: View a Subset of Services
01 PS> Get-Service -ComputerName XenApp1 -Name 2X* 02 Status Name DisplayName 03 ------ ---- ----------- 04 Running 2X Publishing A... 2X Publishing Agent 05 Running 2X Redundancy S... 2X Redundancy Service 06 Running 2X SecureClient... 2X SecureClientGateway 07 Running 2X Terminal Ser... 2X Terminal Server Agent
By now, you should be able to see the pattern of how PowerShell works for local versus remote systems. Basically, any cmdlet that works locally will also work remotely if you specify -ComputerName <Remote Computer Name>
in the syntax.
The PowerShell Help System
If you're going to use PowerShell with any of its advanced features, or if you're going to write your own scripts, you'll need to learn to use PowerShell's Help system.
To get help, you simply type the cmdlet Get-Help
at the PS prompt. To find help with a specific cmdlet, use the Get-Help
cmdlet followed by the command with which you need assistance:
PS> Get-Help Get-Process
The Help system looks like Unix-style man pages, with the cmdlet name (NAME), a short description (SYNOPSIS), the cmdlet syntax (SYNTAX), an extended description (DESCRIPTION), links and related commands (RELATED LINKS), and additional information (REMARKS).
To see examples of the cmdlet you're interested in, use the Get-Help
command like this:
PS> Get-Help Get-Process -examples
PowerShell also understands wildcards (* ?
). For example, to see a list of all of the Get cmdlets, use:
PS> Get-Help Get*
To see a list of all possible Get cmdlets with a four-letter Noun, use:
PS> Get-Help Get?????
The five ?
s represent the hyphen (-
) and the four Noun positions.
The cmdlets under RELATED LINKS could also be useful. For example, Get-Process
has four related cmdlets:
Start-Process
Stop-Process
Wait-Process
Debug-Process
You now have enough information to practice running Get cmdlets on your local system and on remote systems to view system information. If you venture further into this powerful language and its possibilities in your environment, please set up a virtual machine so you don't irreversibly damage a live or production system.
Starting and Stopping Services
One of the basic duties of an Administrator is to start, stop, and restart services on systems. PowerShell empowers you to do so on a local system and on remote systems. Manipulating services on a local system takes PowerShell scripting to an overly complex extreme, but it demonstrates the syntax and the necessary switches (parameters) required to control services on remote systems.
Although you can't directly manipulate services on remote systems, it is possible to do so programmatically. It adds an additional level of complexity to your task, but the result is worth the trouble. How do you know if a cmdlet has remote system capability? Use the help system
C:\> Get-Help Get-Service
and focus on the SYNTAX section of the help listing (Listing 6).
Listing 6: Get-Help SYNTAX Section
SYNTAX Get-Service [[-Name] <string[]>] [-ComputerName <string[]>] [-DependentServices] [-Exclude <string[]>] [-Include <string[]>] [-RequiredServices] [<CommonParameters>] Get-Service -DisplayName <string[]> [-ComputerName <string[]>] [-DependentServices] [-Exclude <string[]>] [-Include <string[]>] [-RequiredServices] [<CommonParameters>] Get-Service [-InputObject <ServiceController[]>] [-ComputerName <string[]>] [-DependentServices] [-Exclude <string[]>] [-Include <string[]>] [-RequiredServices] [<CommonParameters>]
Note that one of the optional Get-Service
parameters is -ComputerName
. This parameter means the Get-Service
cmdlet has the ability to extract information from remote systems named by the -ComputerName
switch.
However, if you were to look at the Start-Service
or Stop-Service
service manipulation cmdlets, you would not see -ComputerName
or anything related to remote computers.
Listing 7 is an example from my local system. I need to check the status and start the Windows Defender service if it isn't already started. The output tells me that the service is not only in a Stopped state but is Disabled.
Listing 7: Get-Service on a Local System
01 PS C:\Users\khess> Get-Service -DisplayName 'Windows Defender' 02 Status Name DisplayName 03 ------ ---- ----------- 04 Stopped WinDefend Windows Defender 05 PS C:\Users\khess> Start-Service -DisplayName 'Windows Defender' 06 Start-Service : Service 'Windows Defender (WinDefend)' cannot be started due 07 to the following error: Cannot start service WinDefend on computer '.'. 08 At line:1 char:14 09 + start-service <<<< -DisplayName 'Windows Defender' 10 + CategoryInfo : OpenError: 11 (System.ServiceProcess.ServiceController:ServiceController) [Start-Service], 12 ServiceCommandException + FullyQualifiedErrorId : 13 CouldNotStartService,Microsoft.PowerShell.Commands.StartServiceCommand
A quick visual Services
check, as shown in Figure 1, shows that the Windows Defender Service is in a Disabled state. You can't start a disabled service; you have to change its startup type to Automatic or Manual before you can start the service.
In PowerShell, however, you can perform a status change with the Set-Service
cmdlet:
PS C:\Users\khess> Set-Service -DisplayName 'Windows Defender' -StartupType Automatic cmdlet Set-Service at command pipeline position 1 Supply values for the following parameters: Name: WinDefend
Now, you can start the service by issuing the Start-Service
cmdlet:
PS C:\Users\khess> Start-Service -DisplayName 'Windows Defender'
It's always wise to check the status of a service that you change for confirmation of its current condition (Listing 8).
Listing 8: Checking Service Status
01 PS C:\Users\khess> Get-Service -DisplayName 'Windows Defender' 02 Status Name DisplayName 03 ------ ---- ----------- 04 Running WinDefend Windows Defender
This is a nice exercise, but it's easier to start and stop Windows services on a local system via the Computer Management Services application. Starting, stopping, and changing the service startup type on remote systems in an automated fashion is not so straightforward. As I stated earlier, to perform this function, you have to do so programmatically.
The Windows Telnet Service is disabled by default on all server systems because it is a non-secure protocol. That is to say, the Telnet client and server exchange usernames and passwords in cleartext, which can be easily captured and used to compromise systems. However, it has all of the elements of a Windows service that's perfect for demonstration purposes.
To list the services and their statuses remotely, I try:
PS C:\> Get-Service -ComputerName XENAPP0 Get-Service : Cannot open Service Control Manager on computer 'XENAPP0'. This operation might require other privileges. ...
The output tells me that the remote system doesn't allow remote management. To enable remote management, I need to connect to the system on which I'm trying to run the remote command, launch a CMD window as Administrator, and run:
C:\Users\Administrator>winrm quickconfig
After answering Yes to the questions that follow, I will be able to run remote commands on my systems.
Now when I rerun Get-Service
on the remote system, I see useful output. In Listing 9, you can see that the Telnet Server service is in a Stopped state and most likely is also Disabled. Your first inclination might be to issue a command such as
Listing 9: Get-Service on a Remote System
01 PS C:\> Get-Service -ComputerName XENAPP0 02 Status Name DisplayName 03 ------ ---- ----------- 04 ... 05 Running TermService Remote Desktop Services 06 Stopped THREADORDER Thread Ordering Server 07 Stopped TlntSvr Telnet 08 Running TrkWks Distributed Link Tracking Client 09 ...
PS C:\> Start-Service -ComputerName XENAPP0 -DisplayName Telnet
to start the service. Doing so would return the expected error:
Start-Service : A parameter cannot be found that matches parameter name 'ComputerName'. ...
Fortunately, Microsoft provides a -ComputerName
switch for the Set-Service
cmdlet that makes the task of remotely setting its StartupType
parameter to Automatic
or Manual
direct and easy:
PS C:\> Set-Service -ComputerName XENAPP0 -DisplayName Telnet -StartupType Manual cmdlet Set-Service at command pipeline position 1 Supply values for the following parameters: Name: TlntSvr
Note that you're prompted to enter the name of the service (TlntSvr) to complete the action. Although the status of the Telnet Server service will not have changed if you enter Get-Service
again, its StartupType
is set to Manual
. The service is now in a "startable" state. There's no -ComputerName
parameter available for the Start-Service
cmdlet. After a lengthy search, however, I found the following method to start a service on a remote system:
PS C:\> (Get-WmiObject -Computer XENAPP0 Win32_Service -Filter "Name='TlntSvr'").InvokeMethod("StartService",$null)
I checked out several methods that claimed to be successful, but this is the only one that did it for me. After issuing this command, I received a single 0
as a return value, which indicates success. Checking the status of the Telnet Server service again outputs the following line of interest:
Status Name DisplayName ------ ---- ----------- ... Running TlntSvr Telnet ...
To stop the service, just replace Start-Service
with Stop-Service
in the script. This technique works for any Windows service.
Starting and Stopping Processes
The description of the Start-Process
cmdlet is: "Starts one or more processes on the local computer." However, you probably realize by now that there will be a way to get around this egregious limitation. That workaround is known as the Invoke-Command
cmdlet. Invoke-Command
, as stated in the cmdlet SYNOPSIS
, runs commands on local and remote computers. For example, if there's a hung process on a remote system running in a CMD window, you can find it and stop that process with a simple Invoke-Command
(Listing 10).
Listing 10: Invoke-Command
01 PS C:\> Get-Process -ComputerName XENAPP0 02 PS C:\Users\khess> Get-Process -ComputerName XENAPP0 03 Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName 04 ------- ------ ----- ----- ----- ------ -- ----------- 05 80 9 1292 4548 33 336 CdfSvc 06 23 5 1996 3084 47 2208 cmd 07 36 5 1180 4296 60 1724 conhost 08 37 6 1908 4848 61 2644 conhost 09 ...
Note the Id of the cmd process (2208). When you stop a process, you must know its Id and send a signal to the remote system to kill or stop that process Id:
PS C:\Users\khess> Invoke-Command -ComputerName XENAPP0 -ScriptBlock {Stop-Process -Id 2208} Confirm Are you sure you want to perform the Stop-Process operation on the following item: cmd(2208)? [Y] Yes [A] Yes to All [N] No [L] No to All [?] Help (default is "Y"): Y
The process will end immediately on receipt of the Stop-Process
signal.
To run a CMD process by substituting Start-Process
and cmd
for a process name. The command would look like this:
PS C:\Users\khess> Invoke-Command -ComputerName XENAPP0 -ScriptBlock {Start-Process cmd -PassThru }
The -PassThru
switch allows you to send output to your screen. Without that switch, you'll see no output onscreen. You might assume that the script successfully created a CMD window on the remote system, but you'll find that this did not happen. Well, it did happen momentarily, but the remote CMD window launches and then dies.
Windows, by design, doesn't allow you to launch programs in this way. That said, it is still possible to do so programmatically. However, doing so is far outside the scope of an introductory PowerShell article.
Running Commands on Remote Computers
Via PowerShell, you have the ability to run commands on remote computers that have non-interactive output to the screen. For example, if you want to see a quick netstat on a system, you can do so by issuing the command:
PS C:\Users\khess> Invoke-Command { netstat } -ComputerName XENAPP0
Or, do the following to check the TCP/IP configuration:
PS C:\Users\khess> Invoke-Command { ipconfig /all } -ComputerName XENAPP0
If you try launching a CMD window this way, you'll understand what happens when you attempt to run an interactive application (Listing 11). The CMD does launch, but dies, on the remote system, and you receive screen output from the results of that launch.
Listing 11: Attempt to Run Interactive App
01 PS C:\> Invoke-Command { cmd } -ComputerName XENAPP0 02 Microsoft Windows [Version 6.1.7600] 03 Copyright (c) 2009 Microsoft Corporation. All rights reserved. 04 C:\Users\khess\Documents> <--Remote system directory. 05 C:\> <--Local system prompt.
If you're familiar with the PsTools suite, now owned by Microsoft, the PsExec command performs a similar function, as do these commands in PowerShell. To do so, you launch a command on a remote system in a non-interactive way, send the command, and receive a response. Although you can carry on an interactive PowerShell session, that's the topic of a future article.
Summary
PowerShell is an important tool to add to your Windows administrator toolbox. The more experience you gain with this essential tool, the better you'll like it. PowerShell isn't a traditional scripting language, although it does include many of the same attributes: variables, prompted input, redirected output, looping, and advanced decision making. If you're new to PowerShell, practice with the Get cmdlets for a while before attempting any Set or other intrusive cmdlets.
I hope that you can see the potential for PowerShell in your environment. Be aware that systems in a domain act differently from those in a standalone environment. Administrators might have to make domain-wide policy changes to allow remote management on systems. PowerShell and remote management are system administration tools and aren't necessarily inherent security risks, but you might have difficulty when pleading your case to your security team.
Typical users (those without Local Administrator or Domain Administrator privileges) can't run these commands. PowerShell has security checks built in so that non-administrator staff can't issue system-changing commands and wreak havoc in your environment.