Chapter 15. Services

Many of the more important activities performed by a Windows-based computer are carried out using services, specialized applications that can run without the need for user intervention. Because services perform so many important tasks, service management — including monitoring services, configuring service properties, changing service passwords, and starting and stopping services — is an important part of system administration. Scripting can help facilitate the management of services throughout your organization.

In This Chapter

Services Overview

Services are an important part of the Microsoft® Windows® 2000 operating system. A service, which is similar to the daemons used in the UNIX operating system, is an application that can communicate with and be administered by the Service Control Manager (SCM). In addition, a service can:

  • Automatically start each time a computer starts.

  • Run when no user is logged on to the computer. In fact, services can run even if no user ever logs on to the computer.

  • Respond to requests without human intervention.

  • Be configured to automatically restart if it fails.

These capabilities not only make services vitally important to the way computers function, but also make system administration viable. For example, without the DHCP service, an administrator would have to manually configure each computer’s IP address. Without the DNS service, an administrator would have to manually configure and maintain Hosts or Lmhosts files. Without the automated capabilities of services, these tasks simply could not be performed in an enterprise setting.

Because services play such an important role in an organization’s computing infrastructure, service management is a crucial part of any system administrator’s job. If a service stops functioning, it affects the computer on which the service runs. Depending on the service, however, it could also adversely affect many users and their computers. If the DHCP service fails, computers are not given IP addresses, and users lose network connectivity. If the DNS service fails, the Active Directory® directory service is unavailable, and users cannot locate domain resources.

Managing services helps ensure that:

  • Computers can fill their roles as workstations, domain controllers, mail servers, and database servers. For example, if the mail service stops, a mail server cannot carry out its assigned function.

  • Users have access to resources both on the local computer and throughout the network.

  • Disruptions to the workplace are minimized. By monitoring services, you can be alerted the moment a problem occurs. For example, if the mail service stops, the service failure can be immediately detected and the mail service restarted, even before users begin to experience problems.

How Services Work

Services, like other applications, are run from executable files. For example, the DNS service runs in an instance of systemrootsystem32dns.exe, and Internet Information Services runs in an instance of systemroot system32inetsrvinetinfo.exe.

However, services are a special type of application. Unlike the executable files for most applications, the executable file for a service includes code that enables it to perform the special functions of a service and to communicate with the SCM.

To run an application as a service, the following components (some part of the operating system, some part of the individual service) must be present.

  • Service Control Manager. Communicates with services by forwarding commands that request a service to start, stop, pause, or resume. The SCM also monitors the status of each installed service and carries out specific actions if a service fails. (A failure is defined as any time a service stops without first sending the appropriate stop code to the SCM.)

    The SCM provides a unified means for configuring and managing services. In particular, the SCM does the following:

    • Maintains a database of all services.

    • Starts services either during system startup or on demand.

    • Enumerates installed services.

    • Maintains status information for all services.

    • Transmits commands (control requests) to services.

    • Locks and unlocks the service database.

  • Executable file for each service. Includes code that enables the service to respond to commands from the SCM and to communicate its status to the SCM. This requirement precludes most applications from running as a service; Notepad, for example, cannot run as a service because the developers did not include code that enables Notepad to communicate with the SCM.

  • Service control program. Allows a user to communicate with the SCM, which in turn communicates with an individual service. For example, to modify a service, you use a service control program (SCP) to send the modification commands to the SCM. In turn, the SCM relays those commands to the service, and the appropriate modifications are made.

    The Services snap-in to the Microsoft Management Console (MMC) is an example of a service control program, as are Sc.exe and the Net Start and Net Stop commands. Windows Management Instrumentation (WMI) scripts can also function as service control programs.

Running Services in Windows 2000

When a Windows 2000–based computer starts, the executable file for SCM (Services.exe) begins running before the logon dialog box appears. This allows autostart services to start before any user logs on.

After Services.exe starts, the SCM scans the contents of the registry subkey HKEY_LOCAL_MACHINESYSTEMCurrentControlSetServices. For each service listed in the registry, the SCM creates a corresponding entry in the services database. It establishes channels for communicating with service control programs and then starts the autostart services.

When a service starts, it initializes a minimum of two threads. One thread is used to communicate with the SCM and the other is used to respond to requests from client applications. For example, a Web server service creates a TCP “listen socket” and waits for inbound HTTP requests. When such a request is received, the thread becomes active, processes the request, and then suspends itself until the next request arrives. An application that initializes only a single thread cannot run as a service.

Monitoring Services

Many of the important operations that take place on a computer (especially on servers) run as services. This makes it imperative that you carefully monitor the services running on the computers in your network. Many services (such as DNS and DHCP) are so critical that a failure on a single server could adversely affect hundreds or even thousands of users, preventing them from logging on to the network or accessing network-based resources.

In general, there are three forms of service monitoring:

  • Monitoring service availability. Measures the percentage of time that a service is available.

    The exact definition of availability depends on the expectations for each service. If a database service must be available to users from 8:00 A.M. to 6:00 P.M. Monday through Friday, it can be considered completely available as long as the service is running during those times. If the service fails on a Saturday, or at 2:00 A.M. on Tuesday, this does not affect availability. It does, however, affect service reliability.

  • Monitoring service reliability. Measures how frequently a service fails and the amount of time required to restore a failed service to full functionality.

    Reliability is calculated by dividing the time the service is functioning by the total number of days in a year. For example, a service that experiences a total downtime of 2 days during the course of a year is 99.5 percent reliable (363 days of availability divided by 365 days in a year).

  • Monitoring service performance. Measures whether the service carries out its tasks in the expected manner (for example, whether the service handles the expected number of requests in the expected amount of time). Although it is possible to use WMI to monitor the performance of some operating system services in Windows 2000, a complete discussion of this type of monitoring is beyond the scope of this chapter.

Monitoring Service Availability

When you monitor a service for availability, you verify only that the service is running. If you need to know whether the service is running at peak efficiency, you need to use a more in-depth type of monitoring (such as performance monitoring). Although relatively simple, availability monitoring is extremely important; other questions, such as whether the service is performing at the expected level, are meaningless if the service is not even running.

Availability monitoring generally involves a probe that returns the status of a service. By saving the results of each probe to a database, you can calculate the availability of a service. For example, if you issue 100 probes and the service responds 99 times, the service has an availability of 99 percent.

Availability is often expressed as the amount of time during a year that the service was not available. For example, a service with an availability of 99 percent means the service was unavailable for a total of 3.7 days. This could be the result of one outage of 3.7 days or several outages that, combined, add up to 3.7 days.

To increase the availability of a service, you can do one of two things:

  • Increase the mean time between failures.

    Unfortunately, a service failure is often the result of a bug, either in the service or in the operating system. Unless you wrote the code for both the service and the operating system, it might be difficult for you to increase the mean time between failures.

  • Decrease the time it takes to restart the service.

    If you manually restart the service each time it fails, the service does not return to full functionality unless you are available to restart it. To increase availability, you can write a script that monitors the service state periodically and restarts the service automatically each time it fails.

Scripting Steps

Reporting on the availability of services can be done by:

  • Reporting the status of all services.

  • Reporting on services that are in a specific state (for example, services that are running or services that are not running).

Reporting the state of all services

Listing 15.1 contains a script that monitors service availability; it does this by querying the list of services installed on a computer and reporting the current state of each service. To carry out this task, the script must perform the following steps:

  1. Create a variable to specify the target computer name.

  2. Use a GetObject call to connect to the WMI namespace rootcimv2 on the target computer, and set the impersonation level to “impersonate.”

  3. Use the ExecQuery method to query the Win32_Service class. This returns a collection consisting of all the services installed on the computer.

  4. For each service in the collection, echo the service display name and the current state of each service.

Example 15.1. Monitoring Service Availability

1 strComputer = "."
2 Set objWMIService = GetObject("winmgmts:" & _
3     "{impersonationLevel=Impersonate}!\" & strComputer & "
ootcimv2")
4 Set colServices = objWMIService.ExecQuery("SELECT * FROM Win32_Service")
5 For Each objService in colServices
6     Wscript.Echo objService.DisplayName & " = " & objService.State
7 Next

Reporting on services that are not running

Listing 15.2 contains a script that monitors service availability; it does this by querying the list of services installed on a computer and reporting on all services that are not running. To carry out this task, the script must perform the following steps:

  1. Create a variable to specify the target computer name.

  2. Use a GetObject call to connect to the WMI namespace rootcimv2 on the target computer, and set the impersonation level to “impersonate.”

  3. Use the ExecQuery method to query the Win32_Service class. To restrict data retrieval to the set of services that are not running, the query includes a Where clause that limits the return to services with a state that does not equal “Running.”

    Unlike the WMI Query Language WQL statement in Listing 15.1 that retrieved all the properties for every service, the query in Listing 15.2 retrieves only the values of the display name and state properties. This minimizes the amount of data that must be sent across the network.

  4. For each service in the collection, echo the service display name and the service state.

Example 15.2. Monitoring Inactive Services

1 strComputer = "."
2 Set objWMIService = GetObject("winmgmts:" & _
3     "{impersonationLevel=Impersonate}!\" & strComputer & "
ootcimv2")
4 Set colStoppedServices = objWMIService.ExecQuery _
5  ("SELECT DisplayName,State FROM Win32_Service WHERE State <> 'Running'")
6 For Each objService in colStoppedServices
7     Wscript.Echo objService.DisplayName & " = " & objService.State
8 Next

An attempt to echo a Win32_Service property other than display name and state would result in an error based on the property list defined in the WQL query.

Monitoring Service Reliability

Reliability monitoring enables you to track the mean time between service failures. The mean time between failures tells you the amount of time you can expect a service to run before it fails. A service with a mean time between failures of 1000 hours is expected to run approximately 1000 hours before encountering problems.

Knowing the mean time between failure can help you prevent problems before they occur. For example, you might have a service that fails every 10 days because of a memory leak. Rather than wait for the service to fail (perhaps at a highly inconvenient time for your users), you might periodically schedule the service to stop and restart at a time that minimizes the impact on users, on other services, and on other parts of the computing environment.

Reliability monitoring can also tell you how long it takes for a service to be restored in the event that it does fail. For example, your reliability statistics might show that it takes 6 hours to fully restore a particular service. This information can help you plan routine service maintenance: If you need to upgrade or reconfigure the service, schedule this maintenance during a 6-hour block that minimally disrupts users.

You can create an event subscription that notifies you each time a service changes state (for example, goes from running to stopped). By saving these state changes to a database, you can calculate the mean time between failures and the time required to restore full functionality.

Scripting Steps

Listing 15.3 contains a script that uses a temporary event subscription to monitor changes in service state. To carry out this task, the script must perform the following steps:

  1. Create a variable to specify the target computer name.

  2. Use a GetObject call to connect to the WMI namespace rootcimv2 on the target computer, and set the impersonation level to “impersonate.”

  3. Use the ExecNotificationQuery method to register for notification each time there is an instance modification (each time an instance within the namespace changes in some way).

    Because the script monitors only changes to services, a Where clause is included to limit monitoring and data retrieval to instance modifications that involve the Win32_Service class.

  4. Create a loop that allows the script to run indefinitely.

    To stop monitoring, you need to terminate the script.

  5. Use the NextEvent method to retrieve each event when it occurs.

  6. Each time a service is modified in some way, the script checks to see whether the current state of the service differs from the previous state of the service. If it does not, the script resumes by waiting for the next event.

    Comparing the previous state with the current state enables you to identify whether the modification involved a change in service state. For example, a change in service state would be indicated by a service that was started when last monitored but is now stopped. If the previous and current states are the same, the modification did not involve service state but instead reflects a change to some other property of the service (such as changing the start mode from manual to autostart).

  7. If the service states differ, echo the display name of the service, its current state, and its previous state.

Example 15.3. Monitoring Changes in Service Status by Using a Temporary Event Subscriber

 1 strComputer = "."
 2 Set objWmiService = GetObject("winmgmts:" & _
 3     "{impersonationLevel=impersonate}!\" & strComputer & "
ootcimv2")
 4 Set objWmiEventSource = objWMIService.ExecNotificationQuery _
 5     ("SELECT * FROM __InstanceModificationEvent " & _
 6         "WITHIN 30 WHERE TargetInstance ISA 'Win32_Service'")
 7 Do
 8     Set objService = objWmiEventSource.NextEvent
 9     If objService.TargetInstance.State <> _
10        objService.PreviousInstance.State Then
11            Wscript.Echo objService.TargetInstance.DisplayName & _
12                " is " & objService.TargetInstance.State & _
13                    ". The service previously was " & _
14                        objService.PreviousInstance.State & "."
15     End If
16 Loop

Retrieving Service Properties

To effectively manage services in your organization, you need a detailed understanding of the properties of those services. You need to know which services are configured to automatically start whenever a computer is started, and which services need to be manually restarted. You also need to know the user account under which each service is running and whether the service runs as part of a service group.

You can use the WMI Win32_Service class to retrieve the properties of all the services installed on all the computers on your network. Some of the important service properties available through the Win32_Service class are listed in Table 15.1.

Table 15.1. Service Properties Available from the Win32_Service Class

Property

Description

AcceptPause

Indicates whether the service can be paused.

AcceptStop

Indicates whether the service can be stopped.

Description

Description of the service.

DesktopInteract

Indicates whether the service can create or communicate with windows on the desktop, and thus interact in some way with a user. Interactive services must run under the Local System account. Most services are not interactive; that is, they do not communicate with the user in any way.

DisplayName

Name of the service as viewed in the Services snap-in. Note that the display name and the service name (which is stored in the registry) are not always the same. For example, the DHCP Client service has the service name Dhcp but the display name DHCP Client.

ErrorControl

Action to be taken if a service fails during startup. Values and their associated actions include:

  • Ignore — User is not notified.

  • Normal — Message box displays notifying the user of the problem.

  • Severe — Computer restarts with last-known good configuration.

  • Critical — Computer attempts to restart with a good configuration. If the service fails to start a second time, startup fails.

ExitCode

Error code defining any problems that were encountered when starting or stopping a service. If a service is able to properly start or stop, this value is set to 0 (no error).

Name

Unique name of the service as stored in the registry.

PathName

Fully qualified path to the executable file responsible for implementing the service.

ProcessID

Process identifier assigned to the service during startup.

ServiceType

Valid service types include:

  • Kernel Driver

  • File System Driver

  • Adapter

  • Recognizer Driver

  • Own Process

  • Win32 Share Process

  • Interactive Process

Started

Indicates whether or not the service is started.

State

Valid states include:

  • Stopped

  • Start Pending

  • Stop Pending

  • Running

  • Continue Pending

  • Pause Pending

  • Paused

  • Unknown

SystemName

Name of the computer where the service is installed. This property is useful in scripts that retrieve service status from multiple computers.

The service properties available through Win32_Service are similar to the service properties available through the Services snap-in, as shown in Figure 15.1.

Win32_Service Properties

Figure 15.1. Win32_Service Properties

Enumerating Service Properties

In an enterprise setting, you often want services to be configured consistently. This facilitates service management, because you do not have to guess how a particular service is configured on a particular computer. Instead, you can be sure that the DHCP service is configured exactly the same on all your DHCP servers.

By using WMI, you can create a script that retrieves the properties of all the services on all your computers and saves that information to a file or database. You can then analyze the information in the file to determine which services, if any, need to be reconfigured in order to bring them in line with organization standards. In fact, the script can even carry out the reconfiguration for you.

Scripting Steps

Listing 15.4 contains a script that retrieves the properties of all the services on a computer and then saves those properties to a text file. To carry out this task, the script must perform the following steps:

  1. Create a constant ForAppending and set the value to 8. This constant is used when opening the text file where the service properties are written.

  2. Create an instance of the FileSystemObject.

  3. Open the text file C:ScriptsService_list.csv.

    If the file does not exist, it is created.

  4. Write a list of field headers to the file.

    Because the data is being saved in comma-separated-values format, field headers are separated by commas.

  5. Create a variable to specify the computer name.

  6. Use a GetObject call to connect to the WMI namespace rootcimv2, and set the impersonation level to “impersonate.”

  7. Use the ExecQuery method to query the Win32_Service class. This returns a collection consisting of all the services installed on the computer.

  8. For each service in the collection, write the service properties to the text file, separating the properties using commas. After all the properties for a service are written to the text file, write a carriage return (WriteLine) so that the properties for the next service are written on a new line in the file.

  9. Close the text file.

Example 15.4. Retrieving Service Properties

 1 Const ForAppending = 8
 2 Set objFSO = CreateObject("Scripting.FileSystemObject")
 3 Set objLogFile = objFSO.OpenTextFile("c:scriptsservice_list.csv", _
 4     ForAppending, True)
 5 objLogFile.Write _
 6     ("System Name,Service Name,Service Type,Service State,Exit " _
 7         & "Code,Process ID,Can Be Paused,Can Be Stopped,Caption," _
 8         & "Description,Can Interact with Desktop,Display Name,Error " _
 9         & "Control,Executable Path Name,Service Started," _
10         & "Start Mode,Account Name ")
11 objLogFile.Writeline
12 strComputer = "."
13 Set objWMIService = GetObject("winmgmts:" _
14     & "{impersonationLevel=impersonate}!\" & strComputer & "
ootcimv2")
15 Set colListOfServices = objWMIService.ExecQuery _
16         ("SELECT * FROM Win32_Service")
17 For Each objService in colListOfServices
18     objLogFile.Write(objService.SystemName) & ","
19     objLogFile.Write(objService.Name) & ","
20     objLogFile.Write(objService.ServiceType) & ","
21     objLogFile.Write(objService.State) & ","
22     objLogFile.Write(objService.ExitCode) & ","
23     objLogFile.Write(objService.ProcessID) & ","
24     objLogFile.Write(objService.AcceptPause) & ","
25     objLogFile.Write(objService.AcceptStop) & ","
26     objLogFile.Write(objService.Caption) & ","
27     objLogFile.Write(objService.Description) & ","
28     objLogFile.Write(objService.DesktopInteract) & ","
29     objLogFile.Write(objService.DisplayName) & ","
30     objLogFile.Write(objService.ErrorControl) & ","
31     objLogFile.Write(objService.PathName) & ","
32     objLogFile.Write(objService.Started) & ","
33     objLogFile.Write(objService.StartMode) & ","
34     objLogFile.Write(objService.StartName) & ","
35     objLogFile.Writeline
36 Next
37 objLogFile.Close

The resulting text file looks similar to the following when opened in a text editor such as Notepad:

System Name,Service Name,Service Type,Service State, Exit Code,Process ID,Can Be
Paused,Can Be Stopped,Caption,Description,Can Interact with Desktop,Display
Name,Error Control, Executable Path Name,Service Started,Start Mode,Account Name
Computer1,Alerter,Share
Process,Stopped,1077,0,False,False,Alerter,Alerter,False,Alerter,Normal,C:Window
sSystem32services.exe,False,Manual,LocalSystem,

As you can see, a text editor is not the optimal choice for reviewing the data. Because of this, you might want to create a script that uses the Tabular Data Control to display service properties within Internet Explorer. This allows you to display data using multiple fonts, multiple colors, or any other formatting style available through HTML. By using the Tabular Data Control, you can also display the data in table form without having to write the HTML code required to create a table for a Web page.

Note

Note

For more information about the Tabular Data Control, see “Creating Enterprise Scripts” in this book.

For example, you might create a Web page similar to the one shown in Figure 15.2.

Displaying Service Properties in a Web Page

Figure 15.2. Displaying Service Properties in a Web Page

Identifying the Services Running in a Process

Windows 2000 allows services to run in a shared process. This means a single executable file can be responsible for running multiple services, an approach that helps conserve system resources. In Windows 2000, each new process is given a minimum working set (memory size) of 800 kilobytes (KB). If you are running five services in separate executable files, that adds up to 4 megabytes (MB) of working set space. By running the five services from a single executable file, you might be able to limit memory use to the 800 KB minimum required for a single executable file.

This is possible if the memory required by each of the services totals 800 KB or less. For example, if each service requires 100 KB of memory, the total memory required by the five services running under a single executable file is 500 KB. In this case, the single executable file is then assigned the minimum 800 KB of memory. If you run each service as a separate executable file, each is assigned the minimum 800 KB, and the five services combined thus use 4 MB of memory.

The reduction in memory use represents the advantage of running multiple services in a single process. The disadvantage is the fact that this can complicate system administration in at least two ways:

  • When services share a process, the failure of any one service in that process results in the failure of all the services in the process.

    For example, if 10 services share a process and Service 1 fails, Services 2 through 10 also fail.

  • Determining which services are running in which process is difficult.

    This is especially true for the operating system application Svchost.exe. Svchost.exe is a generic host process for services running from dynamic-link libraries (DLLs). On a typical Windows 2000–based computer, multiple copies of Svchost.exe are running, each hosting a different set of services. For example, the Netsvcs group hosts four services: Netman, Rasman, Rasauto, and RemoteAccess.

Note

Note

During system startup, Svchost.exe checks the registry for the set of services to load together in a shared process. The Svchost.exe groups and the services hosted by each group are identified in the registry subkey HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindows NTCurrentVersionSvchost.

Knowing the services that run in a given process provides important information when you are troubleshooting computer problems. For example, if an instance of Svchost.exe appears to be leaking memory, you need to know which services are affected if you stop that process. If an instance of Svchost.exe stops running, you need to know which services to restart.

You can use WMI to determine which services are running in a given process. You can do this by retrieving the service path name (for example, C:WindowsSystem32Services.exe) and then enumerating all the services that share that path.

Scripting Steps

You can display services running in shared processes by doing the following:

  • Displaying the services running in a single shared process

  • Displaying the services running in all processes

Displaying the services running in a single shared process

Listing 15.5 contains a script that displays the services running in a single shared process, Services.exe. To carry out this task, the script must perform the following steps:

  1. Create a variable to specify the computer name.

  2. Use a GetObject call to connect to the WMI namespace rootcimv2, and set the impersonation level to “impersonate.”

  3. Use the ExecQuery method to query the Win32_Service class. This returns a collection consisting of all the services installed on the computer.

  4. For each service in the collection, check to see whether the PathName is C:WindowsSystem32Services.exe. (The path name indicates the executable file responsible for the service.) If True, echo the service display name.

Example 15.5. Displaying the Services Running in a Specified Process

1 strComputer = "."
2 Set objWMIService = GetObject("winmgmts:" _
3     & "{impersonationLevel=impersonate}!\" & strComputer & "
ootcimv2")
4 Set colListOfServices = objWMIService.ExecQuery("SELECT * FROM Win32_Service")
5 For Each objService in colListOfServices
6     If objService.PathName = "c:windowssystem32services.exe" Then
7         Wscript.Echo objService.DisplayName
8     End If
9 Next

Displaying the services running in all processes

The script shown in Listing 15.5 works well for services running in a standard executable file such as Services.exe. However, it is less useful for services running in Svchost.exe because several instances of Svchost.exe are probably running on your computer. A script that enumerates services by using the path name lists all the services running under all the instances of Svchost.exe as if they were all part of the same process.

However, you can use the ProcessID property to determine which services are running in a given process: retrieve the list of process IDs corresponding to active services, and then query each ID to determine the individual services sharing that process.

Listing 15.6 contains a script that displays the services running in all the processes on a computer. To carry out this task, the script must perform the following steps.

  1. Create a Dictionary object.

    This is used to temporarily store the unique process ID for each service.

  2. Create a variable to specify the computer name.

  3. Use a GetObject call to connect to the WMI namespace rootcimv2, and set the impersonation level to “impersonate.”

  4. Use the ExecQuery method to query the Win32_Service class. Because process IDs are valid only for services that are running, a Where clause is included to limit data retrieval to those services that are not stopped.

  5. For each service in the collection, retrieve the process ID and then check to see whether that ID has been stored in the Dictionary object.

    This generates a list of unique process IDs. If the ID is already in the Dictionary, the script simply proceeds; this ensures that no duplicate IDs are added to the Dictionary. If the process ID is not in the Dictionary, the script adds the ID.

  6. Retrieve the list of all Dictionary items (the unique process IDs).

  7. Create a loop that cycles through all the items in the Dictionary.

    The loop must begin at 0 because the first item in the Dictionary is assigned item 0. This also means that the loop must end at the number of items minus 1. For example, if the Dictionary has 5 items, the loop would be from 0 to 4 because the Dictionary would contain items 0, 1, 2, 3, and 4.

  8. For each Dictionary item (process ID), use a GetObject call to retrieve the list of services from the Win32_Service class that have that same process ID.

  9. Echo the process ID.

  10. Echo the display name for each service running in that process.

Example 15.6. Displaying the Services Running in All Processes on a Computer

 1 set objIdDictionary = CreateObject("Scripting.Dictionary")
 2 strComputer = "."
 3 Set objWMIService = GetObject("winmgmts:" _
 4     & "{impersonationLevel=impersonate}!\" & strComputer & "
ootcimv2")
 5 Set colServices = objWMIService.ExecQuery _
 6     ("Select * from Win32_Service Where State <> 'Stopped'")
 7 For Each objService in colServices
 8     If objIdDictionary.Exists(objService.ProcessID) Then
 9     Else
10         objIdDictionary.Add objService.ProcessID, objService.ProcessID
11     End If
12 Next
13 colProcessIDs = objIdDictionary.Items
14 For i = 0 to objIdDictionary.Count - 1
15     Set colServices = objWMIService.ExecQuery _
16         ("SELECT * FROM Win32_Service WHERE ProcessID = '" & _
17             colProcessIDs(i) & "'")
18     Wscript.Echo "Process ID: " & colProcessIDs(i)
19     For Each objService in colServices
20         Wscript.Echo VbTab & objService.DisplayName
21     Next
22 Next

When the script in Listing 15.6 is run using Cscript.exe, output similar to the following is displayed in the command window:

Process ID: 1332
        Windows Installer
Process ID: 228
        Net Logon
        IPSEC Policy Agent
        Security Accounts Manager
Process ID: 676
        Remote Registry Service

Changing Service State

You can use WMI to automatically start and stop services based on certain conditions. This enables you to create scripts that can query the services on multiple computers and then either start or stop specific services based on the criteria specified in the script.

For example, if you have a service with a known memory leak, you might need to periodically stop the service, wait a few minutes, and then restart it. Instead of doing this manually, you can schedule a WMI script to run at a specified interval (for example, every 72 hours) and stop and restart the service.

Alternatively, you can create a script that continuously monitors the memory usage of the process responsible for the service and then automatically stops and restarts the service if memory use exceeds a specified threshold.

The approach you take depends on the nature of the problem. If the service consistently runs for 72 hours without a problem, a scheduled script is the optimal solution: It can stop and restart the service before the memory leak occurs. If the memory leak occurs at more random intervals, continuous monitoring can enable you to identify the problem and fix it immediately, without having to wait until the next time the script is scheduled to run.

In addition to starting and stopping services, WMI can be used to pause services and to resume services that have been paused. The difference between a stopped service and a paused service depends on how the service is created. In general, a stopped service quits functioning and disconnects all users currently using the service. A paused service no longer accepts new connections but continues to support users already connected to the service.

When you control services by using WMI, an error code is returned indicating the success or failure of the operation. These error codes are shown in Table 15.2.

Table 15.2. Service Method Error Codes

Value

Description

0

The request was accepted.

1

The request is not supported.

2

The user did not have the necessary access.

3

The service cannot be stopped because other services that are running are dependent on it.

4

The requested control code is not valid, or it is unacceptable to the service.

5

The requested control code cannot be sent to the service because the state of the service (Win32_BaseService State property) is equal to 0, 1, or 2.

6

The service has not been started.

7

The service did not respond to the start request in a timely fashion.

8

Unknown failure when starting the service.

9

The directory path to the service executable file was not found.

10

The service is already running.

11

The database to add a new service is locked.

12

A dependency this service relies on has been removed from the system.

13

The service failed to find the service needed from a dependent service.

14

The service has been disabled from the system.

15

The service does not have the correct authentication to run on the system.

16

This service is being removed from the system.

17

The service has no execution thread.

18

The service has circular dependencies when it starts.

19

A service is running under the same name.

20

The service name has invalid characters.

21

Invalid parameters have been passed to the service.

22

The account under which this service runs is either invalid or lacks the permissions to run the service.

23

The service exists in the database of services available from the system.

24

The service is currently paused in the system.

Determining Which Services Can Be Stopped or Paused

Not all services can be stopped, and even fewer services can be paused. The capability of being stopped or paused must be coded into the service itself; without this code, the service does not respond to stop or pause requests. In particular, services that run as part of Services.exe (the process that is also responsible for running the SCM) can be neither stopped nor paused. If Services.exe could be stopped, that would stop the SCM. Without the SCM, all other services on the computer would be unable to function.

You can query the WMI AcceptStop and the AcceptPause properties to determine which services can be paused and which services can be stopped. For example, of the 88 services installed on a particular Windows 2000 domain controller, 54 were capable of being stopped, but only 17 were capable of being paused.

Scripting Steps

The scripts for determining which services can be stopped or which services can be paused are similar.

Determining which services can be stopped

Listing 15.7 contains a script that displays a list of services that can be stopped. To carry out this task, the script must perform the following steps.

  1. Create a variable to specify the computer name.

  2. Use a GetObject call to connect to the WMI namespace rootcimv2, and set the impersonation level to “impersonate.”

  3. Use the ExecQuery method to query the Win32_Service class. To restrict data retrieval to those services that can be stopped, a Where clause is included that returns only services for which the AcceptStop property is True.

  4. For each service in the collection, echo the service display name.

Example 15.7. Determining Which Services Can Be Stopped

1 strComputer = "."
2 Set objWMIService = GetObject("winmgmts:" _
3     & "{impersonationLevel=impersonate}!\" & strComputer & "
ootcimv2")
4 Set colServices = objWMIService.ExecQuery _
5     ("SELECT * FROM Win32_Service WHERE AcceptStop = True")
6 For Each objService in colServices
7     Wscript.Echo objService.DisplayName
8 Next

Determining which services can be paused

To determine which services can be paused, you can use a script similar to Listing 15.7 but substitute the AcceptPause property for the AcceptStop property in the Where clause, as shown in Listing 15.8. The output displayed in the command window lists the services that can be paused instead of those that can be stopped.

Example 15.8. Determining Which Services Can Be Paused

1 strComputer = "."
2 Set objWMIService = GetObject("winmgmts:" _
3     & "{impersonationLevel=impersonate}!\" & strComputer & "
ootcimv2")
4 Set colServices = objWMIService.ExecQuery _
5     ("SELECT * FROM Win32_Service WHERE AcceptPause = True")
6 For Each objService in colServices
7     Wscript.Echo objService.DisplayName
8 Next

Stopping or Pausing Services

After you have determined which services can be stopped or paused, you can use the StopService and PauseService methods to stop and pause services. The decision to stop a service rather than pause it, or vice versa, depends on several factors, including the following:

  • Is the service capable of being paused? If not, your only option is the stop the service.

  • Do you need to continue handling client requests for anyone already connected to the service? If so, pausing a service typically allows it to handle existing clients while denying access to new clients. By contrast, when you stop a service, all clients are immediately disconnected.

  • Do you need to reconfigure a service and have the changes take effect immediately? Although service properties can be changed while a service is paused, most of them do not take effect until the service is actually stopped and restarted.

The scripting code required to stop a service is almost identical to the code required to pause the service.

Scripting Steps

The scripts for stopping services and pausing services are similar.

Stopping services

Listing 15.9 contains a script that stops all the services running under a specified user account. To carry out this task, the script must perform the following steps:

  1. Create a variable to specify the computer name.

  2. Use a GetObject call to connect to the WMI namespace rootcimv2, and set the impersonation level to “impersonate.”

  3. Use the ExecQuery method to query the Win32_Service class. To limit data retrieval to a specific set of services, a Where clause is included restricting the collection to those services with the StartName .\Netsvc.

  4. For each service in the collection, stop the service using the StopService method.

    The return code for the StopService method is stored in the errReturnCode variable. In a production script, it is a good idea to check the value of that variable to ensure that the method was successfully applied.

Example 15.9. Stopping Services Running Under a Specified Account

1 strComputer = "."
2 Set objWMIService = GetObject("winmgmts:" _
3     & "{impersonationLevel=impersonate}!\" & strComputer & "
ootcimv2")
4 Set colServices = objWMIService.ExecQuery _
5     ("SELECT * FROM win32_Service WHERE StartName = '.\Netsvc'")
6 For Each objService in colServices
7     errReturnCode = objService.StopService()
8 Next

Pausing services

To pause services, use a script similar to Listing 15.9 but substitute the PauseService method for the StopService method, as shown in Listing 15.10.

Example 15.10. Pausing Services Running Under a Specified Account

1 strComputer = "."
2 Set objWMIService = GetObject("winmgmts:" _
3     & "{impersonationLevel=impersonate}!\" & strComputer & "
ootcimv2")
4 Set colProcesses = objWMIService.ExecQuery _
5     ("SELECT * FROM Win32_Service WHERE StartName = '.\Netsvc'")
6 For Each objService in colServices
7     errReturnCode = objService.PauseService()
8 Next

Starting or Resuming Services

Although there might appear to be no practical difference between a service that is stopped and a service that is paused, the two states appear differently to the SCM. A stopped service is a service that is not running and must go through the entire service start procedure. A paused service, however, is still running but has had its functioning is suspended. Because of this, a paused service does not need to go through the entire service start procedure but needs a different procedure to resume functioning.

You must use the proper method to start a service that has been stopped or to resume a service that has been paused. The Win32_Service methods StartService and ResumeService should be used in the following situations:

  • If a service is currently stopped, you must use the StartService method to restart it; ResumeService cannot start a service that is currently stopped.

  • If a service is paused, you must use ResumeService. If you use the StartService method on a paused service, you receive the message, “The service is already running.” However, the service remains paused until the resume service control code is sent to it.

Scripting Steps

The scripts for starting services that are stopped or resuming services that are paused are similar.

Starting automatic services that are stopped

Listing 15.11 contains a script that starts all automatic services that have been stopped. To carry out this task, the script must perform the following steps:

  1. Create a variable to specify the computer name.

  2. Use a GetObject call to connect to the WMI namespace rootcimv2, and set the impersonation level to “impersonate.”

  3. Use the ExecQuery method to query the Win32_Service class. To restrict data retrieval to a subset of services, the query includes a Where clause that limits data retrieval only to autostart services that are currently stopped.

  4. For each service in the collection, use the StartService method to start the stopped service.

Example 15.11. Starting Automatic Services That Are Stopped

1 strComputer = "."
2 Set objWMIService = GetObject("winmgmts:" _
3     & "{impersonationLevel=impersonate}!\" & strComputer & "
ootcimv2")
4 Set colListOfServices = objWMIService.ExecQuery _
5     ("SELECT * FROM Win32_Service WHERE State = 'Stopped' and StartMode = " _
6         & "'Auto'")
7 For Each objService in colListOfServices
8     objService.StartService()
9 Next

Resuming automatic services that are paused

To resume services that have been paused, use a script similar to Listing 15.12 but substitute the Paused property for the Stopped property in the Where clause and the ResumeService method for the StartService method, as shown in Listing 15.12.

Example 15.12. Resuming Automatic Services That Are Paused

1 strComputer = "."
2 Set objWMIService = GetObject("winmgmts:" _
3     & "{impersonationLevel=impersonate}!\" & strComputer & "
ootcimv2")
4 Set colListOfServices = objWMIService.ExecQuery _
5     ("SELECT * FROM Win32_Service WHERE State = 'Paused' and StartMode = " _
6         & "'Auto'")
7 For Each objService in colListOfServices
8     objService.ResumeService()
9 Next

Enumerating Dependent and Antecedent Services

Service management is complicated by service dependencies. For example, many Internet services — including the FTP Publishing Service, the World Wide Web Publishing Service, and the Simple Mail Transfer Protocol (SMTP) Service — are dependent upon the Internet Information Services (IIS) Admin Service. If the IIS Admin Service is not available, none of these dependent services can run.

Conversely, you cannot stop the IIS Admin Service without first stopping the dependent services. If this were allowed, stopping the IIS Admin Service would cause all its dependent services to fail because those services cannot run unless IIS Admin Service is also running. The IIS Admin Service is therefore antecedent to its dependent services.

The relationship between the dependent and antecedent roles is as follows:

  • Antecedent. If Service X is antecedent to Service Y, Service X must be running before you can run Service Y.

  • Dependent. If Service X is dependent on Service Y, Service Y must be running before you can run Service X.

Figure 15.3 maps these service roles to the Dependencies property page found in the Services snap-in.

Dependent and Antecedent Roles

Figure 15.3. Dependent and Antecedent Roles

To determine service dependencies, you need a script that specifies the service to be enumerated and its role. The Win32_DependentService class provides a means to identify the particular role played by that service.

Note

Note

Win32_DependentServices is an association class, a special type of class that specifies a relationship between two WMI objects. For more information about Association classes, see “WMI Scripting Primer” in this book.

For example, to list all the services that depend on the Remote Access Connection Manager, you need to create a script that:

  1. Retrieves all services associated with the Remote Access Connection Manager.

  2. Filters the list so it displays only those associated services for which the Remote Access Connection Manager is the antecedent.

Enumerating Dependent Services

As noted earlier, dependent services are those services that cannot start unless the antecedent service is running. Likewise, you cannot stop an antecedent service if one of its dependents is still running.

These dependencies can complicate service management, particularly if you are creating automated methods for starting and stopping services. Because of this, identifying service dependencies is an important part of service management.

Scripting Steps

You can enumerate dependent services by doing the following:

  • Enumerating dependent services for a single service. This approach is useful if you are creating an automated script for starting or stopping a particular service.

  • Enumerating dependent services for all the services on a computer. This approach is useful if you would like to view the dependency relationships for all your services.

Enumerating dependent services for a single service

Listing 15.13 contains a script that enumerates the dependent services for the Remote Access Connection Manager service. To carry out this task, the script must perform the following steps:

  1. Create a variable to specify the computer name.

  2. Use a GetObject call to connect to the WMI namespace rootcimv2, and set the impersonation level to “impersonate.”

  3. Use the ExecQuery method to query the Win32_Service class.

    You must use an Associators of query and specify the following information:

    • The instance of the service on which the query is performed (Win32_Service.Name = ’Rasman’).

    • The name of the Association class (AssocClass = Win32_DependentService). If the class name is not specified, the query returns all associated classes and their instances.

    • The role played by the Rasman service. In this case, Rasman is antecedent to the services to be returned by the query.

    This query returns a collection of all the services dependent on Remote Access Connection Manager.

  4. For each service in the collection, echo the service display name.

Example 15.13. Enumerating Dependent Services for a Single Service

1 strComputer = "."
2 Set objWMIService = GetObject("winmgmts:" _
3     & "{impersonationLevel=impersonate}!\" & strComputer & "
ootcimv2")
4 Set colServiceList = objWMIService.ExecQuery("ASSOCIATORS OF " _
5    & "{Win32_Service.Name='rasman'} WHERE " _
6         & "AssocClass=Win32_DependentService " & "Role=Antecedent" )
7 For Each objService in colServiceList
8     Wscript.Echo objService.DisplayName
9 Next

Enumerating dependent services for all the services on a computer

Listing 15.14 contains a script that enumerates the dependents for all the services installed on a computer. To carry out this task, the script must perform the following steps:

  1. Create a constant ForAppending, and set the value to 8.

    This constant is used when opening the text file where the service dependency information is written.

  2. Create an instance of the FileSystemObject.

  3. Open the text file C:ScriptsService_dependencies.csv.

    If the file does not exist, it is created automatically.

  4. Write the file header “Service Dependencies” to the text file.

  5. Create a variable to specify the computer name.

  6. Use a GetObject call to connect to the WMI namespace rootcimv2, and set the impersonation level to “impersonate.”

  7. Use the ExecQuery method to query the Win32_Service class. This returns a collection consisting of all the services installed on the computer.

  8. For each service in the collection, use the ExecQuery method to retrieve the list of dependent services.

    This query must use an Associators of query and specify the following information:

    • The instance of the service on which the query is performed. In this script, the service name is stored in the variable strServiceRegistryName.

    • The name of the Association class (AssocClass = Win32_DependentService). If the class name is not specified, the query returns all associated classes and their instances.

    • The role played by the individual service. In this case, the service is Antecedent to the services to be returned by the query.

  9. Use the Count method to check the number of dependent services returned by the query.

    If Count = 0, the service has no dependents, and the display name of the service and the value “None” are written to the text file.

    If Count > 0, the display name of the service and the display name of its dependents are written to the text file.

  10. Close the text file.

Example 15.14. Enumerating Dependent Services for All the Services on a Computer

 1 Const ForAppending = 8
 2 Set objFSO = CreateObject("Scripting.FileSystemObject")
 3 Set objLogFile = _
 4      objFSO.OpenTextFile("c:scriptsservice_dependencies.csv", _
 5          ForAppending, True)
 6 objLogFile.Write("Service Dependencies")
 7 objLogFile.Writeline
 8 strComputer = "."
 9 Set objWMIService = GetObject("winmgmts:" _
10     & "{impersonationLevel=impersonate}!\" & strComputer & "
ootcimv2")
11 Set colListOfServiceS = objWMIService.ExecQuery _
12     ("SELECT * FROM Win32_Service")
13 For Each objService in colListofServices
14     objServiceRegistryName = objService.Name
15     objServiceDisplayName = objService.DisplayName
16     Set colServiceList = GetObject("winmgmts:").ExecQuery _
17         ("ASSOCIATORS OF {Win32_service.Name='" _
18             & objServiceRegistryName & _
19                 "'} WHERE AssocClass=Win32_DependentService Role=Antecedent" )
20     If colServiceList.Count = 0 then
21         objLogFile.Write(objServiceDisplayName) & ", None"
22         objLogFile.Writeline
23     Else
24         For Each objDependentService in colServiceList
25             objLogFile.Write(objServiceDisplayName) & ","
26             objLogFile.Write(objDependentService.DisplayName)
27             objLogFile.Writeline
28         Next
29     End If
30 Next
31 objLogFile.Close

Enumerating Antecedent Services

Antecedent services are services that must be running before dependent services can start. This information is extremely valuable for anyone writing scripts that automatically start services. By enumerating the antecedent services, you can ensure that all required services are already running before you attempt to start a dependent service. Alternatively, by enumerating antecedent services, you can also determine which dependent services you might not want to start. When you start a dependent service, its antecedent services automatically start; in some cases, you might prefer to leave those services inactive.

Antecedent services can be enumerated by using a script similar to the one used to enumerate dependent services. If you set the service role to Dependent in your WMI script, it lists the antecedents for a particular service.

Scripting Steps

Listing 15.15 contains a script that enumerates the antecedents of the Fax service. To carry out this task, the script must perform the following steps:

  1. Create a variable to specify the computer name.

  2. Use a GetObject call to connect to the WMI namespace rootcimv2, and set the impersonation level to “impersonate.”

  3. Use the ExecQuery method to query the Win32_Service class.

    This query must use an Associators of query and specify the following information:

    • The instance of the service on which the query is performed (Win32_Service.Name = ’Fax’).

    • The name of the Association class (AssocClass = Win32_DependentService). If the class name were not specified, the query would return all associated classes and their instances.

    • The role played by the Fax service. In this case, Fax is dependent on the services the query returns.

    This returns a collection consisting of all the services for which the Fax service is a dependent.

  4. For each service in the collection, echo the service display name.

Example 15.15. Enumerating Antecedent Services

1 strComputer = "."
2 Set objWMIService = GetObject("winmgmts:" _
3     & "{impersonationLevel=impersonate}!\" & strComputer & "
ootcimv2")
4 Set colServiceList = objWMIService.ExecQuery _
5     ("ASSOCIATORS OF {Win32_Service.Name='fax'} WHERE " _
6         & "AssocClass=Win32_DependentService Role=Dependent" )
7 For Each objService in colServiceList
8     Wscript.Echo objService.DisplayName
9 Next

Stopping and Starting Dependent Services

Service dependencies are especially important when you try to stop services. To stop an antecedent service, you must first stop the dependent services. For example, if you attempt to stop the IIS Admin Service without first stopping dependent services such as FTP, SMTP, and World Wide Web Publishing Service, you receive an error message, and all the services continue to run.

Dependencies also affect the order in which services start. To start a dependent service, the antecedent service must start first. If you are starting a dependent service such as FTP, the antecedent service (IIS Admin) automatically starts first. Only after IIS Admin starts does the FTP service start.

However, starting the antecedent service first does not cause a dependent service to start. If you start the IIS Admin Service, that service itself starts, but its dependent services (such as FTP) do not automatically start at the same time.

In other words, stopping and restarting an antecedent service sometimes involves stopping and restarting a number of dependent services. You could do this manually — coding all the dependencies within your script. The difficulty with this approach is twofold. First, you must determine all the dependencies and manually add them to the script. Second, if those dependencies ever change (because of a service or operating system upgrade), your script must be modified to reflect these changes.

Alternatively, you can use WMI to enumerate the dependencies, and then stop and restart the appropriate services in the appropriate order. By using WMI, you avoid the problems of hardcoding service dependencies: you do not have to determine these dependencies beforehand, and you do not have to be concerned that changes to the operating system affect these dependencies. Instead, WMI determines the appropriate dependencies each time the script runs.

Scripting Steps

The scripts for stopping and starting dependent services perform similar steps but in the opposite order.

Stopping dependent services

Listing 15.16 contains a script that stops the IIS Admin Service and all its dependents. To carry out this task, the script must perform the following steps:

  1. Create a variable to specify the computer name.

  2. Use a GetObject call to connect to the WMI namespace rootcimv2, and set the impersonation level to “impersonate.”

  3. Use the ExecQuery method to query the Win32_Service class.

    This query must use an Associators of query and specify the following information:

    • The instance of the service on which the query is performed (Win32_Service.Name = ’IISAdmin’).

    • The name of the Association class (AssocClass = Win32_DependentService). If the class name is not specified, the query returns all associated classes and their instances.

    • The role played by the IISAdmin Service. In this case, IISAdmin is Antecedent to the services to be returned by the query.

    The query returns a collection consisting of all the services dependent on the IIS Admin Service.

  4. For each service in the collection, use the StopService method to stop the service.

  5. After a stop control has been sent to each dependent service, pause for 60 seconds (60,000 milliseconds) to give the SCM time to stop each service.

  6. Use a the ExecQuery method to retrieve the instance of the IISAdmin Service.

  7. Use the StopService method to stop the IISAdmin Service.

Example 15.16. Stopping a Service and Its Dependents

 1 strComputer = "."
 2 Set objWMIService = GetObject("winmgmts:" _
 3     & "{impersonationLevel=impersonate}!\" & strComputer & "
ootcimv2")
 4 Set colServiceList = objWMIService.ExecQuery _
 5     ("ASSOCIATORS OF {Win32_Service.Name='iisadmin'} WHERE " _
 6         & "AssocClass=Win32_DependentService Role=Antecedent" )
 7 For Each objService in colServiceList
 8     errReturn = objService.StopService()
 9 Next
10 Wscript.Sleep 60000
11 Set colServiceList = objWMIService.ExecQuery _
12     ("SELECT * FROM Win32_Service WHERE Name='iisadmin'")
13 For Each objService in colServiceList
14     errReturn = objService.StopService()
15 Next

Starting dependent services

To start a service and all its dependents, simply reverse the process for stopping a service and its dependents: start the antecedent service, obtain a list of dependent services, and start each one. Listing 15.17 contains a script that starts the IIS Admin Service and all its dependents.

Example 15.17. Starting Dependent Services

 1 strComputer = "."
 2 Set objWMIService = GetObject("winmgmts:" _
 3     & "{impersonationLevel=impersonate}!\" & strComputer & "
ootcimv2")
 4 Set colServiceList = objWMIService.ExecQuery _
 5     ("SELECT * FROM Win32_Service WHERE Name='iisadmin'")
 6 For Each objService in colServiceList
 7     errReturn = objService.StartService()
 8 Next
 9 Wscript.Sleep 60000
10 Set colServiceList = objWMIService.ExecQuery _
11     ("ASSOCIATORS OF {Win32_Service.Name='iisadmin'} WHERE " _
12         & "AssocClass=Win32_DependentService Role=Antecedent" )
13 For Each objService in colServiceList
14     objService.StartService()
15 Next

Configuring Services

Although services generally require little hands-on administration, there will be times when you need to reconfigure a service. For example, you might want to configure a service so that it starts automatically each time the computer starts. Alternatively, you might want to change the display name of a service, making the service and its function more obvious to administrators working in the Services snap-in. Management tasks such as these can be carried out using WMI.

The WMI Win32_Service Change method is especially useful for changing selected service properties on multiple computers throughout the enterprise. The Change method accepts 11 parameters, each representing a service property that can be modified. When passing these parameters, it is important to maintain the correct order. For example, PathName is the second parameter in the set of 11 parameters. To change this property, the new value for PathName must be the second item in the parameters passed to the Change method.

This means you must pass a value of some kind for DisplayName, the first item in the parameter list. If you do not want to change the current value of DisplayName, you can insert a comma for that value. For example, the following code changes just the PathName:

errReturn = strService.Change( , "c:windowsservices
ew_name.exe")

Because these single commas might be confusing to someone editing your script, you can instead insert the keyword Null or use a constant with the value Null. For example, you might create a constant named SAME_DISPLAYNAME, set it to Null, and then insert the constant into the parameter string as follows:

Const SAME_DISPLAYNAME = Null
errReturn = strService.Change _
    (SAME_DISPLAYNAME , "c:windowsservices
ew_name.exe")

This makes it clearer that you are keeping the same display name and changing only the service path.

Table 15.3 provides a complete list of the service properties exposed to the Change method, as well as their ordinal position. The items that can be changed by using WMI represent only a subset of a service’s full property set and do not include some properties, such as service description.

Table 15.3. Service Properties Exposed to the WMI Change Method

Position

Property

1

DisplayName –Name of the service as displayed in the Services snap-in.

2

PathName –Full path to the service’s executable file.

3

ServiceType —Type of service. Most services are type 4: services that run in their own process.

Valid services types include the following:

  • 1 —Kernel driver

  • 2 —File system driver

  • 3 —Adapter

  • 4 —Own process

  • 5 —Win32 shared process

4

ErrorControl —Action to be taken if a service fails during startup.

Valid error controls include the following:

  • 0 —Ignore

  • 1 —Normal

  • 2 —Severe

  • 3 —Critical

5

StartMode —Method used to start the service. Most service start modes are either Auto or Manual.

Valid start modes include the following:

  • Boot. This start mode is reserved for device drivers.

  • System. This start mode is reserved for device drivers.

  • Auto. Services automatically start each time the computer starts.

  • Manual. Services must be explicitly started, either by using the Services snap-in or by using a script.

  • Disabled. Services cannot be started until the start mode has been changed to either Auto or Manual.

6

DesktopInteract —Indicates whether the service can create or communicate with windows on the desktop. DesktopInteract can be set to either TRUE or FALSE. Interactive services must run under the LocalSystem account.

7

StartName —Account name under which the service runs. If not specified, the service runs under the LocalSystem account.

8

StartPassword —Password for the account name specified by StartName. Use an empty string (“”) to specify no password and NULL to indicate that you are not changing the current password.

9

LoadOrderGroup —Load-ordered group to which the service belongs. When you start services as part of the computer start process, those that belong to load-ordered groups start first, services that belong to groups (but not load-ordered groups) start next, and services that do not belong to groups of any kind start last.

10

LoadOrderGroupDependencies —Set of load-ordered groups that must be running before this service can start.

11

ServiceDependencies —Services that must be running before this service can start.

Configuring Service Start Options

One key aspect of service management is determining when or if services run. The three options for configuring service start options are shown in Table 15.4.

Table 15.4. Service Start Options

Option

Description

Auto

Service is started automatically by the SCM during system startup. Autostart services start before a user logs on to the computer and run even if no user logs on to the computer.

Manual

Service is started by the SCM when a process calls the StartService method. Although manual services must be specifically started by a user (or by a script), they continue to run even if the user logs off.

Manual services are often referred to as on-demand services.

Disabled

Service that can no longer be started. To start a disabled service, you must first change the startup option to either Auto or Manual.

Start options are often changed in order to accomplish one of the following:

  • Ensure that a service automatically restarts when a computer restarts.

    Services set to Manual startup can be started only if a user logs on and either manually starts the service or runs a script to start the service. If the power fails, manual services do not automatically restart when the computer restarts.

  • Prevent Power Users from starting a service.

    Although you can use Group Policy to prevent Power Users from starting a service, you can also set the service start mode to Disabled. A disabled service cannot start unless it is reconfigured as either Manual or Auto. Because Power Users do not have the right to change service configurations, disabling a service also prevents these users from starting that service.

Scripting Steps

Listing 15.18 contains a script that disables all the on-demand services on a computer. To carry out this task, the script must perform the following steps:

  1. Create a variable to specify the computer name.

  2. Use a GetObject call to connect to the WMI namespace rootcimv2, and set the impersonation level to “impersonate.”

  3. Use the ExecQuery method to query the Win32_Service class. To restrict data retrieval to a subset of services, a Where clause is used to limit data retrieval only to those services for which the StartMode property is equal to “Manual.”

  4. For each service in the collection, use the Change method to set StartMode to “Disabled.”

    With the Change method, the StartMode property must be the fifth parameter in the list of arguments supplied. Because of this, four empty arguments (represented by the four commas) must precede the new value for StartMode.

    If the service is currently running, StartMode is changed to Disabled. However, the service continues to run until it is stopped. If you want to immediately disable a service, your script can first stop the service and then change StartMode to Disabled. That both stops the service and prevents it from being restarted.

Example 15.18. Configuring Service Start Options

1 strComputer = "."
2 Set objWMIService = GetObject("winmgmts:" _
3     & "{impersonationLevel=impersonate}!\" & strComputer & "
ootcimv2")
4 Set colServiceList = objWMIService.ExecQuery _
5     ("Select * from Win32_Service where StartMode = 'Manual'")
6 For Each objService in colServiceList
7     errReturnCode = objService.Change( , , , , "Disabled")
8 Next

Configuring Error Control Codes for Autostart Services

When a computer starts, all the autostart services also start. On occasion, one of these services might fail to start along with the computer. When a service fails during system startup, the computer takes action based on the value of the service error control code. These codes and the corresponding actions are listed in Table 15.5.

Table 15.5. Service Error Control Codes

Error code

Value

Description

Ignore

0

Startup continues. No notification is given to the user that the service failed.

Normal

1

Startup continues. Before the user logs on, the user receives the notification, “At least one service or device failed during startup.”

Severe

2

Computer attempts to restart with last-known good configuration. If the service fails again, startup continues and notification is given to the user.

Critical

3

Computer attempts to restart with last-known good configuration. If the service fails again, startup stops.

Also listed in the table are the values that must be used to configure the error control code. Admittedly, this can be a bit confusing. Although error control codes are reported as strings (such as Ignore), you must configure these codes using integers. For example, this line of code sets an error control code to Critical:

errReturn = objService.Change( , , , 3)

Regardless of the error control code, all startup failures are recorded in the System Event Log.

Most Windows 2000 services are installed using the Normal error control code. A few of the exceptions, which are installed using the Ignore error code, include:

  • File Replication Service

  • Smart Card

  • Secondary Logon

  • WMI

For the services installed using the Ignore error code, no notification is given to the user that the service has failed. If you prefer on-screen notification that a service could not start, you can use WMI to change the error control code.

Error control codes apply only to computer startup; error control codes are not used if you stop and then attempt to restart a service after the computer is running.

Scripting Steps

Listing 15.19 contains a script that changes the error control code to Normal for all services that currently have the error control code Ignore. To carry out this task, the script must perform the following steps:

  1. Create a variable to specify the computer name.

  2. Use a GetObject call to connect to the WMI namespace rootcimv2, and set the impersonation level to “impersonate.”

  3. Use the ExecQuery method to query the Win32_Service class. To restrict data retrieval to a subset of services, a Where clause is used to limit data retrieval only to those services for which the ErrorControl property is equal to Manual.

  4. For each service in the collection, use the Change method set ErrorControl to Normal.

    With the Change method, the ErrorControl property must be the fourth parameter in the list of arguments supplied. Because of this, three empty arguments (represented by the three commas) must precede the new value for ErrorControl.

Example 15.19. Configuring Service Error Control Codes

1 Const NORMAL = 1
2 strComputer = "."
3 Set objWMIService = GetObject("winmgmts:" _
4     & "{impersonationLevel=impersonate}!\" & strComputer & "
ootcimv2")
5 Set colServiceList = objWMIService.ExecQuery _
6     ("SELECT * FROM Win32_Service WHERE ErrorControl = 'Ignore'")
7 For Each objService in colServiceList
8     errReturn = objService.Change( , , , NORMAL)
9 Next

Managing Service Accounts and Service Account Passwords

Services must run under a user account. When the SCM starts a service, it logs on to the account that is associated with the service. If the logon is successful, the service process is given an access token. This token identifies the service in any subsequent interactions with securable objects (objects that have a security descriptor attached to them). For example, if the service attempts to access a remote computer, the token is used for authentication. If authentication fails, the service is denied access to the resource.

In Windows 2000, most operating system services run under the LocalSystem account, a special account that is granted all possible privileges to the local computer on which it resides. (Even administrators do not have all privileges granted to them by default.) LocalSystem is often used as a service account because it already has all privileges and does not require special privileges to ensure that the service runs.

Although the LocalSystem account has full access to the local computer, it is never validated by Active Directory. In fact, no password is associated with the LocalSystem account, and you cannot interactively log on as LocalSystem. Because it has no domain credentials, the LocalSystem account has limited access to resources outside the local computer.

Although most services use the LocalSystem account by default, it is recommended that you not run services under this account, especially on domain controllers.

Caution

Caution

The LocalSystem account has access to all resources on the local computer. If the local computer is a domain controller, this means the account has access to everything in Active Directory as well. If someone compromises a service running under LocalSystem, that person then has full Administrator access to every resource on the computer. To avoid this potential security problem, run services under an account other than LocalSystem whenever possible.

Configuring Service Accounts

On occasion, you might need to change the account under which a given service runs. For example, you might run a service under an administrative account. Because this can create a security vulnerability, you might switch the service to an account with fewer privileges. Alternatively, you might have services running under an account that is about to be deleted, or you might want to ensure that, on all your servers, certain services run under certain accounts.

You can use the Win32_Service Change method to configure services to run under a specified user account. When selecting an account, keep in mind the following:

  • The account being used as a service account must have the right to log on as a service. This right can be granted by using Group Policy.

  • The account being used as a service account should not be a member of a local, domain, or enterprise Administrators group.

  • Each instance of a service should run under a unique user account. This provides additional security, and enables the auditing of individual service instances.

  • If the service is interactive, then the service must run under the LocalSystem account.

    LocalSystem is required because only one window station (WinSta0) can be visible and interactive at a time. If a service runs under an account other than LocalSystem, it runs in the Service-0×03e7$Default window station, which is an invisible window. Services running in this window station cannot receive input or display output.

Win32_Service properties and methods related to service accounts are shown in Figure 15.4.

Win32_Service Account Properties and Methods

Figure 15.4. Win32_Service Account Properties and Methods

Scripting Steps

Listing 15.20 contains a script that changes the service account for services from running under a specified user account to LocalSystem. To carry out this task, the script must perform the following steps:

  1. Create a variable to specify the computer name.

  2. Use a GetObject call to connect to the WMI namespace rootcimv2, and set the impersonation level to “impersonate.”

  3. Use the ExecQuery method to query the Win32_Service class.

    To limit data retrieval to a specific set of services, a Where clause is included that restricts the collection to those services with a ServiceName of .Netsvc.

    The period and backslash (.) preceding the account name indicate that the account is a local user account (a second backslash is required whenever a single backslash is used in a query). If the service is running under a domain account, the period is replaced by the domain name (for example, fabrikam\Netsvc).

  4. For each service in the collection, use the Change method to switch the account to LocalSystem.

    Because StartName must be the seventh parameter passed to the Change method, the new account name is preceded by six empty arguments (represented by the six commas). Following StartName, an empty string (“”) is used to indicate that the LocalSystem account does not use a password. If LocalSystem did use a password, the password for the account would be listed following StartName.

Example 15.20. Switching Service Accounts to LocalSystem

1 strComputer = "."
2 Set objWMIService = GetObject("winmgmts:" _
3     & "{impersonationLevel=impersonate}!\" & strComputer & "
ootcimv2")
4 Set colServiceList = objWMIService.ExecQuery _
5     ("SELECT * FROM Win32_Service WHERE StartName = '.\NetSvc'")
6 For Each objService in colServices
7     errServiceChange = objService.Change _
8         ( , , , , , , ".LocalSystem" , "")
9 Next

Configuring Service Account Passwords

When you assign an account to a service, the SCM requires the correct password for that account before it makes the assignment. If you supply an incorrect password, the SCM rejects the account. If you configure a service account using the LocalSystem, LocalService, or NetworkService account, you do not need to supply an account password because these accounts do not have passwords.

The SCM stores the account password in the services database. After the password is assigned, however, the SCM does not ensure that the password stored in the services database and the password assigned to the user account in Active Directory continue to match. Consequently, a situation similar to the following could occur:

  1. You configure a service to run under a particular user account.

  2. The service starts up under that account by using the current account password.

  3. You change the password for the user account.

  4. The service continues to run. However, if the service stops, you cannot restart it because the SCM continues to use the old, invalid password. Changing the password in Active Directory does not change the password stored in the services database.

If you run services under regular user accounts, you need to update those service passwords each time the user account password changes. This can be particularly time-consuming if you are not sure which services are running under that account or which computers have services running under that account.

Fortunately, you can use WMI to check the service accounts on all your computers and, if necessary, change the service account password.

Scripting Steps

Listing 15.21 contains a script that changes the service account password for all scripts running under Netsvc. To carry out this task, the script must perform the following steps:

  1. Create a variable to specify the computer name.

  2. Use a GetObject call to connect to the WMI namespace rootcimv2, and set the impersonation level to “impersonate.”

  3. Use the ExecQuery method to query the Win32_Service class. To limit data retrieval to a specific set of services, a Where clause is included that restricts the collection to those services with the ServiceName .\Netsvc (a second backslash is required whenever a single backslash is used in a query).

  4. For each service in the collection, use the Change method to change the password to “password”.

    When using the Change method, the password must be the eighth parameter passed to the method. Because of this, the password is preceded by seven empty arguments (represented by the seven commas). Unless you are using a variable, the password must be enclosed in quotation marks.

Example 15.21. Changing a Service Account Password

1 strComputer = "."
2 Set objWMIService = GetObject("winmgmts:" _
3     & "{impersonationLevel=impersonate}!\" & strComputer & "
ootcimv2")
4 Set colServiceList = objWMIService.ExecQuery _
5     ("SELECT * FROM Win32_Service WHERE StartName = '.\NetSvc'")
6 For Each objservice in colServiceList
7     errReturn = objService.Change( , , , , , , , "password")
8 Next

Installing and Removing Services

WMI provides methods that allow you to install and remove services programmatically. These methods work best for installing and removing third-party services rather than services that are part of the operating system. It is difficult (or impossible) to install operating system services using WMI, if for no other reason than the fact that you are unlikely to have a detailed list of the executable files and the service accounts required for each service.

And while it is possible to remove operating system services using WMI, this is not recommended. This is because the service removal will be incomplete: for example, the service will no longer function, but it will still be listed as Installed in Windows Components. If you need a command-line method for installing or removing operating system services, you can use Sysocmgr.exe instead; however, Sysocmgr.exe is designed to work on the local computer and not on remote computers.

Although it is somewhat limited in scope, WMI can be useful in the following situations:

  • You need to install an in-house service on multiple computers.

  • You need to replace a third-party service previously used in your organization.

    For example, your script could check for the presence of this service on each computer in your organization and, if found, stop and remove the service. The script could then install the replacement service.

Installing Services

Services are generally installed in one of two ways: either as a part of the operating system installation or by using an installation program provided by the service developer. However, some services, particularly those created in-house, might not have an installation program. In those instances, you can use the Win32_Service Create method to programmatically install services.

Despite the name, the Create method does not actually create a service; it merely installs an existing service. To use this command, you need to copy the service executable file to a computer and then use Win32_Service Create to install the service.

The Create method is similar to the Change method. In both cases, the properties of the service are passed as parameters to the method. As with the parameters used with the Change method, the order in which these parameters are passed is very important. You must pass the parameters in the order shown in Table 15.6.

Table 15.6. Service Properties Exposed to the WMI Create Method

Position

Property

1

Name —Name of the service as stored in the registry.

2

DisplayName —Name of the service as displayed in the Services snap-in.

3

PathName —Full path to the service’s executable file.

4

ServiceType —Type of service.

5

ErrorControl —Action to be taken should a service fail during startup.

6

StartMode —Method used to start the service.

7

DesktopInteract —Indicates whether the service can create or communicate with windows on the desktop.

8

StartName —Account name under which the service runs.

9

StartPassword —Password for the account name specified by StartName.

10

LoadOrderGroup —Load-ordered group to which the service belongs.

11

LoadOrderGroupDependencies —Set of load-ordered groups that must be running before this service can start.

12

ServiceDependencies —Services that must be running before this service can start.

A complete list of the service properties exposed to the Win32_Service Create method are shown in Table 15.6. For more information about each property, see “Retrieving Service Properties” earlier in this chapter.

Note

Note

The ordinal positions of the service properties exposed to the Create method differ slightly from those exposed to the Change method. Make sure you use the values shown in Table 15.6 when installing a service.

Scripting Steps

Listing 15.22 contains a script that installs a service named DbService. To carry out this task, the script must perform the following steps:

  1. Create a constant named OWN_PROCESS, and set the value to 16.

    This constant is used to specify that the service must run in its own process.

  2. Create a constant named NOT_INTERACTIVE, and set the value to True.

    This constant indicates that the service does not interact with the desktop.

  3. Create a variable to specify the computer name.

  4. Use a GetObject call to connect to the WMI namespace rootcimv2, and set the impersonation level to “impersonate.”

  5. Get an instance of the Win32_BaseService class, the parent class for Windows services.

  6. Use the Create method to create (install) a new service.

    To create the service, include the following parameters:

    • DbService. Name of the service as it is stored in the registry.

    • Personnel Database Service. “Friendly” name of the service, as viewed in the Services snap-in.

    • C:WindowsSystem32Db.exe. Local path to the executable file for the service.

    • OWN_PROCESS. Service runs in its own process.

    • Normal. Error control code for the service.

    • Auto. Service startup type.

    • NOT_INTERACTIVE. Service does not interact with the desktop.

    • .LocalService. Account name under which the service runs. (Two backslashes are not required in the name, because the name, and its single backslash, is not being used in a query.)

      You must specify a valid account name when installing a service. If the SCM cannot locate the specified account, installation fails.

    • “”. Empty string indicating that the LocalService account does not use a password.

      If you specify an incorrect password, installation continues because the SCM does not verify passwords when installing a service. However, if the password is incorrect, the service fails when you try to start it.

Example 15.22. Installing a Service

1 Const OWN_PROCESS = 16
2 Const NOT_INTERACTIVE = True
3 strComputer = "."
4 Set objWMIService = GetObject("winmgmts:" _
5     & "{impersonationLevel=impersonate}!\" & strComputer & "
ootcimv2")
6 Set objService = objWMIService.Get("Win32_BaseService")
7 errReturn = objService.Create ("DbService", "Personnel Database", _
8  "c:windowssystem32db.exe", OWN_PROCESS ,2 ,"Automatic" , _
9       NOT_INTERACTIVE ,".LocalSystem" ,"")

Removing Services

As your organization changes, you might decide to remove certain services from certain computers. In-house and third-party services can be removed by using WMI, while operating system services can be removed by using Sysocmgr.exe.

When preparing to remove services, keep the following information in mind:

  • Services must be stopped before you remove them.

    If the service is running when you issue the delete command, the service is marked for deletion, but it continues to run until it stops and all open handles are closed. If the service is never stopped, that service will never be deleted.

  • Removing a service does not remove the service’s executable file.

    Removing a service by using WMI deletes the related registry entries under HKEY_LOCAL_MACHINESYSTEMCurrentControlSetServices. As a result, the service is no longer installed and is not available through the Services snap-in. However, WMI does not delete the executable file, meaning you could easily reinstall the service. To delete the executable file, you must retrieve the path name and then delete the file.

  • Removing a base Windows 2000 service (for example, DHCP) by using WMI deletes the registry entries for that service but does not remove the shortcut from the Administrative Tools menu or remove the service from the Windows Components Wizard. This can confuse anyone trying to determine how the computer has been configured.

    For example, if you remove the DHCP service by using a WMI script, the DHCP service is no longer listed in the Services snap-in. However, a nonfunctioning shortcut to the DHCP console remains in the Administrative Tools menu, and if you start the Windows Component Wizard, it indicates that the DHCP service is installed.

    Because of this, you should always use Sysocmgr.exe to programmatically remove Windows 2000 services.

Scripting Steps

Listing 15.23 contains a script that removes a service named DbService. To carry out this task, the script must perform the following steps:

  1. Create a variable to specify the computer name.

  2. Use a GetObject call to connect to the WMI namespace rootcimv2, and set the impersonation level to “impersonate.”

  3. Use the ExecQuery method to query the Win32_Service class. To restrict data retrieval to a single service, a Where clause is included to limit data retrieval only to the service named DbService. This ensures that the correct service is retrieved because service names must be unique.

  4. For the single service in the collection, use the StopService method to stop the service.

  5. Use the Delete method to delete the service.

Example 15.23. Removing a Service

1 strComputer = "."
2 Set objWMIService = GetObject("winmgmts:" _
3     & "{impersonationLevel=impersonate}!\" & strComputer & "
ootcimv2")
4 Set colListOfServices = objWMIService.ExecQuery _
5     ("SELECT * FROM Win32_Service WHERE Name = 'DbService'")
6 For Each objService in colListOfServices
7     objService.StopService()
8     objService.Delete()
9 Next
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset