Sunday, February 5, 2017

ILMerge workflows Dynamics CRM

If you are creating plugins or workflow that have dependencies on third party libraries then there are two options:
1. Deploy the third party DLL in the GAC
2. ILMerge the thrid party DLL to your workflow or plugin.

Option 1 might seem much easier at first as you can simply go and install the DLL in the GAC. You have to do it once and forget it. However you'll have to do it in each of your enviornment like DEV, Test and Prod.   This eventually becomes a nightmare when you have a big farm, deploying dll's to each of the server in a farm is tedious and error prone, specially say you start referencing a new version of dll.

Option 2 is better approach. In this option you can merge the third party DLL to your dll and deploy your solution. All your dependencies are then in the solution file and you really don't care how big your server farm is.

To do ILMerge you have to download ILMerge from Microsoft. In your plugin or worfklow project, go to project settings and Build Events and In the Post-Build event put the following code, note you might have to change it according to your project structure. In the below example, I have a SolutionItems folder and I have copied the ILMerge tool to this folder. Note also that I am pointing to the location of .net v4.5.2 as ILMerge will require some of the dependencies of the framework. The libraries I am ILMerging are Microsoft.SharePoint.Client.dll and Microsoft.SharePoint.Client.Runtime.dll and I am outputing it to Workflow.dll which is my custom dll contianing workflow activities.

Yes I am merging 2 dlls with my workflow dll.
Note the double quotes as longer file paths and paths with space can break if quotes are not used.
The $(SolutionDir) etc are Visual studio macros, you can google them they are quite handy and using them means that if you have got a build system attached, you will not have to do anything extra.


"$(SolutionDir)$(SolutionName)\SolutionItems\ILMerge.exe"  /keyfile:"$(ProjectDir)key.snk" /ndebug:false /targetplatform:v4,"$(MSBuildProgramFiles32)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5.2" /target:"library" /copyattrs /out:Workflow.dll "$(TargetDir)Workflow.dll" "$(SolutionDir)$(SolutionName)\SolutionItems\Microsoft.SharePoint.Client.dll" "$(SolutionDir)$(SolutionName)\SolutionItems\Microsoft.SharePoint.Client.Runtime.dll"

It was quite tedious to work it out, but works beautifully.
Hope it saves someone some pain.

Sunday, December 13, 2015

Dynamics CRM report viewer problem in a load balanced (f5) environment

This is just a post which might help people who have deployed Dynamics CRM in a load balanced environment.

In our case, it was 3 front -ends and a report server also load balanced. CRM web site was up and running but when we tried to run any report we would get the following error. 404 WebResource.axd and ScriptResource.axd not found. On the CRM front end we received the error ": An error occurred processing a web or script resource request. The resource identifier failed to decrypt. "

The reason this was happening was that the CRM front end was not using sticky sessions and the request was not going to the same server every time.

To fix it on the F5 Big-IP we had to configure Persistance-type = Cookie which is aka sticky session.


Tuesday, November 12, 2013

Forefront Identity Manager (FIM) Custom Management Agent for CRM 2011 - Part 1

In this post I will share my experiences in developing a custom management agent for CRM 2011 in FIM. Since there is quite a bit to cover I have split this post into two parts, this being part 1.
Integration with FIM was quite a steep learning curve for me because I had little or not knowledge of how FIM works but after some playing around, reading articles, banging my head, i finally figured out and got something working. So to get started I will explain a little bit of what FIM does and what we are going to achieve by building a Management Agent for CRM 2011 in FIM.
FIM is the identity management solution from microsoft, but it does a whole lot of other stuff as well such as certificate management, self service password resets etc. It used to be called Identity Lifecycle manager and before that Microsoft Identity Integration server. It has four components
        • FIM Syncronization Service
        • FIM Service
        • FIM Portal 
        • FIM Certificate Management
I am not going to go into detail of all the components, I am just going to stick to what we really need for the purpose of creating a management agent for CRM 2011. You can read about the rest on MSDN and blogs etc. The FIM Synchronization Service is responsible for  passing identity information from one source to the other. This could be from a database such as Oracle  to AD, or from AD to other system such as the HR system or something else, in our case CRM 2011. FIM calls them connected data source or CDS. FIM Sync service can run by itself without the need for other components. To consume data from the CDS's FIM uses adapters which it calls Management Agents (MA). Some MA come pre packed with FIM such as for Active Directory, SQL, Flat Files, Oracle, SAP etc. What we don't have is a MA for CRM 2011, but this is quite straight forward to develop once you know how FIM works. FIM allows developers to create what it calls Extensible Connectivity Management Agent (ECMA) 2 which basically is some .net code that implements interfaces that FIM provides.

Implementation is good but understanding how FIM Sync service stores and pushes data between systems is in my mind critical to understanding how to create a Management Agent for CRM 2011. For this I refer you to these technet articles 1 , 2, 3 which explain the FIM Sync Service inner workings. After reading them you would know that FIM Sync service stores data in "Connector Space" and then pushes it to the "Metaverse". It is from the Metaverse that data is pushed to external system. In short inbound synchronization is populating authoritative data in the metaverse and outbound synchronization is populating from metaverse to external systems.
The msdn articles above also talk about FIM Service and Portal and use what is called declarative synchronization which is configured through the portal and FIM Service. I am going to show you  non-declarative synchronization in which we will be writing code.

What I wanted to achieve using FIM sync service was to automate the user creation process in CRM 2011, i.e. whenever a new user is created in Active Directory
  • it is added as a user in CRM 2011
  • put in appropriate business unit or team
  • get appropriate security roles
  • is disabled as soon as the the user is deleted in AD or account is disabled
This can be extended to whatever degree one wishes to, for example rules can be applied which can add / remove user to teams within CRM based on group membership in AD or SQL or some other system. The whole goal is automation and keeping all the systems in sync all the time without manual intervention.

Now that we have some background knowledge on FIM and our end goal, lets break down the steps that are involved.

  1. Create MA for Active Directory (this is out of box)
  2. Import Data (i.e. populate metaverse)
  3. Create custom MA for CRM
    1. Map attributes
  4. Create metaverse Rules extension


The first thing we have to do is create an MA for Active Directory. This will allow us to pull data from AD into the FIM sync service data base (i.e. the metaverse). Once the data is there we will be ready to export it to our external system aka CRM 2011.
To create a MA for AD, on the MA tab click "Create", This will bring up the "Create Management Agent" box.

Next type in the active directory details, in my case it is contoso.com.
Next select the AD containers, in my case, it is ECMA2, which is an OU that I created specifically for testing purposes, you could leave it empty and that would select everything.
In the next screen, select the AD objects, ensuring that you select "user".

In the next screen, you need to select the AD attributes as shown below. It is an extensive list of attributes so you really should know which ones are needed, in my case since it was just an example, I am selecting only a few attributes such as the sAMAccountName which corresponds to your AD login.

In the screen "Connector Filter" just hit next as we are not going to specify any filters. In the "Join and Projection Rules" project "user" as shown below.
In the "Configure Attribute flow" map the attributes, here you will be mapping the AD attributes to the metaverse attributes. Since we are only interested in users we will be mapping "user" attributes to metaverse's "person" attributes as shown below.
Click next and in the Deprovisioning just select "make them disconnectors". Finally click okay.
This will create your AD MA. Now we will move onto creating the custom (aka ECMA 2) MA for CRM 2011.

To create the CRM MA we need to create a c# project, assuming that you have installed the FIM syncronization service, all you need to do is, go to  "Actions" -> Create Extension Projects ->  Extensible Connectivity 2.0 Extension

This will bring up a box to provide name of the project and select the type of project and Visual Studio version that you want to use, just select 2010.  The project created will contain a class with all the interfaces commented out as shown below:
using System;
using System.IO;
using System.Xml;
using System.Text;
using System.Collections.Specialized;
using Microsoft.MetadirectoryServices;

namespace FimSync_Ezma
{
    public class EzmaExtension :
    //IMAExtensible2CallExport,
    //IMAExtensible2CallImport,
    //IMAExtensible2FileImport,
    //IMAExtensible2FileExport,
    //IMAExtensible2GetHierarchy,
    //IMAExtensible2GetSchema,
    //IMAExtensible2GetCapabilities,
    //IMAExtensible2GetParameters,
    //IMAExtensible2GetPartitions
    {
        //
        // Constructor
        //
        public EzmaExtension()
        {
            //
            // TODO: Add constructor logic here
            //
        }
    };
}


We will need to uncomment IMAExtensible2CallExport, IMAExtensible2GetSchema, IMAExtensible2GetCapabilities, IMAExtensible2GetParameters, next we will have to right click and get Visual Studio to implement these interfaces for us.
The remainder will be covered in part 2 where we will writing the acutal code to create users and also implement the metaverse rules extension to populate the metaverse.
Till next time, Happy CRMing!



Thursday, November 7, 2013

Embedding images in email, Dynamics CRM 2011 - Part 1

Today I am going to show you a way you can send emails with embedded images in CRM 2011 /2013 and for that matter Crm 4.0.
This is nothing new, you might say... images can be added by just copying the image which is available on a public URL and pasting it in the CRM email form and the image will appear.
If you thought this then you are definitely correct. However what if you had a requirement where the images could not be put on a public URL ( or CDN), how would you allow images then.
Well as a smart CRM developer you would say, mail merge? or have the email contain a document as attachment with the image, etc. etc. There are many ways it can be done.

Okay enough of the daddy talk, let me tell you another way which many of you probably already knew. Lets revisit emails and more importantly the MIME content disposition specification which details how email attachments are constructed and which governs how the email clients deal with your email. Essentially what this will tell you is that you can embed images inside the email if you create the attachment as "inline" and reference it is your email body.
To achieve in CRM, we will somehow need to interact with our outgoing email just before it is submitted to the SMTP server for delivery. We will have to convert our images to "inline" attachments and reference them in the email body so that on the other side they come out as email which contain images.

Now the fun part, To implement this we won't be doing adding anything extra to our CRM email form, we will still put the image in the email body as described above - copy -paste, but this time our image is no longer required to be on the a public URL or CDN, it can remain in our internal network. Our code will grab it form our internal network, convert it into a MIME inline attachment and reference it in the email body.

But where and how will we do this you ask? Well the plain old router, yes its the same router that we are all too used to, this time we will be extending the SMTP provider, specifically we will be creating a class that implements  SmtpPollingSendEmailProvider. We have to do this inorder to construct the email ourselves, replace the img tag with the equivalent mime attachment reference and also convert the image to an inline mime attachment.
We will override the Run method, grab the email that needs to be sent and use System.Net.Mail to construct an email, along with the attachment which we will read from the <img src=url>  and then send it through our configured smtp server.

That's it voila! email with embedded image.
In the next part I will share the code along with steps to extend the router to achieve this.

Happy CRMing.

Disclaimer: This method of embedding images is probably not the best performing option, your first choice should always be images that are on a CDN.


Tuesday, May 21, 2013

CRM 2011 Installation files for Windows server 2012

Installation of Crm 2011 on windows server 2012 is now fully supported after the release of rollup 13. The only thing you need is the updated installation files and patch for Windows Server 2012.
The updated installation file can be downloaded from http://www.microsoft.com/en-us/download/details.aspx?id=27822 this was published on 1/20/2012.
Next you will need the patch for Windows Server 2012,This will be downloaded automatically if you select Get Updates during the installation, however in case your server does not have internet access then you will have to manually download the files. These can be hard to find as I discovered but looking hard enough you can figure it out.
Just to save people time  here is the link to the updated installation files. Go to the Microsoft Update catalogue site http://catalog.update.microsoft.com/v7/site/home.aspx
Do a search for CRM.

After downloading the update patch files, you will need to copy them to a location on your server.  Note you only need to copy the file relevant to you language in English's case this is 1033. After than you will need to start the installer using command line and specify the location of the patch files in the xml element.
<Patch update="true">\\vmware-host\Shared Folders\Downloads\crm patch\Server_KB2434455_amd64_1033.msp</Patch>

Now when you run the installation from the command line using the XML config file, you will be easily able to install CRM on Windows server 2012 and SQL Server 2012.
Hope this saves some time.

In the next post I will shed some light on using CRM 2011 on windows server 2012

Happy CRMing!

Tuesday, April 17, 2012

Changing the reply email address to the queue's email address when replying from a queue in crm 2011

The problem was that we have multiple queues with email addresses in crm 2011. When an email arrives it goes to a particular queue depending on the address that was used. Now what happens when you try to reply to that email? Well nothing much only that the "FROM address" lookup defaults to the currently logged on user instead of defaulting to the queue's email address.
There are various ways you can solve this issue by writing plugin or workflow custom activity etc, but the crux of the matter is that the crm user replying from a queue needs to the see what the from email address is.
This can be best achieved by using Java script.
Please note that the code below is an unsupported customization, but it works.
function CheckEnquiryReplyAddress() {
 // Only complete this validate on Create Form
 var formType = Xrm.Page.ui.getFormType();
 var emailStatus = GetAttributeValue("statecode");
                    var emailDirection = GetAttributeValue("directioncode");



 if (formType == 1 || (formType == 2 && emailStatus == "Open")) {  
                                             
                                       
  if (emailDirection == "1"){
                                                              var previousEmailId=getExtraqsParam("_InReplyToId", window.parent.location.search);

   //getting context from the parent window
   var context = Xrm.Page.context;

   try {
    var serverUrl = context.getServerUrl();
    //The XRM OData end-point
    var ODATA_ENDPOINT = "/XRMServices/2011/OrganizationData.svc";
                                                                                     var query="/EmailSet?$select=ActivityId,ActivityTypeCode,DirectionCode,";
                                                                                     query=query+"ToRecipients,Email_QueueItem/QueueId&$expand=Email_QueueItem&$filter=ActivityId eq guid'" + previousEmailId +"'";
    query =serverUrl+ODATA_ENDPOINT+ query;

    var request= new XMLHttpRequest();
    request.open("GET", query, false);
    request.setRequestHeader("Accept", "application/json");
    request.setRequestHeader("Content-Type", "application/json; charset=utf-8"); 
    request.onreadystatechange=function(){ CompleteEnquiryReplyCheck(request,serverUrl);}
    request.send(null);
   }
   catch(e) {
    alert(e.Description);
   }
  }
 }

          
}
function CompleteEnquiryReplyCheck(request,url)
{
 if (request.readyState==4) {
  if(request.status==200) {
   var queue=JSON.parse(request.responseText).d.results[0];
   
   if (queue != null) {
    var queueId = queue.Email_QueueItem.results[0].QueueId.Id;
    var lookup = new Array();
      var lookupItem = new Object();
  
    lookupItem.id = queueId;
    lookupItem.name = queue.Email_QueueItem.results[0].QueueId.Name;
    lookupItem.typename = "queue";
     
    lookup[0] = lookupItem;
  
    Xrm.Page.getAttribute("from").setValue(lookup);
   }
  }
    }
}
The key here is the _InReplyToId query string parameter from here we are able to query the oData service and get the "To" email address of the original email and then it is simply a matter of putting the "To" address in the "From" lookup. Also note that I am checking that this is an outgoing email. Find the code for getExtraqsParam here. Happy Crming!.

Monday, April 16, 2012

Executing CRM 2011 workflow via Javascript

To execute a workflow we have to use the soap web service. We cannot use oData service. So we have to create a soap envelope and post it to the organization web service. The following function will execute the workflow.
function ExecuteWorkFlow(entityId,workflowId,url)
{

    var OrgServicePath = "/XRMServices/2011/Organization.svc/web";
    url = url + OrgServicePath;
    var request;
    request = "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\">" +
  "<s:Body>" +
    "<Execute xmlns=\"http://schemas.microsoft.com/xrm/2011/Contracts/Services\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">"+
      "<request i:type=\"b:ExecuteWorkflowRequest\" xmlns:a=\"http://schemas.microsoft.com/xrm/2011/Contracts\" xmlns:b=\"http://schemas.microsoft.com/crm/2011/Contracts\">"+
        "<a:Parameters xmlns:c=\"http://schemas.datacontract.org/2004/07/System.Collections.Generic\">"+
          "<a:KeyValuePairOfstringanyType>"+
            "<c:key>EntityId</c:key>"+
            "<c:value i:type=\"d:guid\" xmlns:d=\"http://schemas.microsoft.com/2003/10/Serialization/\">"+ entityId+"</c:value>"+
          "</a:KeyValuePairOfstringanyType>"+
          "<a:KeyValuePairOfstringanyType>"+
            "<c:key>WorkflowId</c:key>"+
            "<c:value i:type=\"d:guid\" xmlns:d=\"http://schemas.microsoft.com/2003/10/Serialization/\">"+ workflowId +"</c:value>"+
          "</a:KeyValuePairOfstringanyType>"+
        "</a:Parameters>"+
        "<a:RequestId i:nil=\"true\" />"+
        "<a:RequestName>ExecuteWorkflow</a:RequestName>"+
      "</request>"+
    "</Execute>"+
  "</s:Body>"+
"</s:Envelope>";

  var req = new XMLHttpRequest();
  req.open("POST", url, true)
  // Responses will return XML. It isn't possible to return JSON.
  req.setRequestHeader("Accept", "application/xml, text/xml, */*");
  req.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
  req.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Execute");
  req.onreadystatechange = function () { assignResponse(req); };
  req.send(request);
  

}

function assignResponse(req) {
if (req.readyState == 4) {
    if (req.status == 200) {
        alert('successfully executed the workflow');
   }
   }
}

Happying Coding!