Executing a Dynamics CRM WorkFlow from a Plugin

As part of a StateFlow engine I writing for a Microsoft Dynamics CRM 2011 project I needed to be able to run a series of custom workflow activities.  I needed the workflow activities to be driven off a configuration entity that would hold rules like, how often to run, what status reason to set a matching record to and so on.  One part that was a bit less than obvious was how to get the GUID for the workflow I wanted to execute in a search by “workflow name”.  I’m sure there are several ways to do this but I was surprised when I found you could query it just like any other entity.  Just be sure that you add a condition of “type”, ConditionOperator.Equal, 1 otherwise CRM will grab the unpublished version of the workflow and throw a error “workflow must be in published state”.

Code Snippets

The following is a code sample that calls two methods: GetProcessId and ExecuteWorkFlow before we can do so we need to get the ID of the workflow which is stored in the PrimaryEntityId attribute of the workflow context.

// Create the context

IWorkflowContext context = executionContext.GetExtension<IWorkflowContext>();

_primaryEntity = context.PrimaryEntityId;

Guid processId = new Guid(GetProcessId(service, “workflow”, “name”, “StateFlow”).Id.ToString());

ExecuteWorkFlow(service, processId, _primaryEntity);

private void ExecuteWorkFlow(IOrganizationService service, Guid workFlowId, Guid entityId)

{

try

{

// Create an ExecuteWorkflow request.

ExecuteWorkflowRequest request = new ExecuteWorkflowRequest()

{

WorkflowId = workFlowId,

EntityId = entityId

};

// Execute the workflow.

ExecuteWorkflowResponse response =

(ExecuteWorkflowResponse)service.Execute(request);

}

catch (Exception ex)

{

throw new InvalidPluginExecutionException();

}

}

 

 

private Entity GetProcessId(IOrganizationService service, string EntityLogicalName, string SearchFieldName, object SearchValue)

//Internal method used to search for entity records

{

EntityLogicalName = EntityLogicalName.ToLower();

SearchFieldName = SearchFieldName.ToLower();

QueryExpression query = new QueryExpression(EntityLogicalName);

query.ColumnSet = new ColumnSet(true);

query.Criteria.AddCondition(new ConditionExpression(SearchFieldName, ConditionOperator.Equal, SearchValue));

query.Criteria.AddCondition(new ConditionExpression(“type”, ConditionOperator.Equal, 1));

try

{

EntityCollection response = service.RetrieveMultiple(query);

if (response != null && response.Entities.Count > 0)

return response.Entities[0];

else { return null; }

}

catch (Exception ex)

{

//Do error hadling

throw ex;

}

}

Advertisements

Dynamics CRM 2011 Security Roles Best Practices

Security roles in Microsoft Dynamics CRM 2011 are deceptively simple in CRM.  Just click a few bubbles and you good.  Truth is they can become very difficult to manage if not setup and maintained properly.  Here are some best practices to keep in mind.

  • Never use the Out-Of-Box Security roles, rather always clone them.
  • Business Units are data security related and not a business hierarchy.
  • Security Roles are roles not job titles.
  • Limit sharing to a minimum. If you must share, share to teams.
  • Do not let anyone function as system administrator or system customizer roles. IT and Admins should log in with special accounts to make changes. Example CRM Admin account (Which would have System Administrative access)
  • Keep the number of security roles as minimal as is practical.
  • Use meaningful roll names.

 

The SQL below will give you a listing of each entity and role interaction and setting.

 

SELECT DISTINCT
                      FilteredRole.name, EntityView.PhysicalName AS [Entity Name],
                      CASE Privilege.AccessRight WHEN 1 THEN 'READ' WHEN 2 THEN 'WRITE' WHEN 4 THEN 'APPEND' WHEN 16 THEN 'APPENDTO' WHEN 32 THEN 'CREATE' WHEN 65536
                       THEN 'DELETE' WHEN 262144 THEN 'SHARE' WHEN 524288 THEN 'ASSIGN' END AS [Access Level],
                      CASE PrivilegeDepthMask WHEN 1 THEN 'User' WHEN 2 THEN 'Business Unit' WHEN 4 THEN 'Parent: Child Business Unit' WHEN 8 THEN 'Organisation' END AS [Security Level]
FROM         RolePrivileges INNER JOIN
                      FilteredRole ON RolePrivileges.RoleId = FilteredRole.roleid INNER JOIN
                      PrivilegeObjectTypeCodes ON RolePrivileges.PrivilegeId = PrivilegeObjectTypeCodes.PrivilegeId INNER JOIN
                      Privilege ON RolePrivileges.PrivilegeId = Privilege.PrivilegeId INNER JOIN
                      EntityView ON EntityView.ObjectTypeCode = PrivilegeObjectTypeCodes.ObjectTypeCode
WHERE     (FilteredRole.roletemplateid IS NULL)
ORDER BY FilteredRole.name, [Entity Name]