Monday, March 7, 2011

BizTalk 2010: Calling Dynamics CRM 4.0 Web Services

The project that I am on ran into a critical and somewhat embarrassing situation this past week.  As we moved from a single node project environment into a multi-node test environment we discovered that the Dynamics 4.0 Adapter cannot be installed on multiple BizTalk Servers in the same group.  Yes – you read that correctly.  I have never heard of this kind of limitation and struggle with the idea the adapter was actually made available without this feature.  You can read more about this limitation in the FAQ section here.

With this in mind, we needed to quickly make a 180 degree turn.  I had heard of other people calling the ASMX CRM Web Services from BizTalk via WCF Adapter.  The process of consuming these ASMX services is much like consuming any other Web Service.  That is until you start your application and get the dreaded 401 Unauthorized error. 

 

System.ServiceModel.Security.MessageSecurityException: The HTTP request is unauthorized with client authentication scheme 'Negotiate'. The authentication header received from the server was 'Negotiate,NTLM'. ---> System.Net.WebException: The remote server returned an error: (401) Unauthorized

This seems like a pretty common error within the BizTalk and CRM forums.  However, I was not able find any resolutions to the issue.  It wasn’t until I started stepping through a .NET SDK sample that I realized that I was missing something and that was a required SOAP header.  For your convenience I have built an end to end sample that will walk through creating a Lead within a Dynamics 4.0 system. Keep an eye open for the Message Assignment shape where I assign this header.

  • Once we have created a BizTalk project we need to “Add Generated Items” and then select “Consume WCF Service”

image

  • Even though Dynamics 4.0 still uses ASMX style Web Services we can select “Metadata Exchange (MEX) endpoint.

image

image

  • Once the WSDL definition has been loaded, click the “Next” button to continue.
  • Click “Import” to create your schemas and sample Orchestration.
  • You should find that the following artifacts have been added to your BizTalk Solution.

image

  • Our “main” schema is called CrmService_schemas_microsoft_com_crm_2007_WebServices.xsd.  Within this schema you will find all of the available Actions (Create/Update/Delete/Fetch etc) and the related entities (both custom and out of box).  In this scenario we are going to create a “Lead” within our CRM system.  So we can find the “Create” node and then scroll down and find the “Lead” entity.

image 

image

  • To keep things simple, I have created a little helper message that I will use in a Map to create an instance of this CRM Create message.

image

  • Inside my map, I will simply map from this helper message to the actual CRM Request.

image

  • With the building blocks in place, the next step was to update the  Orchestration that was generated for us.  Pretty standard stuff going on here.  We want to receive an instance of our “helper” message, transform it, send the request to CRM and then write the response to disk. 

image

  • When we generated our CRM schemas, BizTalk also created a Port Type called “CrmServiceSoap” that contains all of the available operations including “Create”.

image

  • Earlier in this post I mentioned that we need to set a SOAP header on the CRM Request message.  In order to do this we want to add a Message Assignment shape after our Transformation shape.
    image
  • Within this shape we want to set the following:

msgCreateCustomer (WCF.OutboundCustomHeaders) = "<headers><CrmAuthenticationToken xmlns=\"http://schemas.microsoft.com/crm/2007/WebServices\"><AuthenticationType xmlns=\"http://schemas.microsoft.com/crm/2007/CoreTypes\">0</AuthenticationType><OrganizationName xmlns=\"http://schemas.microsoft.com/crm/2007/CoreTypes\">YourOrganization</OrganizationName><CallerId xmlns=\"http://schemas.microsoft.com/crm/2007/CoreTypes\">00000000-0000-0000-0000-000000000000</CallerId></CrmAuthenticationToken></headers>";

  • The two important settings are the “AuthenticationType” and the “OrganizationName” properties.    The Authentication property can take one of three values:
    • 0 – AD
    • 1 – Passport
    • 2 – Internet Facing Deployment
  • Since this is an on-premise installation we will go with 0.
  • The “OrganizationName” property becomes extremely important in multi-tenant configurations where you may have multiple organizations or departments sharing a CRM implementation.  Since we only have 1 organization in our deployment it is safe to just set our default value.
  • It is now time to deploy and configure the BizTalk application.
  • When BizTalk generated our CRM schemas and sample Orchestration, it also generated a Binding file.  When we import the binding file we will discover that two send ports have been created.  The first is a basicHttpBinding and the second send port uses a Custom Adapter with a Custom binding.  What is the difference?  Really it comes down to some addition security configuration that is available with the Custom adapter.

image

  • Using these binding as is didn’t work for me.  I needed to change the WCF-Custom Adapter to use the basicHttpBinding.  I also had to make a few Security related changes including setting the Security “mode” was set to “TransportCredentialOnly”

image

  • I also needed to set the “clientCredentialType” to “Windows”

image

  • I wasn’t quite done here.  I also need to add an Endpoint Behavior.  In this case it was a “clientCredentials” behavior.  Within this behavior, I set “Windows” - “allowedImpersonation” = “Impersonation”

image

  • At this point, I am ready to configure the rest of the application and start it.  Once started, I can process my sample helper message.
  • Within my sample file, I have provided the following information.  Once processed by BizTalk, I expect this Lead to be created within our CRM system and the CRM response to be send to disk.

image

  • The response from CRM is a GUID.

image

  • Once we launch the Dynamics CRM GUI, I am able to find my Barry Sanders record. 

image

Conclusion

If you have high-availability requirements then I highly recommend abandoning the Dynamics CRM 4.0 adapter.  Currently, and I am not sure if there are any plans, CRM 2011 does not have an adapter so now is as good as time as any to make the move.   You can read more about CRM 2011 integration on Richard Seroter’s blog.  Also look for a chapter on CRM 2011 integration in our upcoming book.

6 comments:

Reshma said...

Hi Kent,

I just implemented the solution of calling crm webservice in biztalk. In the biztalk administration console, for configuring the orchestration, I had set the Port for CRM to the wcf service port which was set to basichpptbinding and windows autentication. However I am getting the following error: Details:"System.ServiceModel.Security.MessageSecurityException: The HTTP request is unauthorized with client authentication scheme 'Ntlm'. The authentication header received from the server was 'Negotiate,NTLM'. ---> System.Net.WebException: The remte server retrned an error: (401) Unauthorized". Can you please help on this. Thanks.

Kent Weare said...

Did you enable the impersonation configuration in this send port as well?

Tom Burk said...

Hi Kent,

I am replicating this scenario using Biztalk 2010, and CRM 4.0, using the modified SOAP port as you identified. I am using the account schema, and have followed your example very closely. I have double checked many times all your tips. I am getting a TimeoutException for some reason, could there be something else about the headers I am overlooking? Can you provide a look at your soap message? Here is the error I get, did you run into this too? Does this indicate an issue with my SOAP headers?

A message sent to adapter "WCF-Custom" on send port "WcfSendPort_CrmService_CrmServiceSoap12" with URI "http://crmdev/MSCrmServices/2007/CrmService.asmx" is suspended.
Error details: System.TimeoutException: The HTTP request to 'http://crmdev/MSCrmServices/2007/CrmService.asmx' has exceeded the allotted timeout of 00:01:59.9060000. The time allotted to this operation may have been a portion of a longer timeout. ---> System.Net.WebException: The request was aborted: The request was canceled.
at System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)
at System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelAsyncRequest.CompleteGetResponse(IAsyncResult result)
--- End of inner exception stack trace ---

Server stack trace:
at System.Runtime.AsyncResult.End[TAsyncResult](IAsyncResult result)
at System.ServiceModel.Channels.ServiceChannel.SendAsyncResult.End(SendAsyncResult result)
at System.ServiceModel.Channels.ServiceChannel.EndCall(String action, Object[] outs, IAsyncResult result)
at System.ServiceModel.Channels.ServiceChannel.EndRequest(IAsyncResult result)

Exception rethrown at [0]:
at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
at System.ServiceModel.Channels.IRequestChannel.EndRequest(IAsyncResult result)
at Microsoft.BizTalk.Adapter.Wcf.Runtime.WcfClient`2.RequestCallback(IAsyncResult result)
MessageId: {F8232EB8-88F5-4B73-BF93-6516FFF8BB9B}
InstanceID: {26734EB0-3995-4CD7-A1CD-7682B0254900}

Kent Weare said...

Hi Tom,

Have you tried consuming this service from outside BizTalk? Say via WCFTestClient? The reason I ask is that it does not "smell" like a BizTalk issue to me. The error message makes it look like BizTalk has "given up" waiting for CRM to respond. It would be nice to isolate the issue by trying a different client other than BizTalk.

Unless you are passing a very large amount of data, it should not take this long. When we send messages with CRM it takes around 1 second.

Kent

Genuine said...

Awesome!! Thanks for the detailed post.

Tom Burk said...

Ken, I didn't see your reply until now. Thank you, and yes you were right, there was a CRM Plugin that was not enabled which caused the reported issue. Once the Plugin was started the code written in the BizTalk solution began working.