Managing Microsoft Dynamics CRM 2013 access team membership using connections

Dynamics CRM 2013 includes a great new feature called access teams, which makes ad-hoc sharing of records much easier than in previous versions. The basic idea is that an administrator can create one or more team templates for an entity that function sort of like security roles, but for a specific record. When a user is assigned rights on a record via a team template, if a corresponding access team doesn't already exist, CRM will create one on the fly.

While access teams are a great feature from a security perspective, they don't have the same flexibility as connections for representing different relationships between particular records and users. For example, if you have complex sales opportunities that require multiple users to work together on opportunity-specific sales teams in different roles (team lead, proposal writer, researcher, for example), creating specific connection roles and then creating connections between opportunities and users is the best way to represent those relationships in Dynamics CRM.

Unfortunately, if you use both connections and access teams, the out-of-the-box Dynamics CRM interface will require that each be managed separately. In this post, I will show how you can tie connections and access team membership together using workflows and custom workflow activities so that access team membership is granted and revoked when connections are created and deleted.

The approach

In this example, I have a custom entity called "Important Something" (new_importantsomething). I have created two team templates called "Important Something - Manager" and "Important Something - Member." I have also created two connection roles for Important Somethings and Users. These roles are also called "Important Something - Manager" and "Important Something - Member." We will be creating one real-time workflow that executes when a connection record is created and another real-time workflow that executes when a connection record is deleted. These workflows will check whether the "connected to" role is one of the new "important something" roles, and then they will pass the connection details and the role name to a custom workflow activity to handle the user's addition to or removal from the specific access team. Although a single custom workflow activity could be created to do either the addition or removal based on an input parameter flag, in this example we'll be using two separate ones called AssignUserToAccessTeamByName and RemoveUserFromAccessTeamByName.

The custom workflow activities

To add a user to an access team, the AssignUserToAccessTeamByName custom workflow activity will need to execute an AddUserToRecordTeamRequest via the organization web service. This message requires three input parameters:

  1. The user id
  2. The entity record id
  3. The team template

The user id and the entity record id are both stored on the connection record, so the AssignUserToAccessTeamByName activity can get those via an EntityReference input parameter for the connection. The team template can be specified either as an EntityReference or as a string name value to look up the id in the activity method. I prefer the string method because I think it offers more flexibility for workflow design, but there is of course a slight performance hit from having to look up the template id by its name.

Here are the input parameters for the custom workflow activity:

[Input("Connection")]
[ReferenceTarget("connection")]
public InArgument<EntityReference> Connection { get; set; }
[Input("Team Template name")]
public InArgument<string> TeamTemplateName { get; set; }

Here's the code that executes the business logic:

Guid connectionId = Connection.Get(executionContext).Id;
Entity connection = service.Retrieve("connection", connectionId, new ColumnSet("record1id", "record2id"));
EntityReference connectedFromId = (EntityReference)connection["record1id"];
EntityReference connectedToId = (EntityReference)connection["record2id"];
//only run this if the connection is to a user record
if (connectedToId.LogicalName.ToUpper() == "SYSTEMUSER")
{
    Guid teamTemplateId;
    string teamTemplateName = TeamTemplateName.Get(executionContext);
    //look up team template by name
    QueryByAttribute querybyexpression = new QueryByAttribute("teamtemplate");
    querybyexpression.ColumnSet = new ColumnSet("teamtemplatename", "teamtemplateid");
    querybyexpression.Attributes.AddRange("teamtemplatename");
    querybyexpression.Values.AddRange(teamTemplateName);
    EntityCollection retrieved = service.RetrieveMultiple(querybyexpression);
    //if we find something, we're set
    if (retrieved.Entities.Count > 0)
    {
        teamTemplateId = retrieved.Entities[0].Id;
    }
    else
    {
        //throw exception if unable to find a matching template
        throw new Exception("could not find team template named: " + teamTemplateName);
    }
    AddUserToRecordTeamRequest teamAddRequest = new AddUserToRecordTeamRequest();
    teamAddRequest.Record = connectedFromId;
    teamAddRequest.SystemUserId = connectedToId.Id;
    teamAddRequest.TeamTemplateId = teamTemplateId;
    AddUserToRecordTeamResponse response = (AddUserToRecordTeamResponse)service.Execute(teamAddRequest);
}

One thing to note is that a connection record in CRM is actually two separate records (the to/from records are reversed in each), so the workflow will actually fire twice. There's no real problem with the workflow firing twice, but just to reduce unnecessary processing, you'll note that there's a check to make sure the custom workflow activity business logic only executes when a user is the "to" record.

The RemoveUserFromAccessTeamByName custom workflow activity is almost identical to the AssignUserToAccessTeamByName activity, except it executes a RemoveUserFromRecordTeamRequest instead of an AddUserToRecordTeamRequest. The parameters for each method are the same. Full code for each method is available here.

The workflows

As mentioned earlier, the workflows for adding and removing access team members first check to see if the created or deleted connection has a particular connection role, and then they executes their corresponding custom workflow activity. Here's a screenshot of the "Add access team member from connection" workflow:
workflow definition

And here's a screenshot of the custom step input properties for the AssignUserToAccessTeamByName activity:
input properties

And that's it. Now you can manage access team membership and connections in a way to best represent relationships and provide the right level of data access to your users. Are you using access teams yet? If not, do you plan to? Let us know in the comments.

A version of this post was originally published on the HP Enterprise Services Application Services blog.

comments powered by Disqus