Which SSRS reports are being run through Dynamics CRM?

Microsoft Dynamics CRM uses SQL Server Reporting Services (SSRS) for its reporting platform. It’s a fairly decent integration, but it lacks one visible aspect from CRM: what reports are being run?

Getting detailed report execution (who requested the report, what parameters were used at runtime) would require digging deep, but if you just want to know which reports were run and when they were run, you can do it from two SQL queries.

For a list of all reports run for a CRM instance, use the SQL below. Replace “CRM_MSCRM” with the name of your CRM database, and CRM_ReportServer with the name of your SSRS reporting database.

with ReportNames as (
	select r.name, '{' + cast(r.reportid as nvarchar(40)) + '}' as reportid
	from CRM_MSCRM..FilteredReport r
)
SELECT
	rn.Name,
	E.TimeStart,
	E.TimeEnd,
	E.Status
FROM CRM_ReportServer.dbo.Catalog C with(nolock)
left outer join ReportNames rn on c.Name = rn.ReportID
LEFT OUTER JOIN CRM_ReportServer.dbo.ExecutionLog  E with(nolock) ON C.ItemID = E.ReportID
WHERE C.type=2
and rn.Name is not null
order by e.TimeStart desc

If you want a count of all reports run, along with the first/last date they were run, use the following (again replacing CRM_MSCRM and CRM_ReportServer as necessary): Continue reading

Stopping Internet Explorer 11 from showing the mobile express version of Dynamics CRM 2011

If you have a Dynamics CRM 2011 installation, and users have started using Internet Explorer 11 (either by upgrading from IE10, or by installing Windows 8), then you’ve undoubtedly noticed that, when an IE11 user goes to your CRM site, they are greeted by the Mobile Express version, rather than the standard site. There are a few manual workarounds to this:

  • Downgrade from IE11 to IE10 (not an option for Windows 8).
  • Add your CRM domain name to the compatibility list in IE11 (not an option if you don’t want the entire domain to be in compatibility mode, or if you have group policy settings which prohibit this).
  • Instruct users to go to https://yourcrmdomain.com/main.aspx.

That third bullet is interesting… If you go to the root of your CRM domain name in IE11, you will be redirected to the Mobile Express site. If you go to the “main.aspx” page on the root, you go to the full CRM site. Which got me thinking… How can we identify IE11 users accessing the Mobile Express site, and redirect them to /main.aspx?

The solution is in IIS’s URL Rewrite library, which, if you don’t already have, you should get, because I’ve used it before to fix issues related to Dynamics CRM (not to mention its multitude of other uses).

Here’s what our rewrite rule will do:

The full IIS rewrite rule is below. You can drop this into the web.config in the root of your Dynamics CRM 2011 web site, or set it up manually using the URL Rewrite wizard. Either way, when you’re done, you’ll always get the full version of Dynamics CRM when using the IE11 browser.

<rule name="Redirect IE11 from mobile site to main.aspx" patternSyntax="Wildcard" stopProcessing="true">
 <match url="*m/default.aspx" />
 <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
 <add input="{HTTP_USER_AGENT}" pattern="*; rv:11.0) like Gecko" />
 </conditions>
 <action type="Redirect" url="https://crm.innovatix.com/main.aspx" redirectType="Temporary" />
</rule>

By the way, there is only one gotcha: You can’t use IE11 and deliberately go to the Mobile Express site. A small price to pay to fix a much larger problem.

Getting Dynamics CRM ObjectTypeCode values via SQL

Lots of tables in Dynamics CRM use the ObjectTypeCode to identify the entity. Sure, we all remember that Account is ObjecTypeCode 1 and Contact is ObjectTypeCode 2… But what about your custom entities, which start at 10,000 and are not guaranteed to be the same in different organizations, even if you have the same solutions installed — how will we remember their ObjecTypeCode values?

You don’t remember them, you query them when they need you. It’s quick and easy if you are on premises and have access to the SQL database. (If you don’t, ask your DBA to create a view with this query and give you rights to it.)

select coalesce(OriginalLocalizedName,name) as DisplayName, Name as SchemaName, ObjectTypeCode
from EntityLogicalView
order by ObjectTypeCode

Much easier than memorizing, and avoids the problems of remembering the wrong number for the wrong organization or deployment. Oops!

How entity relationships can kill performance in Dynamics CRM 2011

Microsoft Dynamics CRM 2011 allows us to specify relationships between entities, and gives us some configuration as to how those relationships work. One of those relationship types is “parental” — which effectively means, “anything that happens to the parent happens to the child.” Therein lies a lot of power — and a lot of risk.

In the environment where I work, we have a lot of entities. Two of those entities are a “Contract” entity (not the out-of-the-box Contract entity, which we renamed to a more appropriate “Service Contract”) and a “Contract Volume” entity. Intuitively, since you can not have Contract Volume without a Contract, we set the relationship type to parental, with Contract being the parent to Contract Volume.

We have thousands of Contract records, and hundreds of thousands of Contract Volume records. The data for these comes from a separate proprietary application. Data is inserted/updated into CRM from this other system using Scribe Insight. The entities are read-only in CRM, so we don’t have to bi-directional synchronization of data. All was well, and we moved data for a couple weeks without issue.

In a recent release, we expanded the functionality of CRM and found the need to customize the owner of the Contract entity to one of three different CRM teams. So, we did what seemed sensible: update the Scribe package to set the owner based on the criteria we came up with. Unfortunately, running this package took down our CRM system within a minute. The database server was overloaded, and SQL blocks were everywhere.

The problem was our relationship. In a parental relationship, any deleting, sharing, and assigning of the parent will affect all of its children, The effect of this is:

  • Deleting a parent will delete all child records.
  • Sharing or un-sharing a parent will perform an identical share or un-share on all child records.
  • Assigning (changing the owner of) a parent will assign (change the owner of) all child records.

Think about the potential impact of this. If a single record has 10,000 child records, and you assign this record to another user, CRM must perform the same operation against all 10,000 child records — as one operation. Add on top of that CRM’s built-in overhead (auditing, principal object access, logging, etc.), and you’ve effectively killed your system, which likely can not keep up with the scope of your request while dealing with other people’s requests.

A better solution for us was to do the following:

  • Change the Parental relationship to a Referential relationships, where there is no cascading, and a delete will simply remove the parent reference, leaving the child an orphan with no parent.
  • Create a cleanup job to delete all the orphaned child records. This can be done via a a Scribe job, a recurring bulk delete job, or manually.

Dynamics CRM has a lot of power, but you have to think about the effect of the choices made, because they very easily can cause unintended consequences — as they did for us recently!

Getting rid of nameless Contacts when Leads are qualified in Dynamics CRM

Some would wonder why such a question is asked… but in my Microsoft Dynamics CRM 2011 environment, we do not require a First Name or Last Name on a Lead. When a Lead is qualified, users often click the buttons to create an Account and a Contact, even when they haven’t specified any contact-specific information (such as first and last name). What results is often a Contact with no name, and that contact is the Primary Contact for the Account. Ouch!

To fix this, we created a workflow on the Contact entity, which would deactivate the Contact if it had a Qualifying Lead, and had a blank First Name or Last Name. This took care of the Contact, but there was still a problem: the Account’s Primary Contact pointed still pointed to the nameless Contact we just deactivated!

Looking at the Audit History on the Account, we were able to figure out that, when qualifying a lead, CRM creates and updates records as follows:

  1. A new Account record is created. The Primary Contact field is left blank.
  2. A new Contact record is created. The Contact’s Account is set to the Account created in step #1.
  3. The Account record created in step #1 is updated. The Primary Contact field is set to the Contact created in step #2.

To solve the deactivated Primary Contact problem, we created a workflow on the Account entity, which would clear the Primary Contact field if the Primary Contact was set to a deactivated Contact.

Problem solved! Users can continue trying to create Contacts with no names, and we’ll make sure they never see them.

Dynamics CRM doesn’t enable search on Address ZIP/Postal Code by default

In the “oh my, how did that slip through?” category comes this one: in Dynamics CRM 2011, the ZIP/Postal Code field on the Address (CustomerAddress) entity is not searchable by default.

Weirder is that the ZIP/Postal Code fields on the Account, Lead, and Contact entities are searchable by default. Weird only because they use the same underlying table (CustomerAddress).

Keep this in mind when you wonder why you can’t search for additional addresses by ZIP code. Make that ZIP/Postal Code field on the Address entity searchable, and you’re good to go.

URL Rewriting for user-friendly URLs with Dynamics CRM 2011

Anyone who has attempted to configure Dynamics CRM 2011 with an Internet-Facing Deployment (IFD) knows that it is no trivial task. Where there are blog posts that discuss setting up an IFD, and Microsoft documentation for configuring the IFD, they often assume that ADFS and Dynamics CRM are installed on the same server, and that there is only one Dynamics CRM front-end server. Unfortunately, real-world implementations don’t always follow that.

For example, take the following configuration:

  • a Dynamics CRM front-end server on the internal network, providing services to internal clients
  • a Dynamics CRM front-end server in an Internet-facing zone, providing services to external clients
  • a separate ADFS server accessible to internal and external clients

Dynamics CRM with IFD requires a combination of ADFS relaying party trusts and DNS configuration to get things working. One caveat with IFDs is that the internal and external host names for the Dynamics CRM front-end servers must be different because, externally, the host name includes the CRM organization name. Where, internally, you may have https://icrm.contoso.com/crm, externally you would have https://crm.contoso.com.

Let’s flesh out our sample implementation and requirements:

  • icrm.contoso.com is our internal Dynamics CRM front-end server, accessible only on the internal network
  • ecrm.contoso.com is our external Dynamics CRM front-end server, accessible to our internal network and the public Internet
  • adfs.contoso.com is our ADFS server, accessible to our internal network and the public Internet
  • We have two Dynamics CRM organizations: CRM and CRM-Test.
  • We want our internal and external (public Internet) clients to access CRM using the same URLs: crm.contoso.com and crm-test.contoso.com. In other words, we don’t want the two-URL problem outlined above.

The last bit has nothing to do with Dynamics CRM: it is all done in IIS. Let me explain how. Continue reading

Fixing ‘State code or status code is invalid’ errors when deleting solutions in Dynamics CRM 2011

Working with Dynamics CRM 2011, I started getting an error when attempting to delete a managed solution:

Error deleting a managed solution: State code or status code is invalid
State code is invalid or state code is valid but status code is invalid for a specified state code.

The ErrorDetails.txt file states the following — note the text in bold.

Unhandled Exception: System.ServiceModel.FaultException`1[[Microsoft.Xrm.Sdk.OrganizationServiceFault, Microsoft.Xrm.Sdk, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]: 1 is not a valid status code for state code SavedQueryState.Inactive on savedquery.Detail:

Error Code -2147187704

CallStack
at Microsoft.Crm.Extensibility.VersionedPluginProxyStepBase.Execute(PipelineExecutionContext context)
at Microsoft.Crm.Extensibility.Pipeline.Execute(PipelineExecutionContext context)
at Microsoft.Crm.Extensibility.MessageProcessor.Execute(PipelineExecutionContext context)
at Microsoft.Crm.Extensibility.InternalMessageDispatcher.Execute(PipelineExecutionContext context)
at Microsoft.Crm.Extensibility.ExternalMessageDispatcher.ExecuteInternal(IInProcessOrganizationServiceFactory serviceFactory, IPlatformMessageDispatcherFactory dispatcherFactory, String messageName, String requestName, Int32 primaryObjectTypeCode, Int32 secondaryObjectTypeCode, ParameterCollection fields, CorrelationToken correlationToken, CallerOriginToken originToken, UserAuth userAuth, Guid callerId, Guid transactionContextId, Int32 invocationSource, Nullable`1 requestId, Version endpointVersion)
at Microsoft.Crm.Extensibility.OrganizationSdkServiceInternal.ExecuteRequest(OrganizationRequest request, CorrelationToken correlationToken, CallerOriginToken callerOriginToken, WebServiceType serviceType)
at Microsoft.Crm.Extensibility.OrganizationSdkServiceInternal.Execute(OrganizationRequest request, CorrelationToken correlationToken, CallerOriginToken callerOriginToken, WebServiceType serviceType)

This error came about after we marked some of the out-of-the-box views as Inactive (like “Accounts: Responded to Campaigns in Last 6 Months”). Looking at the database, the statecode (Status) and statuscode (Status Reason) for these views (in table SavedQueryBase) were both “1”. There was a simple workaround to this: change the statuscode to “2”, using the following SQL.

update savedquerybase
set statuscode = 2
where statuscode = 1
and statecode = 1

That allowed the solution to delete without error, and the views I wanted inactivated were still inactive. Seems a bug in Dynamics CRM 2011, but I can’t say for sure.

Of course, use this as your own risk!

Setting the entity owner to a team using Scribe Insight and Dynamics CRM 2011

While working on migrating data from a legacy CRM system to Microsoft Dynamics CRM 2011 using Scribe Insight, I decided to set the owner of all migrated entities to a specific team (rather than the default, which is the Dynamics user specified in the Scribe connection). To do this seemingly would require one simple steps: to look up the GUID of the team, and set the value of the Dynamics CRM “ownerid” to that value.

To set the “ownerid” I used the DBLOOKUP command to look up the team by name, and return the teamid: DBLOOKUP( “MyTeamName”, “CRM”, “team”, “name”, “teamid” ). This worked fine, but on its own, Dynamics threw an error, saying that the GUID wasn’t valid.

The problem lies in how Dynamics manages owners: an owner can be a user or a team. The owner of an entity is assumed to be a user unless you change the “owneridtype” field, which is visible in Scribe but is not listed in the Dynamics data dictionary (as far as I could tell). If the owner is a user, the “useridtype” should be 8. If the owner is a team, the “useridtype” should be 9.

After setting the “useridtype” to 9, Dynamics accepted the GUID of my team in the “ownerid” field. Problem solved!