Tuesday, October 23, 2018

Extracting attachments from an incoming email, create case, and add attachments to as notes

Problem

One of my client's using Dynamics CRM Online 2016 Update had a requirements to create Case (renamed to Enquiry) records when an email arrives to their info@*****.com mailbox. Using OOTB Automatic Record Creation and Update Rules feature a Case is created and the email is visible under Activities.
Our client felt that the user experience wasn't great and the original Email was hard to find with all the other activities got created afterwards.

Solution

The solution was to extract the attachments from the email message and create notes against the Case record during the case creation process. Below are the steps used to implement the solution.
  1. Create a custom workflow activity to extract the attachments and create notes
  2. Create a workflow which includes the above custom workflow activity as a Step
  3. Create an Automatic Record Creation and Update Rule
  4. Specify Record Creation and Update Details which calls the workflow created in step 2

Implementation

Create a custom workflow activity to extract the attachments and create notes

Below is the source code of the custom workflow activity.
using System;
using System.Activities;
using System.Linq;

using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Xrm.Sdk.Workflow;

namespace Dyn365Apps.CRM.Workflow
{
    public class ExtractAttachmentsFromEmailAndCreateNotes : CodeActivity
    {
        [RequiredArgument]
        [Input("Email")]
        [ReferenceTarget("email")]
        public InArgument<EntityReference> receivedEmail { get; set; }

        [RequiredArgument]
        [Input("Enquiry")]
        [ReferenceTarget("incident")]
        public InArgument<EntityReference> enquiry { get; set; }

        protected override void Execute(CodeActivityContext context)
        {
            var trace = context.GetExtension<ITracingService>();

            try
            {
                var serviceFactory = context.GetExtension<IOrganizationServiceFactory>();
                var service = serviceFactory.CreateOrganizationService(Guid.Empty); //Use current user's IDif (service != null)
                {
                    trace.Trace("Organization Service Created");
                }

                // Get Attachment Count
                trace.Trace("Get Attachment Count");
                var rem = receivedEmail.Get(context);
                Entity email = service.Retrieve(rem.LogicalName, rem.Id, new ColumnSet("attachmentcount"));
                int atc = (int)email["attachmentcount"];
                trace.Trace("Attachment count = " + atc.ToString());

                if (atc > 0)
                {
                    // Get all attachments
                    QueryExpression queryAtt = new QueryExpression("activitymimeattachment");
                    queryAtt.ColumnSet = new ColumnSet(newstring[] { "activityid", "attachmentid", "filename", "body", "mimetype", "subject" });
                    queryAtt.Criteria = new FilterExpression();
                    queryAtt.Criteria.FilterOperator = LogicalOperator.And;
                    queryAtt.Criteria.AddCondition(new ConditionExpression("activityid", ConditionOperator.Equal, email.Id));
                    EntityCollection eatt = service.RetrieveMultiple(queryAtt);
                    var entities = eatt.Entities;

                    trace.Trace("Entities count = " + entities.Count());

                    foreach (var ent in entities)
                    {                        
                        trace.Trace("Inside the for loop");
                        trace.Trace("Attributes count = " + ent.Attributes.Count());

                        // Instantiate an Annotation object.
                        Entity annotation = new Entity("annotation");

                        if (ent.Attributes.Contains("subject"))
                        {
                            trace.Trace("subject = " + ent.Attributes["subject"].ToString());
                            annotation["subject"] = ent.Attributes["subject"].ToString();
                        }
                        else
                        {
                            trace.Trace("subject not found");
                            annotation["subject"] = "Undefined";
                        }
                        
                        if(ent.Attributes.Contains("filename"))
                        {
                            trace.Trace("filename = " + ent.Attributes["filename"].ToString());
                            annotation["filename"] = ent.Attributes["filename"].ToString();
                        }
                        else
                        {
                            trace.Trace("filename not found");
                            annotation["filename"] = "Undefined.txt";
                        }

                        if (ent.Attributes.Contains("mimetype"))
                        {
                            trace.Trace("mimetype = " + ent.Attributes["mimetype"].ToString());
                            annotation["mimetype"] = ent.Attributes["mimetype"].ToString();
                        }
                        else
                        {
                            trace.Trace("mimetype not found");
                            annotation["mimetype"] = "plain/text";
                        }

                        if (ent.Attributes.Contains("body"))
                        {
                            annotation["documentbody"] = ent.Attributes["body"];
                        }
                        
                        trace.Trace("objectid = " + enquiry.Get(context).Id.ToString());
                        
                        annotation["objectid"] = enquiry.Get(context);
                        annotation["objecttypecode"] = 112; // Case// Create a Note with the attachment
                        service.Create(annotation);
                    }
                }
            }
            catch (Exception ex)
            {
                trace.Trace("ex.Message = {0}", ex.Message);
                trace.Trace("ex.StackTrace = {0}", ex.StackTrace);
            }
        }
    }
}

Create a workflow which includes the above custom workflow activity as a Step

  • Create a workflow and name it Extract Attachments from Email - Create Notes - Attach to Enquiry
  • Set the Entity as Email
  • Enable workflow to run as a child process.
  • Add the custom workflow activity ExtractAttachmentsFromEmailAndCreateNotes as a step.
  • Custom workflow activity requires two inputs
  1. Email
  2. Case (Enquiry)
  • Activate the workflow.

Create an Automatic Record Creation and Update Rule

  • Navigate to Settings > Service Management and click on Automatic Record Creation and Update Rules.
  • Create a new Record Creation and Update Rule.

Specify Record Creation and Update Details which calls the workflow created in step 2

  • Create a new Record Creation and Update Details record.
  • Configure the conditions and actions.
  • Under Specify Other Actions, add a new step to Start a Child Workflow.
  • Select the Workflow created in Step 1
That's it (Assumption: The mailbox is configured to receive emails).
Now send an email with attachments to the mailbox.
The attachments in the Email Message are now under Case record's Notes section.

Enabling Auditing to monitor User's Security Role changes

I got an call from a client who recently went live with a system we built. Some of their user's security roles were getting removed and they wanted to find out what's going on.
Dynamics 365 (CRM) can record association and disassociation of security roles of a user as part of the OOTB Auditing capability.
Here are the instructions I provided my client to resolve the issue.

Enable Auditing

First step is to make sure Auditing feature is still enabled as we have configured.
Navigate to Settings > Administration > System Settings
Click Auditing Tab
Under Audit Settings, tick the Start Auditing checkbox
If you would like to monitor user access, tick Audit user access checkbox as well.
Click OK.

Enable Auditing for Security Roles

Next step is to make sure Auditing is enabled for Security Roles entity.
Navigate to Settings > Customizations > Customize the System
New Window opens
Expand Entities from the left pane
Click on Security Roles entity (Please wait for few seconds for it to load)
Tick Auditing checkbox.
Click Save.
Click Publish.

Viewing Audit Logs

Finally, to view the audit logs,
navigate to Settings > Auditing > Audit Summary View
You’ll see the list of audit entries
Double click to open the record to see more details.
That's it. 

Get files of last hour in Azure Data Factory

  Case I have a Data Factory pipeline that should run each hour and collect all new files added to the data lake since the last run. What is...