Exchange 2007 Offline Address Book Generation
December 5th, 2008
Update on 12/11/2008: DGoldman’s blog listed an unofficial fix for the problems listed below that matches what I went through to resolve this.
During a recent Exchange 2007 migration I ran into a few days of problems related to generating an Offline Address Book. The environment was as follows:
- Existing Exchange 2003 infrastructure with 2 front-end servers and 2 backend servers, one of which was a cluster of 2 physical servers
- 2 Exchange 2007 Hub/CAS servers running Windows 2008 X64, patched through Update Rollup 5
- 2 Node Exchange 2007 SCC Mailbox cluster running Windows 2008 X64, patched through Update Rollup 5
I created a new Offline Address Book through the Exchange Management Console under Organizational Configuration > Mailbox > Offline Address Book. The new OAB was configured normally: generate on my SCC cluster, distribute via web on my CAS servers, enable public folder distribution. The problem was that everytime the OAB generated, whether it be manually initiated (Update-OfflineAddressBook -Identity "Name of OAB“) or through the schedule, it failed with these event log errors:
Source: MSExchangeSA (OAL Generator)
Event ID: 9109
Description: OALGen encountered an error ffffffff (internal ID 50506a8) while generating address list ‘/o=Uconn/cn=addrlists/cn=oabs/cn=OAB 2007′. Check other logged events to see if this is a serious error.
Source: MSExchangeSA (OAL Generator)
Event ID: 9334
Description: OALGen encountered error ffffffff while initializing the offline address list generation process. No offline address lists have been generated. Please check the event log for more information.
Initially, I tried a lot of workaround related to permissions. There’s a blog on MSDN that had a lot of information about MSExchangeSA errors. I thought I had it fixed when I found this post, but in the end it had no effect. My permissions were not as this post said they should be, but it didn’t make any difference. I also tried adding the computer account for the cluster to the Exchange Servers group, but that also had no effect.
I found a forum post that indicated a known problem with OAB generator on SCC clusters. It recommended a workaround where you change the generation location to a local disk instead of the clustered disk. To do so, I modified the registry key HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\MSExchangeSA\Parameters\. This was set to a location on the shared storage. I changed this registry key on both cluster nodes to a local disk folder, and reran the OAB update. This time I got 4 errors, but they were different errors and I was encouraged.
Source: MSExchangeSA (OAL Generator)
Event ID: 9328
Description: OALGen encountered file error 80070003 (internal ID 5050221) while generating the offline address list for address list ‘\Global Address List’. Make sure there is enough disk space available.
Source: MSExchangeSA (OAL Generator)
Event ID: 9373
Description: OALGen detected that the file ‘\\
Source: MSExchangeSA (OAL Generator)
Event ID: 9328
Description: OALGen encountered file error ffffffff (internal ID 505026d) while generating the offline address list for address list ‘\Global Address List’. Make sure there is enough disk space available.
Source: MSExchangeSA (OAL Generator)
Event ID: 9371
Description: OALGen encountered an error while generating the differential downloads of address list ‘\Global Address List’. The offline address list has not been updated so clients will not be able to download the current set of changes. Check other logged events to find the cause of this error.
Further reading on that above forum post lead me to Microsoft KB957978, which indicated needing Update Rollup 5 (which I already had) and the patch from Microsoft KB955733. This hotfix was recommended for problems in generating an OAB on SCC clusters in Windows 2008. After installing the hotfix, I could finally successfully generate an OAB. The problems didn’t end there, however.
My next step was to confirm that the OAB successfully copied to my CAS servers, and it did. The last step is to confirm that clients can actually download the OAB, and this is where I ran into the next problem. When trying to download the OAB in Outlook 2007, I received error 0X8004010F. I initiated the download by selecting the Tools menu > Send/Receive > Download Address Book. If I changed the OAB for my mailbox database back to an OAB on Exchange 2003, it worked fine. Once again, the DGoldman blog was helpful through this post.
When I viewed the OAB associated with my mailbox database, it was correctly listed. But, through the recommendation of the above post, I looked at the actual object through ADSIEdit.msc and found that it had the incorrect value. The mailbox database object is located here:
CN=
The property to look at is msExchUseOAB. In my case, the DN listed in this property was incorrect. I’m not sure how the Exchange Management Console still listed the correct OAB when i viewed it, but it did. So, I deleted this OAB and created a new one with a completely different name. Then, I verified that it generated successfully and copied to the CAS successfully. Then, I associated the new OAB with my mailbox database and checked this property in ADSIEdit.msc. This time, it had the correct value. And, I could finally download the OAB in Outlook successfully.
I was satisfied, but still curious. My thinking was, that after the patching and verification of the correct DN above, that I didn’t need to the registry change to change the location of OAB generation to a local disk. So, I changed the value back to the cluster disk location, and everything still worked. So, it seems that those are two separate fixes. If you do the registry change, that may help you by itself. If you apply the patch, that may help you by itself. But it doesn’t seem that they are both necessary.
Filed under: Microsoft Exchange 2007, System Administration | 2 Comments »
Creating REST web services in C#, Part 2
July 22nd, 2008
In part one of this article series, I introduced creating RESTful web services in C# and ASP.NET. My hope is that you found it easy to create a web service quickly and have it align to REST design. I highlighted how the developer has great flexibility in deploying these web services. This can be a double-edged sword, however, when the web services become more and more complicated. Soon you’ll be writing code to handle all of the different HTTP verbs (GET, POST, PUT, DELETE, etc), all of the different combinations of input parameters, and all of the possible output results. Even worse, if you’re coding multiple web services you may find yourself duplicating a lot of logic because of the lack of a framework. This is exactly the situation that I found myself in and I quickly learned that I would need some help.
What I found that I needed was a generic class that I could implement in each of my web services which would check the incoming HttpRequest for validity. The definition of validity will change from one project to the next, so the solution had to be flexible. What I decided on was creating an abstract class that encapsulated the relevant request information that I used across web services. This abstract class has one abstract property, IsValid, which must be implemented in each class. So, for example, if one web service only accepts GET requests while another accepts GET, POST, and PUT, we can implement different IsValid properties while still having access to the common features of the class.
Below are the common features that this class will expose as properties:
Verb(enum of Verbs): HTTP verb, with support for GET, POST, PUT, and DELETE. Others could be easily implemented, if necessary.Method(String): The method being called. This assumes that the architecture which I described in my previous article where we modified the web.config file is being used. So, for our example URL ofhttp://localhost/SquareMyInteger/10, theMethodwould beSquareMyInteger.Arguments(String[]): All of the URL arguments specified after theMethod. So, for the above URL we would have oneArgument: 10.ContentType(String): The expected return content type in HTTP header format. My implementation supports three content types:text/xml (default),application/json,text/plain. The class looks for the requested content type in the query string argumentformat. For example:http://localhost/SquareMyInteger/10?format=jsonOutputFormat(enum of OutputFormats): The abbreviated version ofContentType. For example, ifContentTypeistext/xml,OutputFormatisxml. Can also bejsonortext.Data(String): The raw data sent to the web service from the client.Query(NameValueCollection): A collection of query string arguments, if present.StatusCode(Int32): The HTTP status code to return to the client after processing. The default value is 200, indicating OK.
The abstract property IsValid is of type Boolean and when implemented in a new class has access to all of the above properties. Below is an example of a REST web service which implements my request validation class, IRestRequest.
using System; using System.Web; namespace MyWebServices { public class WebService1 : IHttpHandler { public void ProcessRequest(HttpContext context) { WebService1Request REST = new WebService1Request(context.Request); if (!REST.IsValid) { context.Response.StatusCode = REST.StatusCode; context.Response.End(); } context.Response.Write(REST.Arguments[0]); context.Response.ContentType = "text/plain"; context.Response.Flush(); } public Boolean IsReusable { get { return false; } } } internal class WebService1Request : IRestRequest { public WebService1Request(HttpRequest r) : base(r) { } public override Boolean IsValid { get { if (this.Verb != RestVerbs.GET) { this.StatusCode = 405; return false; } if (this.Arguments.Length != 1) { this.StatusCode = 400; return false; } return true; } } } }
You should be familiar with the basic structure of this example, such as the ProcessRequest method. Below the actual web service class, I have created a new class that implements IRestRequest. I name these classes the same as the web service class, followed by Request, or WebService1Request in this example. Inside of this class there is a constructor which only calls the base constructor. You will notice that there is one argument in the constructor, an HttpRequest object. When an instance of this class is instantiated in the web service, you must pass the context.Request object to the constructor, as show in the example.
Next, you implement the abstract IsValid property. In this example, validity is determined by two factors. First, the only HTTP verb accepted is GET. Second, there must be exactly one argument in the request. If either of these conditions is not satisfied, then the return value is false and the return HTTP status is set to a value indicating an error. HTTP status 405 indicates that an illegal method was used in the call, such as POST when GET is expected. HTTP status code 400 indicates bad request, which in this example occurs due to the lack of a URL argument.
The IsValid property could be much more complex if you are working with a web service that accepts multiple verbs, all with their own requirements for arguments and incoming data. But, all you are doing is defining what makes the request valid, not working with the actual data. This separation makes your implementation code for the web service much more logical. As you can see in the above example, the first thing that the web service does is instantiate this new class, sending it the incoming request object. It then checks for validity, and if false, sends and error the client and ends the session. If the request is valid, the REST object, in this case, contains known valid information and your implementation can proceed without constant checks for the information you need. You don’t need to drill down into the context.RequestArguments array.
The IRestRequest abstract class is available to download on my Google Code project site. It can easily be modified to suit your needs, such as addition more supported verbs or different content types. Also, this code makes use of LINQ statements. If you aren’t running .NET 3.5 the LINQ statements in the constructor can be replaced with traditional logic. If you aren’t yet using LINQ, seriously consider doing so.
In part three of this article series, I will discuss how to implement security for these REST web services.
Filed under: .NET, Development, REST, Web Services | 2 Comments »
Using IIS 7 for SharePoint 2007 Forms Based Authentication
July 14th, 2008
This article outlines the procedure for using the features built into IIS 7 on Windows Server 2008 to create a forms-based membership provider for SharePoint 2007 without writing any custom code. This will allow for the creation of user accounts that will reside in a SQL Server database. These accounts will have access to your SharePoint environment over forms based authentication and can be granted (or denied) access to SharePoint resources just like an Active Directory account. I have tested this using SQL Server 2005 Standard edition as a store for account information.
First, a SQL Server database is needed.
- On your SQL Server, open
%windir%\Microsoft.NET\Framework\v2.0.50727\aspnet_regsql.exe. Note that if using a 64-bit version of the operating system, you will open Framework64 rather than Framework. - Select Configure SQL Server for application services.
- The server name should be the local server. Select Windows authentication. Under database enter a name for the new database, such as MossUserDB.
- When the wizard completes you will have a newly created database with all of the necessary tables and stored procedures.
The accounts used for your central administration site and SharePoint applications pools will need permissions to this database. I have tested this with db_owner permissions for these accounts, but a more restrictive permission set may be possible.
Once you have a database created, you need to create a SharePoint forms based authentication zone.
- Open the Central Administration page for your SharePoint implementation.
- Click on Application Management.
- Click Create or extend Web application under SharePoint Web Application Management.
- Click Extend an existing Web application
- Under Web Application select the application for which you wish to create a forms based provider.
- Under IIS Web Site select Create a new IIS web site. Enter a good description such as what will be the FQDN of the extended application. My recommendation is to use port 80 and set a unique host header value for this site. For example, if your main SharePoint site is
https://sharepoint.mysite.xyz, then create this new site similar tohttps://extranet.mysite.xyz. Alternatively, you could use the same domain with an alternative port. If you’d like to follow my recommendation, enter 80 under Port and enter the unique domain name under Host Header (i.e. the extranet.mysite.xyz from above). - Under Security Configuration you can accept the defaults or tweak them to suit your needs.
- Under Load Balanced URL the address is automatically generated from the values entered above. If you choose to use port 80, you can remove the
":80"at the end of the generated URL. For Zone, select what is appropriate for your implementation. - Click OK. A new application will be created in IIS.
Now that your web application has been extended, you need to create the IIS 7 connections to your empty database.
- On your Windows Server 2008 computer, open the IIS 7 Manager from Administrative Tools.
- Expand the tree to Server Name > Sites > Site Name, where Site Name is the description entered in the above steps.
- You should now be looking at the Features View for this site. If you are looking at the content view (the folders and files in the directory), click Features View at the bottom of the console.
- Double-click on Connection Strings.
- Click on Add in the Actions pane.
- Enter a simple name for this connection string, such as MossDB.
- Enter the name of your SQL server and the name of the database created above.
- If you correctly assigned permissions above then you should be able to choose Use Windows Integrated Security. If you experience problems, you can explicitly set the credentials to a user to which you assign SQL Server database permissions.
- Click OK.
- Go back to the main features view and select Providers.
- Under Feature select .NET Roles.
- Click Add in the Actions pane.
- Under Type select SqlRoleProvider.
- Under Name enter a simple name to describe this role, such as MossRole.
- Under Data > ConnectionStringName select the connection string created above.
- Accept defaults for the rest of the settings and click OK.
- Under Feature select .NET Users.
- Click Add in the Actions pane.
- Under Type select SqlMembershipProvider.
- Under Name enter a simple name to describe the provider, such as MossUsers.
- For now, leave the default settings in tact under Behaviors.
- Under Data > ConnectionStringName select the connection string created above.
- Accept defaults for the rest of the settings and click OK.
IIS is now configured to use the SQL Server database as a store for accounts. Now create a user that can be used to test SharePoint forms based authentication in the next step.
- Still in IIS 7 Manager, switch back to the main features view of the new site.
- Open .NET Users.
- Select Set Default Provider in the Actions pane.
- Select the provider created in the previous step, such as MossUsers.
- Select Add in the Actions pane
- Enter the details for a new user.
Now SharePoint 2007 must be configured to use the new membership provider.
- Open the Central Administration page for your SharePoint site.
- Go to the Application Management tab.
- Select Authentication providers under Application Security.
- Ensure that you are using the correct Web Application and select the Zone created above.
- Under Authentication Type select Forms.
- Under Membership Provider Name enter the name chosen above when we created a .NET Users provider, such as MossUsers.
- Under Role Manager Name enter the name chosen above when we created a .NET Roles provider, such as MossRoles.
- Click Save.
You should now be able to access the newly created SharePoint site and authenticate using forms based authentication with your new user account. You should also be able to assign permissions to resources using accounts created in this manner.
Filed under: SharePoint | 2 Comments »
Creating REST web services in C#, Part 1
July 11th, 2008
I have coded a few .NET-based web services in the past through SOAP and the features integrated into Visual Studio. The creation process worked well, but the consumption process in anything other than .NET never impressed me. And, In anything that I do I try to stick to the KISS philosophy - Keep it Simple, Stupid. That’s why, when I learned about REST, I knew that my SOAP days were numbered. REST is more of a guideline than a standard, and a developer has great flexibility in their implementation. This is nice, but it also means that there isn’t a standard “REST Framework” for .NET. My intention here, then, is to explain how I have implemented REST web services in C#. I’m not going to explain REST any further since there are already great resources for that (I would recommend these two articles at CodeProject).
A web page is created to be consumed by a human reader (usually). A web service, on the other hand, is created to be consumed by a human’s code (usually). So, then, why would we implement a REST web service using traditional ASP.NET pages? It didn’t occur to me at first, but do my web services need all the functionality afforded by the myriad of namespaces associated with a “standard” ASPX page? For example, creating a blank Web Form in Visual Studio 2008 results in the following declarations:
1 2 3 4 5 6 7 8 9 10 11 12 | using System; using System.Collections; using System.Configuration; using System.Data; using System.Linq; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.HtmlControls; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Xml.Linq; |
This doesn’t look like the beginning of a web service to me. Fortunately, there is a much more appropriate alternative: HTTP Handlers. An HTTP Handler is any class that implements the System.Web.IHttpHandler interface. This interface defines one method, ProcessRequest, and one property, IsReusable. When an HTTP handler is called, the entire HTTP session is exposed through a System.Web.HttpContext object. This gives us the ability to access any information about the actual HTTP request through HttpContext.Request and return any information through the HttpContext.Response object.
Let’s look at an example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | using System; using System.Web; namespace MyWebServices { public class MyHttpHandler : IHttpHandler { public void ProcessRequest(HttpContext context) { } public Boolean IsReusable { get { return false; } } } } |
By design, REST requests are stateless, therefore we set IsReusable to false. If you look at the ProcessRequest method, you’ll notice the HttpContext variable. This object is automatically available to you when implementing IHttpHandler. According to MSDN, the HttpContext object “…encapsulates all HTTP-specific information about an individual HTTP request.” So, if we want to find out information about the request, we access HttpContext.Request. If we want to send data back, we access HttpContext.Response. I learn by example, so below is code for a elementary math web service that will take integer input from the query string on a GET request and return the integer’s square in plain text.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | using System; using System.Web; namespace MyWebServices { public class SquareMyInteger : IHttpHandler { public void ProcessRequest(HttpContext context) { context.Response.ContentType = "text/plain"; if (context.Request.HttpMethod == "GET") { Int32 toSquare; if (Int32.TryParse(context.Request.QueryString["num"], out toSquare)) { Int32 squared = toSquare * toSquare; context.Response.Write(squared.ToString()); } else { context.Response.Write("Not an integer!"); } } } public Boolean IsReusable { get { return false; } } } } |
Note that you can add references to any .NET namespace; you are not limited to System and System.Web. But you can see that this is much more lightweight by default than a full ASP.NET page. So, where should this code live? To start, lets put it into what ASP.NET calls a generic handler file. This type of file has an ASHX file where the first line is an ASP directive that looks like this:
<%@ WebHandler Language="C#" Class="SquareMyInteger" Debug="true" %>
using System;
...
This line tells ASP.NET that this file is a WebHandler, written in C#, in debug mode, and that the HTTP handler method and property are in the SquareMyInteger class. So, upon HTTP request of this file ASP.NET will execute the ProcessRequest method inside of the SquareMyInteger class. A good convention is to save this file with a name that matches the class, i.e. SquareMyInteger.ashx. In the above code example, the integer to be squared is passed via query string and retrieved using context.Request.QueryString["num"]. So, a query to this web service would look like the following:
http://localhost/SquareMyInteger.ashx?num=10
If you’ve done your homework on REST, you know that this URL is acceptable, but not ideal. A better REST URL would eliminate the awkward query string syntax and look like this:
http://localhost/SquareMyInteger/10
This URL is inline with REST design, but how do we implement this as a .NET web service? The answer, as it turns out, is fairly simple, especially with Visual Studio. First, remove the special ASP line from the top of the above web service and compile the code as a class library (DLL). Create a blank web project and add a reference to the DLL which you just compiled. Add a web.config file to the project.
Inside of this web.config file is where we will instruct IIS to look for requests like above and direct them to our class, even though the above files won’t really exist on the file system. Inside of the web.config file find this section:
<configuration>
<system.web>
<httpHandlers>
Inside of this section, after all the <remove /> and <add /> lines, create a new add line that matches below:
<add verb="*" path="SquareMyInteger/*" type="MyWebServices.SquareMyInteger, MyWebServices" />
…where MyWebServices.SquareMyInteger is the namespace and class which we created and MyWebServices is the file name of the DLL which we compiled. That’s it. We don’t actually need to create any files in the directory other than the web.config. Now, any request to that directory that contains SquareMyInteger followed by a slash and any content will be passed to our class. That was easy, right? There’s only one catch. The above <add /> directive is for IIS6 and previous. IIS7 defines this in a different location within web.config. If that applies to you, locate the below section in your web.config file:
<system.webserver>
<handlers>
Inside of this section, after the other <remove /> and <add /> lines, add the below directive:
<add name="SquareMyInteger" verb="*" path="SquareMyInteger/*" type="MyWebServices.SquareMyInteger, MyWebServices" />
This is almost identical to the IIS6 version of this directive, with the addition of a name attribute which can be anything that describes the directive. Notice that both this and the IIS6 directive include a verb attribute. This can be set to only accept certain verbs, such as GET or POST. In our example, we use the asterisk to indicate we will accept any verb.
You have now implemented a REST web service in C#. In part two of this article I describe how to validate HTTP requests inside of C# REST web services.
Filed under: .NET, Development, REST, Web Services | 5 Comments »