April 2009
Volume 24 Number 04
Take Control - Use SharePoint to Manage Your Windows Services
By Pav Cherny | April 2009
This article discusses:
|
This article uses the following technologies: Windows SharePoint Services |
Contents
A Non-Integrated Solution Prototype
Integrating with SharePoint
Defining Process Identities
Windows Service Dependencies
Adding Configuration Parameters
Final Touches and Testing
Summing Up
All Windows Server operating systems depend heavily on Windows services. This hasn't changed since Microsoft first introduced services with Microsoft Windows NT 3.1 in 1993 and it likely will not change in future operating systems either. Even SharePoint couldn't exist without Windows services, such as Internet Information Services (IIS) and additional services for SharePoint-specific tasks. You can take advantage of the built-in services infrastructure through custom jobs for the Windows SharePoint Services Timer (SPTimerV3) service, but if you want extensive data processing, continuous system monitoring, virus scanning, network communication, or agent-based backup and restore operations, it's still best to create your own separate Windows services. It isn't difficult to build a Windows service. Microsoft Visual Studio 2008 includes the necessary project templates and wizards to get you started and you can use C/C++ as well as managed languages to implement the service logic. However, it is not as straightforward to integrate a Windows service with SharePoint. The Windows SharePoint Services (WSS) 3.0 Software Development Kit (SDK) does not cover this topic very well, there are no wizards, and you must ensure that the integration results do not jeopardize SharePoint security.
In this article, I'll show you how to integrate a Windows services–based solution with SharePoint. The results enable you to provision, start, stop, and remove service instances through SharePoint 3.0 Central Administration and the SharePoint administration object model, maintain service settings centrally in the SharePoint configuration database, and ensure that all service instances in a SharePoint farm use the same security account and configuration parameters. When you change configuration settings in SharePoint, you can apply the changes consistently across all servers in the farm. Of course, it is also possible to configure parameters separately for individual service instances, if necessary. For my sample solution, I chose to perform a sensitive operation on front-end servers to highlight SharePoint-specific security considerations. The companion material includes source code and compiled files in various stages of completion according to the sequence of steps I'll discuss here. The companion material also includes step-by-step worksheets if you want to follow my explanations in your own development environment.
A Non-Integrated Solution Prototype
It is a best practice to start a development cycle by gathering requirements, outlining a general solution design, and delivering proof of concept in the form of a representative prototype. This is particularly true for security-sensitive SharePoint services that require elevated permissions on front-end servers or application servers. While it is not necessary to integrate the prototype with SharePoint for manageability, your prototype must address SharePoint security dependencies right from the start to avoid complex design issues later on. Even seemingly simple solutions might pose tricky challenges in a SharePoint environment.
Take this article's sample solution, for instance. It addresses the requirement to consolidate Security event log entries from front-end servers in a custom SharePoint list. It's a seemingly simple task: The solution registers a handler called EntryWrittenEventHandler in the Event Log subsystem on the local computer to receive an event notification whenever the operating system adds a new entry to the Security event log, and then, if the entry meets a specified filter criterion, such as entry type equals FailureAudit or SuccessAudit, the solution adds a corresponding item to a custom Security Audit list in SharePoint. It isn't difficult to build a basic Windows service and a custom SharePoint list to do this, yet the service account requires write permissions in the SharePoint site to add items to the custom list and administrative permissions on the local server to register EntryWrittenEventHandler—and this is a security issue on SharePoint servers.
While you can satisfy the SharePoint permission requirements by running your Windows service in the context of the SharePoint site's application pool account, you must not grant local administrative permissions to SharePoint security accounts. Granting security accounts administrative permissions on front-end servers jeopardizes SharePoint security, as explained in the TechNet MagazineJanuary 2009 column entitled " SharePoint Security Accounts."
So, how do you avoid the elevation of permissions if your solution must perform a security-sensitive operation? A good technique is to split the solution into multiple components and use a secure interprocess communication mechanism between them. You can then run the component that requires administrative permissions in the context of the local System account, while the other component runs in the context of the SharePoint security account. Microsoft uses this technique in the SPTimerV3 service. SPTimerV3 runs without administrative permissions in the context of the SharePoint farm account and relies on the Windows SharePoint Services Administration (SPAdmin) service to perform administrative tasks on the local server. The SPAdmin service runs in the context of the local System account. SPTimerV3 and SPAdmin use .NET Remoting for interprocess communication, yet you can also use a lighter weight mechanism, such as named pipes, as long as you protect the communication facility by means of access control lists (ACLs). In the sample solution, I use the named-pipe approach.
Take a look at the overall solution architecture in Figure 1. The solution relies on a separate Security Event Handler service that includes the aforementioned EntryWrittenEventHandler callback function for receiving notifications from the local event log subsystem. This service runs in the context of the local System account. Its counterpart is the Security Event Receiver service, which runs in the context of the SharePoint site's application pool account to create content items in a specified security audit list. When you start the Security Event Receiver, the service creates a secure named pipe that grants only the local System account access permissions. The Security Event Handler service uses this named pipe whenever a "Security Event Log Entry Written" event fires to communicate with the Security Event Receiver. This design works without the requirement for elevated permissions and without jeopardizing SharePoint security. It's just a little more involved than the typical monolithic Windows service architecture.
Figure 1 Splitting up a SharePoint Solution
You might notice in the sample application that the Microsoft .NET Framework cannot parse the description of Security entries. This is not critical for the purposes of this article, but consider installing the hotfix described in the Microsoft Knowledge Base article " An application that uses Windows NT security event log APIs cannot read the description of an event log message from a computer that is running Windows Vista or Windows Server 2008."
Integrating with SharePoint
The delivery of a functional prototype is an important milestone; another is to deliver appropriate management tools, and this is a key aspect of SharePoint integration for Windows services. Without integration, it is hard to control deployed service instances or ensure a consistent service behavior across all servers in a farm. With integration, you can use SharePoint 3.0 Central Administration to determine the servers that run your services and configure relevant parameters for all service instances centrally. Figure 2shows the application after you've completed all integration steps. It might look like a small application, but SharePoint integration gives it all the characteristics of a full-featured SharePoint solution.
Figure 2 Integration with SharePoint
Essentially, you need four components to integrate a Windows service with SharePoint: a class that identifies the service, a class that enables SharePoint to control individual service instances, an administrative tool that adds objects of these two classes to the SharePoint configuration, and a SharePoint solution package to deploy these components.
According to the solution architecture depicted in Figure 1, there are two Windows services: Security Event Handler and Security Event Receiver. So let's create the corresponding service classes in a new class library project. I added this project directly to the SecurityAudit solution I've built in Visual Studio. It's called SecurityEventManagement. After I added a reference to the Windows SharePoint Services library, I created a new class derived from the SPWindowsService class for each Windows service. In the initial implementation of the SecurityEventReceiverService class, the SecurityEventHandlerService class has the same structure; only the service name is different, as you will see if you analyze the code in the Step 2 folder in the companion material.
The service class represents a particular Windows service in a SharePoint farm. It is a global object for all service instances, but you also need a class to associate an instance of the service with a particular SharePoint server. The SharePoint administration object model defines several service base classes and the service instance classes to which they correspond. The SPWindowsService class is the right choice for the Windows services, and thus the SPWindowsServiceInstance class is the right base class for the service instance classes: SecurityEventReceiverServiceInstance and SecurityEventHandlerServiceInstance. Again, the class definitions are relatively uncomplicated.
Now let me show you how to build the administrative tool to add the service and service instance objects to the SharePoint configuration. You can accomplish this through application pages in SharePoint 3.0 Central Administration, Stsadm.exe command extensions, or by any other means, such as through the use of Windows PowerShell. I opted to create a Stsadm.exe command extension because of its low overhead, which means little distraction from the job at hand. For detailed information about Stsadm.exe command extensions, read the article " How to: Extend the STSADM Utility."
The administrative tool must instantiate and persist service and service instance objects. The constructors of the classes reveal how to instantiate the objects. It is noteworthy that SharePoint service classes are derived from the SPPersistedObject class. When you call the Update() method, these objects are persisted. Similarly, if you call the Delete() method after de-provisioning, these objects are removed again. The SecurityEventServiceAdministration.cs code file in the SecurityEventManagement project illustrates these steps.
And that's it! You now can compile the solution and deploy it through the use of a WSS 3.0 solution package (.wsp). For a detailed explanation of how to create a solution package with Visual Studio and MakeCab.exe, I recommend Andrew Connell's articleon how to create WSS solution files. Also see the code in the companion material.
Defining Process Identities
The simplicity of SharePoint integration might be a nice surprise, but it's a direct result of having taken full advantage of the SharePoint object model. You can already provision service instances and use the management links in SharePoint 3.0 Central Administration (see Figure 3). For example, you can start and stop the Security Event Receiver service; just the Security Event Handler service isn't as easy. This is the case because SharePoint updates the service configuration when you click the Start link. By default, SharePoint configures the service to run in the context of the Local Service account, which doesn't have permissions to register an EntryWrittenEventHandler. Hence, the Security Event Handler service fails. You need to hardwire the Security Event Handler service to the local System account and should provide an option to provision the Security Event Receiver service with a desired application pool account as well.
Figure 3 SharePoint 3.0 Central Administration
Let's start with the Security Event Handler service because it is straightforward to hardwire the System account. It just takes three lines of code in the service constructor. The first line sets the service's process identity to the System account. The other two lines disable credential deployments and credential updates to prevent administrators from making changes to the account information in SharePoint 3.0 Central Administration. The Service Accounts page (_admin/FarmCredentialManagement.aspx) no longer lists this service in the Windows Service list box.
public SecurityEventHandlerService(SPFarm spFarm) : base(ntServiceName, spFarm) { base.ProcessIdentity.CurrentIdentityType = IdentityType.LocalSystem; base.ProcessIdentity.IsCredentialDeploymentEnabled = false; base.ProcessIdentity.IsCredentialUpdateEnabled = false; }
The Security Event Receiver service is slightly more complicated because you must handle a variety of security account information during service provisioning. The security account can be a domain user account with a password or a system account without a password. When you deal with a domain user account, the account name must follow NetBIOS name conventions and you should verify the password. In my implementation, the code sets the IsCredentialDeploymentEnabled and IsCredentialUpdateEnabled properties in this class to true so that SharePoint administrators can change the security account in SharePoint 3.0 Central Administration after provisioning the Security Event Receiver service.
Of course, you must also extend the administrative tool to provision the SecurityEventReceiverService object with a user name and password. Check out the SecurityEventServiceAdministration.cs file in the Step 3 companion folder. It includes the necessary modifications to the Stsadm.exe command extension.
Windows Service Dependencies
At this point, you can start and stop provisioned service instances on SharePoint servers and the services retain their security accounts. However, if you stop both service instances and then attempt to start the SecurityEventHandlerService instance without first starting the SecurityEventReceiverService instance on a selected server, you encounter an error that states that "The dependency service or group failed to start." The cause is that the Security Audit Setup program configures the Security Event Handler service to depend on the Security Event Receiver service so that the Service Control Manager (SCM) starts the Security Event Receiver service automatically when you start the Security Event Handler service. However, SCM cannot start a service if its Startup Type is disabled, and SharePoint sets the Startup Type to disabled when you stop a service instance. Consider changing the service installer logic. You can avoid this error if you remove all dependencies and install the services initially with the Startup Type set to Disabled. This ensures a consistent control behavior in SharePoint 3.0 Central Administration.
It's great to avoid unpleasant errors, yet the original dependencies existed for a reason. After all, Security Event Handler service and Security Event Receiver service depend on each other for a functioning Security Audit solution. If you start or stop the one, you should also start or stop the other. You can achieve this behavior by overriding the Provision and Unprovision methods in the service instances. The SecurityEventReceiverServiceInstance attempts to locate its SecurityEventHandlerServiceInstance counterpart on the same server and starts or stops it in addition to its own instance, if found. The code also demonstrates how to customize the Start and Stop action links to display a message box in the user interface to inform the administrator about the affected services.
If you analyze the source code in the Step 4 companion folder, you will notice that the SecurityEventHandlerServiceInstance class does not contain overridden Provision and Unprovision methods. It is sufficient to start and stop the services through the SecurityEventReceiverServiceInstance class. Instead, I marked the SecurityEventHandlerServiceInstance as a system service, which removes the corresponding Start and Stop links and hides the service from the list of configurable services. The SecurityEventHandlerService reference is now visible only in the All view of the Services on the Server page. The following code shows how to mark a service instance as a system service:
public override bool SystemService { get { return true; } }
Adding Configuration Parameters
The solution is now an integral part of SharePoint. The next step is to move relevant configuration settings from the local registry to the SharePoint configuration database in order to increase configuration consistency across the entire SharePoint farm. All service instances share the same configuration database. You only need to modify the Windows services to read the parameters from the configuration database instead of the registry. To maintain parameters in the configuration database, use the SPPersistedObject class, as explained in the WSS 3.0 SDK under " SPPersistedObject Class (Microsoft.SharePoint.Administration)."
The SPWindowsService and SPWindowsServiceInstance classes are derived from the SPPersistedObject class and are therefore my SecurityEventReceiverService and SecurityEventReceiverServiceInstance classes. This means that you can add global parameters as persisted properties directly to the service and service instance classes. In my example, these are the parameters SiteURL, ListName, and EventFilter. The SecurityEventReceiverServiceInstance class would be the right choice for instance-specific parameters, but in my solution all service instances are supposed to use the same settings, so I extended the SecurityEventReceiverService class.
To retrieve parameter values, you need access to the configuration database. SharePoint takes care of this for you if you keep the process identity of your service class credential-deployment-enabled (IsCredentialDeploymentEnabled = true). I did this for the Security Event Receiver service class, yet the Windows service must also know how to use the service class. You can take care of this requirement by adding a class-library reference to the Visual Studio project that contains your Windows service. In my example, this is the SecurityEventReceiver project. Make sure you mark the SecurityEventReceiverService class as public so that the Windows service can use it and deploy the class library's assembly in the Global Assembly Cache (GAC) by means of your SharePoint solution package. You need to deploy to the GAC before using the GetValue method of the farm's Services collection to retrieve the service object. Here's how to access the configuration properties of the SecurityEventReceiverService class:
SecurityEventReceiverService EventReceiverService = SPFarm.Local.Services.GetValue<SecurityEventReceiverService>( SecurityEventReceiverService.ntServiceName); if (EventReceiverService == null) throw new Exception("Unable to locate SecurityEventReceiverService" + " in the local SharePoint farm."); siteURL = EventReceiverService.SiteURL; listName = EventReceiverService.ListName; eventFilter = EventReceiverService.EventFilter;
Now that the Windows service can read the parameters from the configuration database, the remaining task is to set them. The preferred choice for many administrators is a custom application page, added to SharePoint 3.0 Central Administration and tied to the service through the ManageLink property, but you can also use Stsadm.exe command extensions or other methods. The following code uses the ManageLink property, added to the SecurityEventReceiverServiceInstance class.
public override SPActionLink ManageLink { get { return new SPActionLink("SecurityAudit/ManageSecurityAuditSettings.aspx"); } }
It converts the service's display name into a hyperlink that opens the _admin/SecurityAudit/ManageSecurityAuditSettings.aspx page. You can find the ManageSecurityAuditSettings.aspx page in a separate class library, which I called ManageSecurityAuditSettings. For details on how to build custom application pages, read Ted Pattison's article " Creating an Application Page in Windows SharePoint Services 3.0." See also the Step 5 companion folder.
Final Touches and Testing
The project is now almost complete, apart from finishing touches and proper solution testing. For example, you might want to change the display name of your services in SharePoint 3.0 Central Administration because the default names SecurityEventManagement.SecurityEventHandlerService and SecurityEventManagement.SecurityEventReceiverService are not very user friendly. However, you won't achieve the desired effect by overriding the DisplayName property in your service classes. The display name corresponds to the TypeName property instead, so never rely on TypeName == GetType().ToString() in a SharePoint solution.
Note also that both service and service instance classes offer a TypeName property because it is inherited from the SPPersistedObject class, but there is a quasi-hierarchical relationship. Service instances use the name of their associated service, by default. Service instance classes inherit an overridden TypeName property from the SPServiceInstance class that returns the TypeName of the associated service class. Of course, you can override the TypeName property in your service instance class again to change the display name according to certain conditions, such as to indicate that a critical component is missing.
SharePoint terminology is not as consistent as it could be, but it's not difficult to determine the purpose of virtual methods and properties regardless of their names. To determine available properties and methods, in Visual Studio, right-click on a base class such as SPWindowsService and select Got To Definition, then right-click SPService, Got To Definition, and so forth through the entire inheritance hierarchy. You might also check the WSS 3.0 SDK. The SPWindowsService and SPWindowsServiceInstance classes are hardly covered, but the lists of class members are useful to some degree ( SPWindowsService Membersand SPWindowsServiceInstance Members).
Keep in mind, however, that successful testing of service and service instance implementations on a single development computer is not a sufficient release criterion for SharePoint-integrated Windows services. SharePoint 3.0 Central Administration deals with Windows services quite differently on the local computer than on remote computers in a farm. Locally, SharePoint 3.0 Central Administration uses the security context of the logged-on SharePoint administrator, which is most likely a local administrator with all necessary permissions to perform administrative actions on services. Remotely, however, SharePoint 3.0 Central Administration uses timer jobs and the SPTimerV3 service. As I mentioned earlier, the SPTimerV3 service's security account is not a local Administrator on the remote computer, so the SPTimerV3 service uses the SPAdmin service to carry out the actions in the context of the local System account. In order to ensure that the differences in security contexts don't interfere with the operational state of your Windows services, I recommend that you fully test your solution in a farm environment with at least two front-end servers and a separate computer running SQL Server in a restrictive security configuration according to the " Windows SharePoint Services Security Account Requirements" worksheet.
Summing Up
You need to pay special attention to solution design and security when you're building SharePoint-integrated Windows services, but given the proper groundwork, the actual integration with SharePoint is relatively uncomplicated. The SharePoint object model already includes the necessary logic to provision, start, stop, and unprovision service instances on local and remote servers in a SharePoint farm. You need only define corresponding service and service instance classes to integrate your Windows services into the SharePoint configuration. Service and service instance classes are derived from the SPPersistedObject class, which means that you can use these classes to maintain service parameters directly in the SharePoint configuration database instead of the local registry, resulting in improved manageability and configuration consistency.
Overall, a full-featured solution that appears seamlessly integrated with SharePoint must include the Windows services administrative tools to provision service objects, application pages to extend SharePoint 3.0 Central Administration, and a SharePoint solution package to deploy the SharePoint components on all servers in the farm in addition to the actual Windows services. Windows services are vital components on Windows servers, and with the integration capabilities available in SharePoint they can assume critical functions in the SharePoint environment as well. For more information see www.microsoft.com/sharepoint, Windows SharePoint Services SDK Documentation, and blogs.msdn.com/sharepoint.
Pav Cherny is an IT expert and author specializing in Microsoft technologies for collaboration and unified communication. His publications include white papers, product manuals, and books with a focus on IT operations and system administration. Pav is President of Biblioso Corporation, a company that specializes in managed documentation and localization services.