The Strength of PowerShell

In this installment of our multipart PowerShell series, you learn how to control services, processes, and commands on your local and remote systems.

In Part 1 of this PowerShell series, you learned how to start PowerShell, run some basic Get commands, and use the Help system. The excitement of PowerShell comes from the ability to manage other systems remotely with cmdlets. Using PowerShell on your local system has an appeal, but PowerShell is a tool for managing remote systems without the need to log on locally to each system. It’s time-consuming to log on to multiple systems to start, stop, restart, or otherwise manipulate services and processes and retrieve information. 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.

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. In this article, I teach you how to control services, processes, and commands on your local system and on remote systems. 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 this series covers. Windows 8 and Windows Server 8 use the new PowerShell 3.0 that I’ll cover in a later entry.

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 locally on a system and on remote systems. Although manipulating services on a local system takes PowerShell scripting to an overly complex extreme, 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 to find out what you learned in the first part of this series:

C:\> Get-Help Get-Service

Focus on the SYNTAX section of the help listing:

SYNTAX
    Get-Service [[-Name] ] [-ComputerName ]
 [-DependentServices] [-Exclude ] [-Include ]
 [-RequiredServices] []

    Get-Service -DisplayName  [-ComputerName ]
 [-DependentServices] [-Exclude ] [-Include ]
 [-RequiredServices] []

    Get-Service [-InputObject ] [-ComputerName ]
     [-DependentServices] [-Exclude ] [-Include ]
  [-RequiredServices] []

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, you don’t see -ComputerName or anything related to remote computers in Start-Service or Stop-Service service manipulation cmdlets:

C:\> Get-Help Start-Service

SYNTAX
    Start-Service [-Name]  [-Exclude ] [-Include ]
 [-PassThru] [-Confirm] [-WhatIf] []

    Start-Service -DisplayName  [-Exclude ]
 [-Include ] [-PassThru] [-Confirm] [-WhatIf] []

    Start-Service [-InputObject ] [-Exclude ]
 [-Include ] [-PassThru] [-Confirm] [-WhatIf] []

Look at an example from my local system. I need to check the status of and start the Windows Defender service if it isn’t already started:

PS C:\Users\khess> Get-Service -DisplayName 'Windows Defender'

Status   Name               DisplayName
------   ----               -----------
Stopped  WinDefend          Windows Defender

PS C:\Users\khess> Start-Service -DisplayName 'Windows Defender'
Start-Service : Service 'Windows Defender (WinDefend)' cannot be started due to
 the following error: Cannot start service WinDefend on computer '.'.
At line:1 char:14
+ start-service <<<<  -DisplayName 'Windows Defender'
    + CategoryInfo          : OpenError:
(System.ServiceProcess.ServiceController:ServiceController) [Start-Service],
ServiceCommandException + FullyQualifiedErrorId :
CouldNotStartService,Microsoft.PowerShell.Commands.StartServiceCommand
   

This message means that the service is not only in a Stopped state but is Disabled. A quick visual Services check, as shown in Figure 1, reveals 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.

Figure 1: Windows 7 Services highlighting the disabled Windows Defender service.

In PowerShell, you 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:

PS C:\Users\khess> Get-Service -DisplayName 'Windows Defender'

Status   Name               DisplayName
------   ----               -----------
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:

PS C:\> Get-Service -ComputerName XENAPP0

Get-Service : Cannot open Service Control Manager on computer 'XENAPP0'. This operation might require other privileges.
At line:1 char:12
+ Get-Service <<<<  -ComputerName XENAPP0
    + CategoryInfo          : NotSpecified: (:) [Get-Service], InvalidOperationException
    + FullyQualifiedErrorId : System.InvalidOperationException,Microsoft.Power
   Shell.Commands.GetServiceCommand
   

This message tells you that the remote system doesn’t allow remote management. To enable remote management, connect to the system on which you’re trying to run the remote command, launch a CMD window as Administrator, and run:

C:\Users\Administrator>winrm quickconfig

Answer Yes to the following questions and you should then be able to run remote commands on your systems:

WinRM service is already running on this machine.
WinRM is not set up to allow remote access to this machine for management.
The following changes must be made:

Configure LocalAccountTokenFilterPolicy to grant administrative rights remotely to local users.

Make these changes [y/n]? y

WinRM has been updated for remote management.

Configured LocalAccountTokenFilterPolicy to grant administrative rights remotely to local users.

Now, rerun your Get-Service cmdlet on the remote system:

PS C:\> Get-Service -ComputerName XENAPP0

Status   Name               DisplayName
------   ----               -----------
…
Running  TermService        Remote Desktop Services
Stopped  THREADORDER        Thread Ordering Server
Stopped  TlntSvr            Telnet
Running  TrkWks             Distributed Link Tracking Client
...

You see that the Telnet Server service is in a Stopped state and most likely is also Disabled. Your first inclination is to issue a command such as:

PS C:\> Start-Service -ComputerName XENAPP0 -DisplayName Telnet

Doing so returns the expected error:

Start-Service : A parameter cannot be found that matches parameter name 'ComputerName'.
At line:1 char:28
+ Start-Service -ComputerName <<<<  XENAPP0 -DisplayName Telnet
    + CategoryInfo          : InvalidArgument: (:) [Start-Service], ParameterBindingException
    + FullyQualifiedErrorId :
NamedParameterNotFound,Microsoft.PowerShell.Commands.StartServiceCommand

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.

PS C:\> Get-Service -ComputerName XENAPP0

Status   Name               DisplayName
------   ----               -----------
…
Running  TermService        Remote Desktop Services
Stopped  THREADORDER        Thread Ordering Server
Stopped  TlntSvr            Telnet
Running  TrkWks             Distributed Link Tracking Client
...

Although the status of the Telnet Server service hasn’t changed, 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, 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, you should receive a single 0 as a return value, which indicates success. You can again check the status of the Telnet Server service:

PS C:\> Get-Service -ComputerName XENAPP0

Status   Name               DisplayName
------   ----               -----------
…
Running  TermService        Remote Desktop Services
Stopped  THREADORDER        Thread Ordering Server
Running  TlntSvr            Telnet
Running  TrkWks             Distributed Link Tracking Client
...

Replace Start-Service with Stop-Service in the script to stop the service. 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’s a way 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:

PS C:\> Get-Process -ComputerName XENAPP0

PS C:\Users\khess> Get-Process -ComputerName XENAPP0

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
     80       9     1292       4548    33             336 CdfSvc
     23       5     1996       3084    47            2208 cmd
     36       5     1180       4296    60            1724 conhost
     37       6     1908       4848    61            2644 conhost
...

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.

Now try to run a CMD process by substituting Start-Process and cmd for a process name. The command would look something like:

PS C:\Users\khess> Invoke-Command -ComputerName XENAPP0 -ScriptBlock {Start-Process cmd -PassThru }

The -PassThru switch allows you to see output to your screen. Without that switch, you’ll see no output to your screen. You might assume that the script successfully created a CMD window on the remote system. You’ll find that this did not happen. Actually, it did happen momentarily, but the remote CMD window launches and then dies.

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessNam PSComputer
                                                          e          Name
-------  ------    -----      ----- -----   ------     -- ---------- ----------
      5       2     1348        980     6     0.02   2960 cmd        xenapp0

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

You do have the ability, via PowerShell, 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

Active Connections

  Proto  Local Address          Foreign Address        State
  TCP    192.168.1.91:445       KEN:34268              ESTABLISHED
  TCP    192.168.1.91:5985      KEN:34534              ESTABLISHED
  TCP    192.168.1.91:5985      KEN:34535              ESTABLISHED

Or, check the TCP/IP configuration:

PS C:\Users\khess> Invoke-Command { ipconfig /all } -ComputerName XENAPP0

Windows IP Configuration

   Host Name . . . . . . . . . . . . : XENAPP0
   Primary Dns Suffix  . . . . . . . :
   Node Type . . . . . . . . . . . . : Broadcast
   IP Routing Enabled. . . . . . . . : No
   WINS Proxy Enabled. . . . . . . . : No
   DNS Suffix Search List. . . . . . : gateway.2wire.net

Ethernet adapter Local Area Connection 4:

   Connection-specific DNS Suffix  . : gateway.2wire.net
   Description . . . . . . . . . . . : Citrix PV Ethernet Adapter
   Physical Address. . . . . . . . . : 5A-65-68-CB-55-BA
   DHCP Enabled. . . . . . . . . . . : Yes
   Autoconfiguration Enabled . . . . : Yes
   IPv4 Address. . . . . . . . . . . : 192.168.1.91(Preferred)
   Subnet Mask . . . . . . . . . . . : 255.255.255.0
   Lease Obtained. . . . . . . . . . : Friday, April 13, 2012 10:55:02 AM
   Lease Expires . . . . . . . . . . : Monday, April 23, 2012 10:55:02 AM
   Default Gateway . . . . . . . . . : 192.168.1.254
   DHCP Server . . . . . . . . . . . : 192.168.1.254
   DNS Servers . . . . . . . . . . . : 192.168.1.254
   NetBIOS over Tcpip. . . . . . . . : Enabled

If you try launching a CMD window using this method, you’ll understand what happens when you attempt to run an interactive application.

PS C:\> Invoke-Command { cmd } -ComputerName XENAPP0
Microsoft Windows [Version 6.1.7600]
Copyright (c) 2009 Microsoft Corporation.  All rights reserved.

C:\Users\khess\Documents>   <--Remote system directory.

C:\>   <--Local system prompt.

The CMD did launch but died on the remote system, and you received screen output from the results of that launch.

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: You launch a command on a remote system in a non-interactive way; you send the command; and you receive a response. There is a way to carry on an interactive PowerShell session but that’s the topic of a future post.

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 Administrator tools and aren’t necessarily inherent security risks, but you might have a difficult time ahead of you when you plead your case to your Security team.

Normal users (those without Local Administrator or Domain Administrator privileges) can’t run these commands. PowerShell has security checks built in to it so that non-Administrator staff can’t issue system-changing commands and wreak havoc in your environment. Keep practicing, and next time, I’ll look at gathering information from multiple systems.

[Part 3]