SharePoint 2010 Custom Workflow Actions for SP Designer

I already build up an easy workflow for SharePoint with Visual Studio, but i never did develope a custom SharePoint Designer workflow action. But now i got a requirement in a project in which we need to provide the possibility to convert an office document to pdf and store it in a completely different Site Collection.

Cause this part of action will be needed some more often, we decided to build up a custom SharePoint Designer Worklfow action. We had a SharePoint 2013 platform but no workflow manager in this farm. So the custom action is based on SharePoint 2010 workflow platform. The other great thing of custom SPD Workflow actions is that you can update them more easily than workflows itself. You do not need to update the whole workflow. You only need to update them if you change the .action file (you see later what it’s all about) and you can use them in every SharePoint Site you have.

So what is this? A SharePoint Designer Workflow Action is a piece of code, which will be executed inside the workflow. It can use parameters and variables. You know it from SharePoint Designer if you build workflows. You’ll find those actions in the ribbon and use it to define the steps a workflow should do.


Let me show you how we succeded using this cool feature.

At first we create an empty SharePoint 201x Solution (depends on your dev machine).

We add these references:



Now we add a mapped folder to this path:


In this path we later add our worklow custom action definition.

Now we create a new class file in your solution which contains the logic and the code for your custom SP Designer workflow action.


It should have those usings:

using Microsoft.SharePoint.Workflow;
using Microsoft.SharePoint.WorkflowActions;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Compiler;

The class should inherit from Activtity.

After that you define the parameters. There are some which need to be set by the workflow author, so that the custom action is more flexibel and can be used by almoste every workflow. We see them later in the action file again. After defining the properties we build the method which cares about the executive code. So here is the logic of your custom action.

namespace aspSPDActivity
public class CopyFile : Activity
public static DependencyProperty SiteUrlProperty = DependencyProperty.Register("SiteUrl", typeof(string), typeof(CopyFile), new PropertyMetadata(""));
public string SiteUrl
get { return ((string)(base.GetValue(CopyFile.SiteUrlProperty))); }
set { base.SetValue(CopyFile.SiteUrlProperty, value); }


public static DependencyProperty ListNameProperty = DependencyProperty.Register("ListName", typeof(string), typeof(CopyFile), new PropertyMetadata(""));
public string ListName
get { return ((string)(base.GetValue(CopyFile.ListNameProperty))); }
set { base.SetValue(CopyFile.ListNameProperty, value); }


public static DependencyProperty ListUrlProperty = DependencyProperty.Register("ListUrl", typeof(string), typeof(CopyFile), new PropertyMetadata(""));
public string ListUrl
get { return ((string)(base.GetValue(CopyFile.ListUrlProperty))); }
set { base.SetValue(CopyFile.ListUrlProperty, value); }


public static DependencyProperty __ActivationPropertiesProperty = DependencyProperty.Register("__ActivationProperties", typeof(Microsoft.SharePoint.Workflow.SPWorkflowActivationProperties), typeof(CopyFile));
public Microsoft.SharePoint.Workflow.SPWorkflowActivationProperties __ActivationProperties
return (Microsoft.SharePoint.Workflow.SPWorkflowActivationProperties)base.GetValue(CopyFile.__ActivationPropertiesProperty);
base.SetValue(CopyFile.__ActivationPropertiesProperty, value);

public static DependencyProperty __ContextProperty = DependencyProperty.Register("__Context", typeof(WorkflowContext), typeof(CopyFile));
public WorkflowContext __Context
get { return (WorkflowContext)this.GetValue(__ContextProperty); }
set { this.SetValue(__ContextProperty, value); }

public static DependencyProperty __ListIdProperty = DependencyProperty.Register("__ListId", typeof(string), typeof(CopyFile));
public string __ListId
return ((string)(base.GetValue(CopyFile.__ListIdProperty)));
base.SetValue(CopyFile.__ListIdProperty, value);

protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
ISharePointService wfService = executionContext.GetService<isharepointservice>();
//Your code
wfService.LogToHistoryList(executionContext.ContextGuid, SPWorkflowHistoryEventType.WorkflowComment, 0, TimeSpan.Zero, "Information", "Operation completed", string.Empty);
catch (Exception fx)
wfService.LogToHistoryList(executionContext.ContextGuid, SPWorkflowHistoryEventType.WorkflowComment, 0, TimeSpan.Zero, "Error", "Operation stopped: " + fx.Message + " ", string.Empty);

return ActivityExecutionStatus.Faulting;
return ActivityExecutionStatus.Closed;



As you can see i added in the Execute Method the following:
ISharePointService wfService = executionContext.GetService</isharepointservice><isharepointservice>();

This is a possibility to log information in your activity. In the catch area you can see that i am logging the error. You can also add do some logging in the try block and bring some information about the operations.

In order to bring this custom workflow action to life, we need to define a so called .actions file, which basically is a xml-structured file to define the action name, description, classnames and the input und and output. That is necessary for the use of it in SharePoint Designer.

[sourcecode language="csharp"]

< ?xml version="1.0" encoding="utf-8" ?>
<action Name="Copy File"
Assembly="aspSPDActivity, Version=, Culture=neutral, PublicKeyToken=bab37e6505ee834d"
Category="asp Actions">
<ruledesigner Sentence="Convert Word to PDF and store it to Site = %1 in Library named %2 ">
<fieldbind Field="SiteUrl" DesignerType="Hyperlink" Id="1" Text="Site Url"></fieldbind>
<fieldbind Field="ListName" DesignerType="Textarea" Id="2" Text="Library Display Name"></fieldbind>
<parameter Name="__Context" Type="Microsoft.SharePoint.WorkflowActions.WorkflowContext" Direction="Optional"></parameter>
<parameter Name="__ListId" Type="System.String, mscorlib" Direction="Optional"></parameter>
<parameter Name="__ItemID" Type="System.Int32, mscorlib" Direction="Optional"></parameter>
<parameter Name="SiteUrl" Type="System.String, mscorlib"  Direction="Optional"></parameter>
<parameter Name="ListName" Type="System.String, mscorlib"  Direction="Optional"></parameter>
<parameter Name="ListUrl" Type="System.String, mscorlib"  Direction="In"></parameter>
<parameter Name="__ActivationProperties" Type="Microsoft.SharePoint.Workflow.SPWorkflowActivationProperties, Microsoft.SharePoint" Direction="Out"></parameter>

Some explanations to this. XXXXXXXXXXXXXXXXXXXX

Well, at last point we need to add an assembly entry to the web.config of your Sharepoint Server. But we can do it by using a feature event receiver, so in this code we will be flexibel and can use relative paths.

Add a feature to your solution and change the scope to webapplication. Add a feature event receiver to it and use this code for adding it the webconfig.

Notice: Please test the following webconfigmodification in your dev system, cause it can have side effects to other parts of your web.config.

protected string assembly = "aspSPDActivity.CopyFile, Version=, Culture=neutral, PublicKeyToken=bab37e6505ee834d";
protected string namespace_ = "aspSPDActivity";

public override void FeatureActivated(SPFeatureReceiverProperties properties)
SPWebApplication currentWebApp = (SPWebApplication)properties.Feature.Parent;
AddAuthorizedType(currentWebApp, assembly, namespace_);
private void AddAuthorizedType(SPWebApplication webApplication, string assembly, string namespace_)
var modification = new SPWebConfigModification();
modification.Path = "configuration/System.Workflow.ComponentModel.WorkflowCompiler/authorizedTypes/targetFx";
modification.Name = "PublishFiles";
modification.Sequence = 0;
modification.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;
modification.Value = string.Format("<authorizedtype Assembly='{0}' Namespace='{1}' TypeName='*' Authorized='True'></authorizedtype>", assembly, namespace_);

You might notice a difference. The node <targetfx> exists since  SP2013. That means if you want to deploy it to SP2010 you should keep this in mind. Cause we working on a SharePoint 2013 platform using the workflow 2010 platform we have to put our entry inside of <targetfx>. If it is outside it won’t work. It would throw an error which says that it could found the namespace or type of assembly. If you like you could add a relay which decides automatically on which platform it is and how to add the assembly.

After deployment it should look like this in SPD:


So, now it’s your turn, to put some extra stuff to your SharePoint Designer Workflow actions:) Some ideas might be to copy files from one site collection to another or to convert documents to pdf, or anything you need.

The article or information provided here represents completely my own personal view & thought. It is recommended to test the content or scripts of the site in the lab, before making use in the production environment & use it completely at your own risk. The articles, scripts, suggestions or tricks published on the site are provided AS-IS with no warranties or guarantees and confers no rights.

About Karsten Schneider 308 Articles
Consultant for Microsoft 365 Applications with a strong focus in Teams, SharePoint Online, OneDrive for Business as well as PowerPlatform with PowerApps, Flow and PowerBI. I provide Workshops for Governance & Security in Office 365 and Development of Solutions in the area of Collaboration and Teamwork based on Microsoft 365 and Azure Cloud Solutions. In his free time he tries to collect tipps and worthy experience in this blog.

1 Comment

1 Trackback / Pingback

  1. Lessons Learned: SharePoint Designer Custom Activities | time is .net

Leave a Reply

Your email address will not be published.