Search This Blog

Programming eDiscovery in SharePoint Server 2013

Recently I needed to get a grip on how to work with the eDiscovery tools in SharePoint 2013 from the server-side object model. There's not much information out there on how to do this (and some of the information out there is plain wrong), so I built a proof-of-concept console app to work through the key features, including:

Programmatically retrieving an eDiscovery case.

Creating a new source.

Creating a custodian.

Creating a new source.

Creating a new eDiscovery set.

Using queries and exports.

Applying in-place holds to eDiscovery sets.

I'll keep the explanation to a minimum, as I'm hoping the code largely speaks for itself. I'll assume you know the basic concepts of eDiscovery in SharePoint 2013, including how to work with cases, sources, sets, queries, and exports through the UI. To use the code, you'll need assembly references to Microsoft.SharePoint.dll and Microsoft.Office.Policy.dll. All the eDiscovery classes you need are in the Microsoft.Office.Server.Discovery namespace.

Our first task is to retrieve a case. An eDiscovery case is an individual site (SPWeb) within your eDiscovery Center site collection. Individual eDiscovery cases are represented by the Case class. The Case class provides the entry point for all eDiscovery operations in code. To retrieve a case, you need to call the Case constructor and pass the corresponding SPWeb instance as a parameter:

using
(SPSite site = new SPSite("http://sp2013/sites/ediscovery/"))

using
(SPWeb webCase = site.OpenWeb("case1"))

{

Case myCase = new Case(webCase);

Next, let's use the Case instance to create a new custodian. Essentially all this does is create a new list item in the Custodians list in the case web. Later, you'll see how we can assign custodians to sources to indicate who's responsible for the source:

//Create a custodian

Custodian newCustodian = myCase.CreateCustodian();

newCustodian.Name = "Legal Eagle";

newCustodian.LoginName = "JASON\\bob"; // i.e. domain username

newCustodian.Update();

Next let's create a new source. Essentially, SharePoint 2013 eDiscovery sources are either SharePoint webs or Exchange mailboxes. In both cases, the source is represented by the Source class. To create a new source from a SharePoint web, you call the Case.CreateLocation method. To create a new source from an Exchange mailbox, you call the Case.CreateMailbox method. I'm going to focus on creating a source from a SharePoint web. Once you've created the new Source instance, if you want it to work properly, you need to set three key properties:

Set the Source.WebId property to the ID of the SPWeb for which you want to define a source.

Set the Source.SiteId property to the ID of the SPSite containing the web for which you want to define a source.

Set the Source.FederationId property to the ID of the search result source (e.g. "Local SharePoint Results") from which you want to retrieve content.

Getting the ID of webs and sites is straightforward. Getting the ID of the search result source is a little more complex (credit). The code looks like this:

Now that we've created a source, we can use it in eDiscovery sets or queries. In the object model, eDiscovery sets are represented by the SourceGroup class. Let's create a new one and add our source to it:

// Create a new eDiscovery Set

SourceGroup newSet = myCase.CreateSourceGroup();

newSet.Name = "Team Site Leaflets";

newSet.AddSource(newSource);

newSet.DateStartFilter = new DateTime(2015, 1, 1);

newSet.Query = "Leaflet";

newSet.Update();

If you want to apply an in-place hold to the set, you simply set the SourceGroup.Preserve property to true and then call the Update method:

// Put the set on hold

newSet.Preserve = true;

newSet.Update();

Note that this requests an in-place hold. The hold won't actually take effect until the eDiscovery In-Place Hold Processing timer job runs.

Next, let's look at how to create a query. In the object model, queries are represented by the SavedSearch class:

// Create a new query based on the discovery set we created earlier

SavedSearch
newQuery = myCase.CreateSavedSearch();

newQuery.Name
= "Coded Query";

newQuery.SourceManagement
= SourceManagementType.SourceGroup;

newQuery.SourceGroupIds.Add(1)
// SPListItem ID of the discovery set

newQuery.Deduplication = true;

newQuery.SPFilters = "Created<=2/15/2015";

newQuery.Update();

When you create a query through the UI, you can choose whether to include eDiscovery sets (each consisting of one or more sources with filter criteria) or sources (no filter criteria). In the object model, you specify query scope by setting the SavedSearch.SourceManagement property to one of the following SourceManagementType enumeration values:

SourceManagementType.AllContent. This corresponds to the All case content option in the UI. The query contains all the sources defined in the case, with any eDiscovery set filters applied.

SourceManagementType.SourceGroup. This corresponds to the Select eDiscovery sets option in the UI. The query contains the sources defined in the discovery sets you select, with any discovery set filters applied.

SourceManagementType.Source. This corresponds to the Select sources option in the UI. The query contains the sources you select, and no discovery set filters are applied.

Once you've configured this property, you can either add sources or discovery sets to the query by adding integer IDs to the SourceIds or SourceGroupIds collections respectively. In this example, I've specified the SourceManagementType.SourceGroup option and then added the integer ID of my discovery set to the SourceGroupIds collection.

The SavedSearch class also allows you to specify string filters and refiners for the query. You can use the SPFilters and SPRefinements properties to specify filters and refiners for SharePoint sources, and you can use the EXFilters and EXRefinements properties to specify filters and refiners for Exchange mailboxes. A word of warning, however - the syntax required for the refinement properties is not user friendly. For example, if you create a query with refiners that match a file extension of "txt" and an author of "Administrator", the SPRefinements property ends up looking like this:

So it's probably fair to say that our ability to create query refiners in code is limited.

Finally, let's take a look at how to create an export in code. When you create an export, you're essentially just adding an item to the Exports list in the case web. However, the Export class does expose a ResultLink property that enables you to get the exported and packaged discovery content.

Hi Prashant - you're absolutely right. SavedSearch.SPRefinements is writeable but SavedSearch.SPFilters is not. I'll figure out a workaround and update the post when I can. You might be able to do it through the string-based SavedSearch.Query property.

Post a Comment

Popular posts from this blog

If you use SharePoint Designer 2013 to build workflows, there's a fair chance you'll have come across the following error message:Server-side activities have been updated. You need to restart SharePoint Designer to use the updated version of activities.
Needless to say, restarting SharePoint Designer rarely makes the error go away. The usual advice is:

Approach 1: Clear the cache folders
See for example How to Clear Your SharePoint Designer 2010/2013 Cache. If you've just deployed some custom workflow activities to your site, this will probably solve your problem (and you should clear the cache folders every time you deploy custom activities). If the error occurs spontaneously, this approach often won't help.

Approach 2: Reinstall SharePoint Designer
This might work if you've got a preview version of SharePoint Designer installed. If not, it's unlikely to help. It didn't work for me, and it didn't work for countless others on the forums.

Today's problem occured after I restarted a Hyper-V based SharePoint 2013 farm (Windows Server 2012, one SharePoint 2013 machine, one SQL Server 2012 machine, one DC). I fired up Central Administration and was hit with the following error:

After checking the obvious things - testing connectivity to the DB server, checking the SQL service was running, verifying permissions, etc - I initially figured this was an issue with my Hyper-V snapshots being out of sync, so I ran the SharePoint Products Configuration Wizard. This hit me with the following error:

Failed to detect if this server is joined to a server farm. Possible reasons for this failure could be that you no longer have appropriate permissions to the server farm, the database server hosting the server farm is unresponsive, the configuration database is inaccessib…