October 1st, 2009

Hey, Scripting Guy! How Can I Use Windows PowerShell to Stop Services?

Bookmark and Share

(Portions of this blog post previously appeared in the Microsoft Press book, Windows PowerShell Scripting Guide.)

Hey, Scripting Guy! Question

Hey, Scripting Guy! I have some services that are running on my computer. I looked them up on TechNet and I do not believe I need them to be running. Is there a way I can write a Windows PowerShell script to stop the services? If I can, would you be able to write me a sample script to give me the idea of what I need to do? I sure would appreciate it.

<

p style=”MARGIN: 0in 0in 8pt” class=”MsoNormal”>– TM

Hey, Scripting Guy! Answer

Hello TM,

Microsoft Scripting Guy Ed Wilson here. One of my pet peeves is services that are running that I do not think need to be running. It seems like there are thousands of examples of these non-essential services running on my computer. The problem is that all of the services are used for something. One example is the Zune Bus Enumerator service. Why is that service running 24×7 when I only connect my Zune to my computer once a month at the most? In my case, it is running because I am too lazy to stop and to restart the service. It needs to be running when I connect my Zune to my desktop, but most of the time it could just as well be turned off. This is just one such example. There are many other examples (maybe not thousands) that you will find on your computer. The trick is to know exactly what a service does and make an intelligent decision as to whether you can live without the benefits the service provides. In the past, I have written different scripts that put my computer into different configurations depending on which tasks I am planning on performing. One of these days, I will do that again. As with everything, make sure you test the configuration in a virtual machine before actually using it on a real computer.

TM, let’s take a look at stopping services using Windows PowerShell. There are two ways to stop services in Windows PowerShell. You can use the Stop-Service cmdlet, or you can use WMI. The two ways of stopping services are illustrated here using the bits service as an example:

Stop-Service –name bits
(Get-WmiObject -class win32_service -filter “name = ‘bits'”).stopService()

As you can see, the easiest way to stop a process is to use the Stop-Service cmdlet. In the StopService.ps1 script we stop the BITS service on our computer. There are two ways to stop a service using the Stop-Service cmdlet: by name or by displayname. In the StopService.ps1 script, we stop the BITS service by using its service name: bits. If we were to use the displayname property to stop the BITS service, we would need to type, “Background Intelligent Transfer Service”. In general, if you know the actual service name, it will be less typing to use the name rather than the displayname property to control the service. The $strService variable is used to hold the name of the service we wish to stop. After we have the name of the service, we use the Stop-Service cmdlet to stop the service. Here we use the –name parameter and supply it the name of the service to stop that is contained in the $strService variable. The StopService.ps1 script is seen here:

StopService.ps1

$strService = “bits”
Stop-Service -Name $strService

When using the Stop-Service cmdlet to stop a service, ensure that the script is running with administrator rights, or it will fail. The error shown in the following image will be generated if administrator rights are not utilized:

Image of error shown if script is run without administrator rights

 

If we wanted to stop several services, we could easily modify the StopService.ps1 script to accommodate our needs. The change to the script would entail creating an array of service names, and using the foreach statement to iterate through the array. The remainder of the script would stay essentially the same.

In the StopMultipleServices.ps1 script, we first create an array of service names. This is done in the first line of our script when we assign the name of several services to the variable $aryServices. We then use the foreach statement to iterate through the array of services. We use the $strService variable as the enumerator through the array. We then use the Write-Host cmdlet to display a message that says a particular service is being stopped. After we have done this, we call the Stop-Service cmdlet and pass it the name of the service to be stopped. The StopMultipleServices.ps1 script is seen here.

StopMultipleServices.ps1

$aryServices = “bits”, “wuauserv”, “CcmExec”
foreach ($strService in $aryServices)
{
 Write-Host “Stopping $strService …”
 Stop-Service -Name $strService
}


As mentioned earlier when we began the section on setting the service configuration, it makes sense to query the acceptStop property of the Win32_Service WMI class before attempting to stop the service.

If you are having a problem with your script using the Get-WmiObject cmdlet and the WIN32_Service class, remember that the property names are not the same as the ones used by the Get-Service cmdlet. A good example of this is the fact that Get-Service uses canstop for the property that indicates if a service is stoppable. The Get-WmiObject cmdlet and WIN32_Service WMI class use the property acceptStop to indicate the exact same thing.

Not only does this make the script run faster and more efficiently, but it can also assist in preventing scripts from hanging. In the CheckServiceThenStop.ps1 script, we first create a variable called $strService that is used to hold the name of the service to stop. This can be hardcoded, or easily modified to accept command-line input. The easiest way to do that would be to modify $strService to use $args. The name of the computer that has the service we wish to stop is stored in the variable $strComputer. Here we are using the name localhost to refer to the local computer. We use the $strClass variable to hold the name of the WMI class to query: WIN32_Service, for this example.

We supply three arguments to the Get-WmiObject cmdlet: the class, the computer, and the filter. The class and computer arguments simply read the values stored in the $strService and $strComputer variables respectively. The –filter argument takes the place of a where clause from a WQL query. By using the –filter argument, the code is a bit cleaner than if we had to write the equivalent WQL query, which would look something like the following:

“Select * from win32_service where name = ‘bits’”

While the syntax is not horrid, it is a lot more typing than the Windows PowerShell statement.

When using the –filter argument of the Get-WMIObject cmdlet, you do not need the word where in the filter. In fact, if you do include the word where in the filter, it will cause the filter to fail. So while it is the equivalent of WQL where clause, it does not include the word where in it. Remembering this will not only save you time typing, but also save you time troubleshooting as well.

We use the if statement to determine if we are going to attempt to stop the service or not. The nice thing is that because the acceptStop property is a Boolean (true/false) value, we can simplify our syntax and use the if(condition is true) format. This is much better than typing something like the following:

If ( $objWMIService.acceptStop –eq “true”)

Our code is exactly the same, but we save a little bit of typing and gain the benefit of more readable code in the process. The actual if statement is seen here:

if( $objWMIService.Acceptstop )

We then open a set of curly brackets, use the Write-Host cmdlet to print out a message indicating our intention to attempt to stop the service specified in the $strService variable. After we have done this, we call the stopService method from the WIN32_Service class and attempt to stop the specified service. The variable $rtn is used to capture completion status information from the method call. A “0” indicates no errors; any other number would merit investigation.

To examine the return codes from the stopService method call, we use the Switch statement. If the returnvalue property is equal to 0, we use Write-Host to indicate that there were no errors, and the method completed successfully. Otherwise, we evaluate the error code for the more common errors, and display the appropriate message. If an error occurs that we did not anticipate, we use the DEFAULT switch to display the exact error number. The Switch statement is seen here:

Switch ($rtn.returnvalue)
  {
   0 { Write-Host -foregroundcolor green “$strService stopped” }
   2 { Write-Host -foregroundcolor red “$strService service reports” `
       ” access denied” }
   5 { Write-Host -ForegroundColor red “$strService service cannot” `
       ” accept control at this time” }
   10 { Write-Host -ForegroundColor red “$strService service is already” `
         ” stopped” }
   DEFAULT { Write-Host -ForegroundColor red “$strService service reports” `
             ” ERROR $($rtn.returnValue)” }
  }

 

However, if the service will not accept a stop command, we use an else statement and use the Write-Host statement to display the name of the service and a statement to the effect that the service will not accept a stop request from the service controller. This should only occur if the service reports that it is not configured to accept a stop command. Keep in mind there could be the situation where the service is configured to accept a stop request, but it simply is not accepting a stop request at this time. This could occur when the service controller was busy working with another service. This should result in a return error code of 5, which is properly evaluated by the Switch statement. The completed CheckServiceThenStop.ps1 script is seen here.

CheckServiceThenStop.ps1

$strService = “bits”
$strComputer = “localhost”
$strClass = “win32_service”
$objWmiService = Get-Wmiobject -Class $strClass -computer $strComputer `
  -filter “name = ‘$strService'”
if( $objWMIService.Acceptstop )
 {
  Write-Host “stopping the $strService service now …”
  $rtn = $objWMIService.stopService()
  Switch ($rtn.returnvalue)
  {
   0 { Write-Host -foregroundcolor green “$strService stopped” }
   2 { Write-Host -foregroundcolor red “$strService service reports” `
       ” access denied” }
   5 { Write-Host -ForegroundColor red “$strService service cannot” `
       ” accept control at this time” }
   10 { Write-Host -ForegroundColor red “$strService service is already” `
         ” stopped” }
   DEFAULT { Write-Host -ForegroundColor red “$strService service reports” `
             ” ERROR $($rtn.returnValue)” }
  }
 }
ELSE
 {
  Write-Host “$strService will not accept a stop request”
 }

Well, TM, that is about all there is to stopping services using Windows PowerShell.

If you want to know exactly what we will be scripting tomorrow, follow us on Twitter or Facebook. If you have any questions, send e-mail to us at scripter@microsoft.com or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

 

Ed Wilson and Craig Liebendorfer, Scripting Guys

 

Author

0 comments

Discussion are closed.