Custom Workflow Activity for Creating a SharePoint Site

Recently I've had to automate various SharePoint 2013 site management tasks within workflows. I started out using SharePoint Designer 2013, but switched to building custom workflow activities in Visual Studio 2012 to give me a bit more flexibility and reusability (and to reduce the likelihood of SPD screwing up my work). I've developed custom workflow activities that perform the following tasks:
All of these activities involve calling SharePoint web services. I've used the REST API where it supports the operation, and I've used client-side object model (CSOM) XML directly when the REST API won't do what I need. (Huge thanks to Chris Givens for showing me how to work with CSOM XML). I'm hoping to document each of these activities and I'll add links to the list above (update - all done!).

In this post I'll show you how to build a workflow activity that creates a SharePoint site.

Prerequisites

To run these activities, your workflow service needs to be running with app permissions and it needs full control rights on the entire site collection. For guidance on how to set this up please refer to Create a workflow permissions by using the SharePoint 2013 Workflow platform on MSDN. I don't want to dwell on this as it's well-documented elsewhere - however, please note that to grant full control across the entire site collection, rather than a specific site, you need to replace the permission request XML in the MSDN article with the following:

<AppPermissionRequests>
   <AppPermissionRequest Scope="http://sharepoint/content/sitecollection" Right="FullControl" />
</AppPermissionRequests>

(Use this exactly as is - don't replace the Scope attribute with a specific URL.)

Fundamentals

You can use the REST API to create a site. You need to send a web request that resembles the following:

Endpoint:
{site collection URL}/_api/web/webinfos/add

HTTP method:
POST

Headers:
Accept: application/json; odata=verbose
Content-Type: application/json; odata=verbose

Body:
{"parameters":{
   "Url":"project1",
   "Title":"Project One",
   "Description":"A description of Project One",
   "Language":"1033",
   "WebTemplate":"STS#0",
   "UseUniquePermissions":true,
   "__metadata":{
      "type":"SP.WebInfoCreationInformation"
   }
}}

Note that the __metadata variable is preceded by a double underscore. This post from SharePoint Ryan really helped me figure out how to structure the JSON body.

Note also that you don't need to include an X-RequestDigest header when you're calling services from a SharePoint workflow - the workflow platform will add any required security headers for you.

Build the XAML file

To develop custom workflow activities for SharePoint 2013, you need to create an empty SharePoint 2013 project in Visual Studio 2012. You're going to deploy it as a sandbox solution (this is fine for Office 365 as the workflow activity is entirely declarative). You can then add workflow activities to the project by selecting the Workflow Custom Activity template from the Office/SharePoint category on the Add New Item menu.

Visual Studio will create a .xaml file and an .actions4 file for your workflow activity. The .xaml file is where you define your workflow logic, and the .actions4 file is where you configure your activity for use in SharePoint Designer. By default, Visual Studio will open the .xaml file in designer view.

In this case, I'll start by defining the arguments and variables for my workflow activity. Arguments define the values that you want to pass to and from your activity, whereas variables store values for use within your activity. You can define arguments and variables by clicking Arguments or Variables respectively at the bottom of the designer window.

To start with, I'll define the following arguments:
















The In arguments define the values that I want the consumer of my activity to provide, and the Out arguments define the values that my activity will return to the consumer. In this case I simply want to return the status code returned when I call the web service (to enable the consumer to respond if something went wrong) and the GUID ID of the site I've created.

Next, I'll define the following variables:














You'll see the reason for each of these as we build up the activity, but for now note that DynamicValue is a data structure for building nested sets of key-value pairs in a workflow. This is particularly useful when you're constructing JSON requests. Where you would have used a Dictionary type in SharePoint Designer, use a DynamicValue in Visual Studio.

Now we can build up the activity by dragging activities from the Toolbox on to the designer surface. The complete activity looks like this:








































As you can see, the activity consists of six child activities, all of which are built-in activities that you can add from the toolbox. Let's run through each of these in turn.

Step 1: Get current site URL

In this activity, we want to get the URL of the current site collection. I've used a LookupWorkflowContextProperty activity to do this. In the activity properties, I'm looking up the current site URL and assigning it to the siteUrl variable.















Step 2: Build the REST URL

In this activity, we want to build on the site URL to form a REST endpoint for our request. I've used an Assign activity to do this. In the activity properties, I'm concatenating the site URL and the site-relative REST endpoint, and assigning the result to the restUrl variable.















Step 3: BuildDynamicValue (jsonRequest)

In this activity, we build the JSON body for our REST request. I've used a BuildDynamicValue activity to do this.
















To define the properties in the jsonRequest variable, click the ellipsis in the Properties row. This enables you to provide a set of key/value pairs:






















For each path (key) you define, you can enter a string literal, a local variable or an argument in the Value column. In this case, I've assigned one of the arguments I defined earlier to each key. Notice how the structure of the paths are constructed with forward slashes. This is a shorthand way of adding nested values to the variable, by using XPath notation to define the path to each key. After setting these properties, the content of the jsonRequest variable will resemble the following:

{
   "parameters":{
      "Url":"project1",
      "Title":"Project One",
      "Description":"A description of Project One",
      "Language":"1033",
      "WebTemplate":"STS#0",
      "UseUniquePermissions":true,
      "__metadata":{
         "type":"SP.WebInfoCreationInformation"
      }
   }
}

Step 4: Call the REST API method

We've now set all the variable values we need, so we're ready to call the REST method. I've used the HttpSend activity to do this. You can configure every aspect of the request through the activity properties pane:

























In this case, I've:
  • Set the HTTP Method to POST.
  • Set the content of the request to the value of our jsonRequest variable.
  • Set the Uri for the request to the value of our restUrl variable.
  • Assigned the response body to the responseContent variable (this is a DynamicValue instance, because we're expecting a JSON response).
  • Assigned the response headers to the responseHeaders variable (again, a DynamicValue instance).
  • Assigned the response status code to the responseStatusCode variable (an HttpStatusCode enumeration value).
The other thing you need to do at this stage is to configure the request headers for the service call. To do this, click the ellipsis in the RequestHeaders row. This enables you to define your request headers as key/value pairs:


















Step 5: Assign responseStatusCodeOut

At this point in the execution of our workflow activity, we've called the REST API method and a new site has hopefully being created. Now all we need to do is extract any information we need from the HTTP response. In this Assign task, I'm taking the responseStatusCode variable, converting it to a string, and assigning it to the responseStatusCodeOut argument. The reason for this is that SharePoint Designer doesn't like complex types like HttpStatusCode and DynamicValue. If you want to return information to SharePoint Designer, you need to convert your properties to an SPD-friendly type. In this case, we simply call the ToString method on our responseStatusCode variable and assign it to the String-based responseStatusCodeOut argument:












Step 6: Get site GUID

In this final child activity, we want to get the ID of the newly-created site so that we can return it to consumers of our workflow activity. I've used a GetDynamicProperty<T> activity to do this. This activity enables you to extract a strongly-typed value, in this case a System.Guid, from a DynamicValue variable.















In this case, we want to extract a property from the responseContent variable. This is a variable of type DynamicValue that stores the response from the web service call. To extract a property from a DynamicValue, you use XPath to specify the path (in this case "d/Id") to the property you want. I've assigned the extracted property to the newSiteIdOut argument.

To specify the path to the property you want, you need to know how the JSON returned by the REST API is structured. The best way to do this is view the REST service calls in a web debugger such as Fiddler. In this case, the format of the response resembles the following:

{
   "d":{
      "__metadata":{
         "id":"http://team.jason.net/_api/web/webinfos/add",
         "uri":"http://team.jason.net/_api/web/webinfos/add",
         "type":"SP.WebInformation"
      },
      "Configuration":0,
      "Created":"2014-01-22T14:08:38",
      "Description":"A description of Project One",
      "Id":"6e504370-0ea5-48b1-ad12-8f5d6cd23b23",
      "Language":1033,
      "LastItemModifiedDate":"2014-01-22T14:08:44Z",
      "ServerRelativeUrl":"/project1",
      "Title":"Project One",
      "WebTemplate":"STS",
      "WebTemplateId":0
   }
}

From this response, you can see that specifying a path of "d/Id" will take you to the ID of the newly-created site.


Build the Actions File

Now that we've defined the logic for our workflow activity, we need to edit the .actions4 file so we can use the activity in SharePoint Designer. The .actions4 file defines the sentence that appears in SPD when you add the activity to a workflow, together with the arguments defined in the workflow activity. In this case, your .actions4 file should resemble the following:

<Action Name="Create Site" 
        ClassName="SiteManagementActivities.CreateSite" 
        Category="Site Management Activities" 
        AppliesTo="all">
  <RuleDesigner Sentence="Create a new subsite at the relative URL %1 using site template %2, title %3, description %4, locale ID %5, and unique permissions %6 (Output: %7 %8)">
    <FieldBind  Field="relativeUrl" Text="Relative URL" Id="1" />
    <FieldBind  Field="siteTemplate" Text="Site Template" Id="2" />
    <FieldBind  Field="title" Text="Title" Id="3" />
    <FieldBind  Field="description" Text="Description" Id="4" />
    <FieldBind  Field="lcid" Text="LCID" Id="5" />
    <FieldBind  Field="useUniquePermissions" Text="Use Unique Permissions" Id="6" DesignerType="Bool" />
    <FieldBind  Field="responseStatusCodeOut" Text="Response Status Code" Id="7" />
    <FieldBind  Field="newSiteIdOut" Text="New Site Id" Id="8" />
  </RuleDesigner>
  <Parameters>
    <Parameter Type="System.String, mscorlib" Direction="In" Name="relativeUrl" />
    <Parameter Type="System.String, mscorlib" Direction="In" Name="siteTemplate" />
    <Parameter Type="System.String, mscorlib" Direction="In" Name="title" />
    <Parameter Type="System.String, mscorlib" Direction="In" Name="description" />
    <Parameter Type="System.String, mscorlib" Direction="In" Name="lcid" />
    <Parameter Type="System.Boolean, mscorlib" Direction="In" Name="useUniquePermissions" />
    <Parameter Type="System.String, mscorlib" Direction="Out" Name="responseStatusCodeOut" />
    <Parameter Type="System.Guid, mscorlib" Direction="Out" Name="newSiteIdOut" />
  </Parameters>
</Action>

In the RuleDesigner element, you use the Sentence attribute to define the sentence you want to display in SharePoint Designer. You use placeholders in the format %1 where you want the user to be able to bind values to an argument (regardless of whether the argument is inbound or outbound). For each placeholder, you must add a FieldBind element with the following attributes:
  • Field. This identifies the name of the argument as it appears in your .xaml file. Make sure the name here matches the name of your argument in the .xaml file exactly.
  • Text. This specifies the placeholder text you want to display in the sentence in SharePoint Designer.
  • Id. This identifies the corresponding placeholder in the Sentence attribute.
  • DesignerType. This optional attribute specifies the type of control you want to display in SharePoint Designer. For example, a value of Bool enables the user to specify a Boolean value by clicking Yes or No. If you don't include a DesignerType attribute value, you get the default field editor in SPD - a text box with an ellipsis button and a function button.
Next, you must add a Parameter element for each argument. This specifies the .NET type and the direction of each argument. The Name attribute of each Parameter element must:
  • Match the Field attribute of the corresponding FieldBind element.
  • Match the name of the argument as it appears in your .xaml file.

Use the Activity in SharePoint Designer

To use the Create Site activity in SharePoint Designer, you need to deploy the sandbox solution to the root site of your site collection and then activate the feature that contains your workflow activity. To get the activity to show up (or update) in SharePoint Designer, you first need to close SharePoint Designer and clear the SPD cache. To do this, delete the contents of the following folders:
  • %USERPROFILE%\AppData\Local\Microsoft\WebsiteCache
  • %APPDATA%\Microsoft\Web Server Extensions\Cache
Tip: on my developer VM I use a post-build script to do this - manually clearing the folders quickly gets tedious.

You should then find your custom activity on the Action menu in SPD:






































    Note that the action category, in this case Site Management, shows up as you defined it in the Action element in your .actions4 file. You can then start plugging values into your activity and build a workflow around it.










    Update 17/11/14: a sample Visual Studio solution containing these workflow activities is now available for download.


    Comments

    1. Great work! Big thanks for that. Is there any possibility to download the solution with your Designer Actions?

      ReplyDelete
      Replies
      1. Hi Adrian - I'd like to make it available, just got to figure out the logistics. In the meantime, drop me a line (middle part of my domain name at gmail dot com) and I'll mail you a zipped up solution.

        Delete
    2. Can you explain how to pass the parameter of the activity?

      ReplyDelete
    3. Hi Mark - I'm not sure quite what you mean. You pass arguments to these custom activities in the same way you use any workflow activity in SharePoint Designer - by using the sentence designer. What you put in the Actions file determines how the sentence designer will behave.

      ReplyDelete
    4. How to trigger above custom workflow using CSOM ?

      ReplyDelete
    5. Hi Jason,

      I'm working through this example to create the site workflow custom action. I've nneded to set this up through an "App in SHarePoint" project as I do not have a local install of 2013. This is due to the solution being run in a SharePoiint 365 environment. I've managed to get as far as the actions4 file, but do not have this file available to edit. Am I missing something; does this not work when using an App for SharePoint solution?

      Thanks,

      Grant

      ReplyDelete
      Replies
      1. Hi Grant

        Yeah, unfortunately that's not going to work. If you created the custom action within an app it would be isolated to the app, you wouldn't be able to make it available in SharePoint Designer.

        Like you, I developed these workflow actions for an Office 365 deployment - but you do unfortunately still need a local dev environment to create this kind of thing.

        Delete
      2. I expected this was to be the answer. It's a little clunky, but will get an internal 2013 environment setup and develop against that.

        Thanks.

        Delete
    6. This comment has been removed by the author.

      ReplyDelete
    7. Hi,

      Great post, thankyou.

      This creates a subsite, can you possibly elaborate on how to create a new site collection using a workflow in SPD 2013? is there an endpoint similar to /_api/web/webinfos/add that can create site coll's?

      Thanks!

      ReplyDelete
      Replies
      1. Hi there. Unfortunately it's not possible, and that's by design. All the client-side APIs are made available within a site collection context - to create a site collection, you'd need access to the parent web application object, and you can't do that with any of the client APIs. In addition, SharePoint 2013 workflows run at the site scope - you can't create a farm-scoped or web app-scoped 2013 workflow. (The advantage being that SharePoint 2013 workflows will work in SharePoint Online as well as on premises.) If you've got an on-premises environment and you really want to automate site collection creation with a workflow, you might be able to do it by creating a custom-coded workflow in SharePoint 2010 mode (also supported by SharePoint 2013, but not by SharePoint Online). They're kind of clunky though as I remember.

        Delete
    8. Hi there. Is it possible to add link in quick launch for this created site workflow?

      ReplyDelete

    Post a Comment

    Popular posts from this blog

    Server-side activities have been updated

    The target principal name is incorrect. Cannot generate SSPI context.