Registering CRM plugin steps programmatically

Yesterday I was faced with the challenge of having to register plugin steps with code. I know it’s not a very common task but it happens. I guess the most common case is to have some reusable solution that includes plugins that can run on arbitrary entities that are unknown when building the solution.

Say for example a sequence number solution. You build some generic plugin that can get a sequence number from somewhere and set a property of an entity with the sequence number. You build some clever configuration system so the plugin doesn’t have to be hard coded to specific entities/fields. You can deploy your solution, but you still have to manually create the steps for the plugins so it runs when the correct messages are executed on the correct entities. Say on create of an account.

In the example above it would be nice to be able to add the plugin steps without the use of the plugin registration tool. A configuration page in Silverlight or html5 maybe? Or a small console application or PowerShell CmdLet? Whatever suits you – I personally prefer PowerShell.

Anyway, the web is not exactly full of examples, and certainly not for the 2011/2013 SDK. In this regard (and many others) the 2011 and 1013 sdk are identical. Pre 2011 you had special SDK messages, but now plugin steps act like entities. All you have to do is to insert the correct data in the SdkMessageProcessingStep entity.

First you get a few enumerators to help us out:


enum CrmPluginStepDeployment
{
   ServerOnly = 0,
   OfflineOnly = 1,
   Both = 2
}

enum CrmPluginStepMode
{
   Asynchronous = 1,
   Synchronous = 0
}

enum CrmPluginStepStage
{
   PreValidation = 10,
   PreOperation = 20,
   PostOperation = 40,
}

enum SdkMessageName
{
   Create,
   Update,
   Delete,
   Retrieve,
   Assign,
   GrantAccess,
   ModifyAccess,
   RetrieveMultiple,
   RetrievePrincipalAccess,
   RetrieveSharedPrincipalsAndAccess,
   RevokeAccess,
   SetState,
   SetStateDynamicEntity,
}

I guess the enums are pretty much self-explanatory.

Next we need to define some variables. How you set them depends on your choice of technology; text fields on a web page, args to a console app, parameters of a PowerShell CmdLet etc.

var AssemblyName = "MyCompany.MyProject.Plugins";
var PluginTypeName = "MyCompany.MyProject.Plugins.AssignSequenceNumber";
var CrmPluginStepStage = CrmPluginStepStage.PreOperation;
var EntityName = "account";
var StepName = "Assign sequence number to newly create accounts";
var Rank = 1;
var SdkMessageName = SdkMessageName.Create;

You need to have a plugin assembly registered that matches the namespace above, and you need to initiate the service object with an IOrganizationService. Use a Simplified Connection for that.

Now you can create the SdkMessageProcessingStep with the following snippet.

SdkMessageProcessingStep step = newSdkMessageProcessingStep
{
   AsyncAutoDelete = false,
   Mode = newOptionSetValue((int)CrmPluginStepMode.Synchronous),
   Name = StepName,
   EventHandler = newEntityReference("plugintype", GetPluginTypeId()),
   Rank = Rank,
   SdkMessageId = newEntityReference("sdkmessage", GetMessageId()),
   Stage = newOptionSetValue((int)CrmPluginStepStage),
   SupportedDeployment = newOptionSetValue((int)CrmPluginStepDeployment.ServerOnly),
   SdkMessageFilterId = newEntityReference("sdkmessagefilter", GetSdkMessageFilterId())
};
OrganizationService.Create(step);

As you can tell, you need to look up a few values in CRM for the code above to work. Here are the helper methods you need.

private Guid GetSdkMessageFilterId()
{
   using (CrmServiceContext context = newCrmServiceContext(OrganizationService))
   {
      var sdkMessageFilters = from s in context.SdkMessageFilterSet
      where s.PrimaryObjectTypeCode == EntityName
      where s.SdkMessageId.Id == GetMessageId()
      select s;
      return sdkMessageFilters.First().Id;
   }
}

private Guid GetMessageId()
{
   using (CrmServiceContext context = newCrmServiceContext(OrganizationService))
   {
      var sdkMessages = from s in context.SdkMessageSet
      where s.Name == SdkMessageName.ToString()
      select s;
      return sdkMessages.First().Id;
   }
}

private Guid GetPluginTypeId()
{
   using (CrmServiceContext context = newCrmServiceContext(OrganizationService))
   {
      var pluginAssemblies = from p in context.PluginAssemblySet
      where p.Name == AssemblyName
      select p;
      Guid assemblyId = pluginAssemblies.First().Id;
      var pluginTypes = from p in context.PluginTypeSet
      where p.PluginAssemblyId.Id == assemblyId
      where p.TypeName == PluginTypeName
      select p;
      return pluginTypes.First().Id;
   }
}

The code a little simple – it doesn’t support registration of images for example. But it works and will get you started. I hope it helps, figuring this out took me a while. I hope that my examples make it a little easier.