Tuesday, 9 November 2010

The CRM 4.0 Reporting Services Connector - how it works

The Dynamic CRM Connector for Reporting Services is a very useful component that avoids the need to configure Active Directory delegation (aka double-hop authentication) when multiple servers are used for the CRM, Reporting Services and SQL Server roles in a CRM implementation. In general, it is easy to install and use, but I'm always interested in how these things work.

How the Connector is installed and invoked
The connector is installed as a Data Processing Extension with SSRS. These extensions are registered within the rsreportserver.config file on the Reporting Server, as per the following snippet:

<Data>
<Extension Name="SQL" Type="Microsoft.ReportingServices.DataExtensions.SqlConnectionWrapper,Microsoft.ReportingServices.DataExtensions" /> <Extension Name="OLEDB" Type="Microsoft.ReportingServices.DataExtensions.OleDbConnectionWrapper,Microsoft.ReportingServices.DataExtensions" />
<Extension Name="MSCRM" Type="Microsoft.Crm.Reporting.DataConnector.SrsExtConnection,Microsoft.Crm.Reporting.DataConnector" /> </Data>

All CRM reports with SSRS are configured to use a specific SSRS Data Source. When the Connector is installed, the Data Source is changed to use the MSCRM Data Processing Extension, instead of the default SQL Server extension. See images below:


Report properties showing the MSCRM Data Source


MSCRM Data Source using the SQL Server extension before the connector is installed


MSCRM Data Source using the CRM extension after the connector is installed.

There are 3 differences between these configurations:

  1. The Connection Type, which specifies the extension
  2. The Connection String is absent with the CRM connector. This is because the connector reads some of the database information from registry values that were created during its installation, and some from data passed to it when the report is run (see below)
  3. The Credentials. With the SQL Server connector, standard Windows Integrated security is used - i.e. the user's AD credentials are used to connect to SQL Server. With the CRM connector, separate 'credentials' are passed to SSRS (again, see below)

What happens when a report is run
If you try to run a CRM report with the CRM connector installed, the connector will require some 'credentials', as per point no.3 above. This image shows what happens if you try to run a report from Report Manager:


Running a CRM report from Report Manager when the CRM connector is installed

These 'credentials' are not what they seem; rather they are a cunning way for the CRM platform to pass information about the current user to the CRM connector. The CRM connector expects the current user's systemuserid (a Guid) to be passed into the Log In Name box, and the organizationid (another Guid) to be passed into the Password box. These are not your login name and password.

As the report uses a data source that uses the CRM connector, the RS Report Server code calls the CRM connector code (the SrsExtConnection class in the Microsoft.Crm.Reporting.DataConnector assembly, as per the rsreportserver.config data above). The code will then:
  1. Check that it is permitted to impersonate a CRM user. This checks that the identity the code is running under (which the identity of the ReportServer application pool, or the Reporing Services service, depending on the version of Reporting Services) belongs to the AD group PrivReportingGroup
  2. Connect to the MSCRM_Config database to determine the correct MSCRM organization database, based on the organizationid that was passed in the 'credentials'
  3. Connect to the relevant MSCRM organization database. Note that this is done (as was the previous step) using integrated security under the AD identity as per step 1 above
  4. Use the SQL statement SET Context_Info to pass the calling CRM user's systemuserid into the Context_Info
  5. Execute the SQL statement(s) within the report definition. The definition of all filtered views use the fn_FindUserGuid function to read the systemuserid from the Context_Info

What can you do with this information
One use is for troubleshooting. Checking the rsreportserver.config is a quick way to see if the connector is installed, and checking the configuration of the MSCRM Data Source will tell you if the connector is in use. Changing the MSCRM Data Source is a quick way to turn the connector on or off for test purposes.

You can also run the reports directly, rather than from CRM. Again, when troubleshooting I find it useful to run a report directly from Report Manager web interface. To do this with the connector, you need to enter the systemuserid and organizationid when prompted (see image above). These values can be read from the filteredsystemuser and filterorganization views respectively in the MSCRM database.

A further option is to run the reports via other means, such as Url Access, as described here (that article was written for CRM 3, see here for an update for CRM 4). To do this with the connector installed, you will also have to pass the systemuserid and organizationid on the query string. This is done using the following syntax:

&dsu:CRM=<systemuserid>&dsp:CRM=<organizationid>

1 comments:

Andreas said...

Hi David,

this is an interesting post. Especially we are currently having an issue with the SSRS.

The problem is, that when a user runs a report from crm, it take a long time until the report is shown ( > 1 minute)

The crm trace shows the following error, but the report is shown correctly.

at ErrorInformation.LogError()
at MainApplication.Application_Error(Object sender, EventArgs eventArguments)
at EventHandler.Invoke(Object sender, EventArgs e)
at HttpApplication.RaiseOnError()
at ApplicationStepManager.ResumeSteps(Exception error)
at HttpApplication.System.Web.IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData)
at HttpRuntime.ProcessRequestInternal(HttpWorkerRequest wr)
at HttpRuntime.ProcessRequestNoDemand(HttpWorkerRequest wr)
at ISAPIRuntime.ProcessRequest(IntPtr ecb, Int32 iWRType)
>MSCRM Error Report:
--------------------------------------------------------------------------------------------------------
Error: Fehler bei der Anforderung mit HTTP-Status 401: Unauthorized.
Error Message: Fehler bei der Anforderung mit HTTP-Status 401: Unauthorized.
Source File: Not available
Line Number: Not available
Request URL: http://crm/Reserved.ReportViewerWebControl.axd?ReportSession=whoybk55mchv3a55irtg3ezl&ControlID=038f71000a02428b929e2d084294f914&Culture=1031&UICulture=1031&ReportStack=1&OpType=SessionKeepAlive&TimerMethod=KeepAliveMethodreportViewerTouchSession0&CacheSeed=Wed Nov 10 16:41:48 2010
Stack Trace Info: [WebException: Fehler bei der Anforderung mit HTTP-Status 401: Unauthorized.]
bei System.Web.Services.Protocols.SoapHttpClientProtocol.ReadResponse(SoapClientMessage message, WebResponse response, Stream responseStream, Boolean asyncCall)
bei System.Web.Services.Protocols.SoapHttpClientProtocol.Invoke(String methodName, Object[] parameters)
bei Microsoft.SqlServer.ReportingServices2005.Execution.ReportExecutionService.GetExecutionInfo()
bei Microsoft.SqlServer.ReportingServices2005.Execution.RSExecutionConnection.GetExecutionInfo()
bei Microsoft.Reporting.WebForms.ServerReport.TouchSession()
bei Microsoft.Reporting.WebForms.SessionKeepAliveOperation.PerformOperation(NameValueCollection urlQuery, HttpResponse response)
bei Microsoft.Reporting.WebForms.HttpHandler.ProcessRequest(HttpContext context)
bei System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
bei System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

Have you an idea, what the problem might be?

Thanks, Andreas