Tip #550: Subgrids and scripts

One of the Easter Eggs in CRM Online 2015 Update 1/CRM 2016 are the ability to execute scripts when subgrids load. With this enhancement, CRM developers can set event handlers for the OnLoad event of the subgrid. Scripts can be used to:

  • Get the current view selected in the view selector
  • Change the view of the subgrid to a different view.
  • Get the data rows from the grid.
  • Get the selected rows of the subgrid.

For more details see the “Write scripts for subgrids” section of the CRM 2016 SDK.

Tip #549: Cannot convert A into A

If you were playing with the uber-administrator’s way to create non-intractive users then you might have seen the following error:

Cannot process argument transformation on parameter ‘conn’. Cannot convert the “Microsoft.Xrm.Tooling.Connector.CrmServiceClient” value of type “Microsoft.Xrm.Tooling.Connector.CrmServiceClient” to type “Microsoft.Xrm.Tooling.Connector.CrmServiceClient”.

Ugh? Say again? Cannot convert “A” to “A”?

The other possible error gives away a bit more:

Get-CrmConnection : The component ‘Microsoft.Xrm.Tooling.CrmConnector.Powershell.LoginControl.LoginControlDlg’ does not have a resource identified by the URI
‘/Microsoft.Xrm.Tooling.CrmConnector.Powershell;component/logincontrol/logincontrol.xaml’.

In a nutshell, the above errors are the result of the clash of different versions of Microsoft.Xrm.Tooling.Connector.dll assemblies (and “Cannot convert A to A” is actually “Cannot convert A v1.8 to A v2.0”). How did you end up with the multiple versions?

  1. Imported Microsoft.Xrm.Data.PowerShell module that has a version of the tooling from CRM 2015 distributed with it. (This is done deliberately so that you don’t have to install SDK to use the module)
  2. Installed CRM SDK 2016, registered Microsoft.Xrm.Tooling Powershell snapin and had a valid reason to add it into the current script:
    Add-PSSnapin Microsoft.Xrm.Tooling.Connector
    Import-Module Microsoft.Xrm.Data.Powershell
    

Solution is either don’t use your version of connector or get the right Microsoft.Xrm.Data.Powershell version (there are two distributions) or fiddle with Microsoft.Xrm.Data.Powershell distribution to remove connector from there (isn’t that what Open Source is for?)

Tip #548: 64-bit Office & CRM – Go for it!

In years past the common practice when CRM professionals were asked by clients about installing 64-bit Office, the answer was clear “Don’t do it!”.

First off there was little compelling reason to install 64 Bit Office as the only real application in the suite that took advantage of it was Excel.

That common thinking has changed in the last year or so. First off, we can now access CRM via not only other browsers than IE but the dependence on Active – X support has dropped off the required list.

The latest TechNet Articles for both CRM 2015 and CRM 2016 are recommending the installation of 64-bit Office when you have 64-bit Windows installed.

However, you should still consider the implications of using 64 Bit Office instead of the 32-bit version depending on your organization’s requirements in other areas. The 64-bit version of Office may perform better in some cases, but there are limitations:

  • Solutions using ActiveX controls library, ComCtl controls won’t work.
  • Third-party ActiveX controls and add-ins won’t work.
  • Visual Basic for Applications (VBA) that contain Declare statements won’t work in the 64-bit version of Office without being updated.
  • Compiled Access databases, like .MDE and .ACCDE files, won’t work unless they’re specifically written for the 64-bit version of Office.
  • In SharePoint, the list view won’t be available.

Tip #547: CRM Christmas Presents

This is our final tip of 2015, and so we present a grab bag of goodies:

  1. Fantasy Sales Team is now available for CRM online clients in North America. This is the gamification platform that Microsoft acquired earlier this year. Improve your user adoption by making it a game. Users pick their teams and compete against each others. Managers can define the metrics and point values of the game, and even though it has the word “sales” in the name, you can use it for any type of user metrics, not just sales. EMEA is coming Q1 of calendar year 2016.
  2. One of the hidden gems of CRM 2016 is the enhancements to business rules. They now can be used with Business Process Flow. You can trigger business rules when a certain process flow is active, or based on a specific stage of the process. This is a great improvement and makes business rules and business process flow better together. Read more at the Magnetism blog.
  3. If you want to deploy the new hybrid Server-Side Synchronization between CRM Online and Exchange On Premises, test your connection to your Exchange server from the Microsoft cloud using the Remote Connectivity Analyzer. This tool lets you test the connectivity with your on premises Exchange and Lync servers and identify issues prior to deploying Server-Side Synchronization.

We will be back on January 4th. Happy holidays to all of you. Join us next year for more great tips.

Tip #546: Avoid using the same domain for ADFS and CRM

I’m not sure how to condense 3 days of pain and desperation into a tip of the day but I shall try.

tl;dr

Do not use the same base domain for ADFS and CRM if you have other applications (e.g. a web site) requiring Single Sing-On (SSO) with CRM.

Please explain

If you ever set up Internet Facing Deployment for CRM On-premises, you know that it is generally a small PITA. Some even make a living out of solving mysteries of IFD configurations. All you need is a CRM server, say crm.contoso.com, and an ADFS server, say adfs.contoso.com, both accessible from the outside (whatever outside means to you, it does not need necessarily to be public). Then a bit of magic grease and it’s all up and running.

The problems begin when you try to create a website that uses the same ADFS for user authentication. Thanks to Owin and Katana, it’s now a point-and-click exercise. Why would you want an SSO between CRM and the website? Well, how about hosting an ASP.NET page in IFRAME inside CRM form and passing the information between the form/CRM and the site? All without asking people to authenticate twice. (While we’re on the subject – the SDK sample is awfully out of date, don’t try this at home, I’ll get to it in a separate tip.)

In the configuration above, all works fine in ADFS 2.0 but fails in ADFS 3.0 (Windows Server 2012 R2). Both web site and CRM can authenticate independently, if you authenticate to the web site first, CRM will honor that authentication and won’t prompt, however, going from authenticated CRM session to the web site (the scenario that we actually need) fails miserably in an endless “please login” loop.

In the end I managed to trace it down to the web site sending existing cookies (that CRM authentication process has created) to ADFS but ADFS logs an obscure and exceptionally brief error “Ignore corrupted SSO cookie” and promptly redirects the web site to login. I even checked the cookies by hand. Yep, the very first one MSISAuth cookie (the one holding authentication information) is not a valid base64 string. Huh?

Digging deeper; there are two MSISAuth cookies, one for .contoso.com domain and another for adfs.contoso.com, latter being the invalid one. Huh?!

Long string of coffee cups, trials and errors followed by almost accidental discovery:

Using different domain name for ADFS server allows flawless SSO in both directions between your CRM and the web site

In the scenario above replacing adfs.contoso.com with adfs.osotnoc.com, for example, would do the trick. Of course you’ll need a domain and an SSL certificate but those can be had for less than all those cups of coffee put together. Changing name of your ADFS service is a bit tricky but can also be done.

To be honest, I still have no idea why it was happening but in the absence of any other information I squarely blame it on ADFS 3.0. People who actually know a thing or two about claims, authentication, tickets, cookies and goblins are more than welcome to drop a note or two shedding some light on the behavior.

Tip #545: Create non-interactive user like a boss

I won’t name any names in this post. You know who you are.

Actually a super-duper developer #1: One of the follow up questions I got from an attendee at Extreme CRM was around the complexity of setting up a non-interactive service account today. I have to agree with them it seems overly tedious if you truly want to use it as a non-interactive without burning a full CRM Online license. Are there any plans to make it easier to setup and use non-interactive service accounts in CRM Online?

The discussion followed, pointing to interactive instructions.

Actually a super-duper developer #2: I do not think this can be scripted because some of the changes are in Office 365 (ie: licensing and adding the user), and some are in CRM. I don’t think some of this is scriptable at this time.

Challenge accepted

What you need:

You need to be an O365 admin and CRM system admin to run this script

Add-PSSnapin Microsoft.Xrm.Tooling.Connector
Import-Module MSOnline
Import-Module Microsoft.Xrm.Data.Powershell

# login interactively
$cred = Get-Credential
Connect-MsolService -Credential $cred

$upn = 'crm.ni.service@contoso.onmicrosoft.com'
$role = 'System Administrator'

# create O365 user
New-MsolUser `
  -DisplayName "CRM Non-interactive Service" `
  -UserPrincipalName $upn `
  -Password 'pass@word1' `
  -UsageLocation US `
  -LicenseAssignment contoso:CRMSTANDARD

# get connection to CRM
$global:conn = Get-CrmConnection `
  -Credential $cred `
  -ServerUrl https://contoso.crm.dynamics.com `
  -OrganizationName contoso

# get the user (note: you may want 
# to insert a wait or a waiting loop here)
$users = Get-CrmRecords `
   -EntityLogicalName systemuser `
   -FilterAttribute domainname `
   -FilterOperator eq `
   -FilterValue $upn `
   -Fields systemuserid, fullname

$user = $users.CrmRecords[0]

# add role to the user
Add-CrmSecurityRoleToUser `
   -UserId $user.systemuserid `
   -SecurityRoleName $role

# set access mode to non-interactive
Set-CrmRecord -conn $conn `
   -EntityLogicalName systemuser `
   -Id $User.systemuserid `
   -Fields @{"accessmode" = `
     New-CrmOptionSetValue -Value 4}

# remove the license
Set-MsolUserLicense `
   -UserPrincipalName $upn `
   -RemoveLicenses contoso:CRMSTANDARD

Like a boss meme

Tip #544: Enabling JWT in ADFS breaks Dynamics CRM for Outlook

If you ever dealt with Dynamics CRM authentication at “close range”, you know that CRM supports OAuth. Presumably, with CRM 2016 and ADFS 3.0 (Windows Server 2012 R2), we should be able to use OAuth for CRM On-premises, right? Especially now that ADFS supports JSON Web Tokens, so we should be able just enable JWT and move on. As it turns out, enabling JWT on ADFS completely breaks Dynamics CRM for Outlook that can no longer authenticate.

Not enabling JWT is not an option either because according to that article

JWTs are the only supported token type for OAuth requests.

So unless you are not using CRM for Outlook, OAuth implementation for CRM On-premises would have to wait. There are other obstacles in ADFS 3.0 as well, and looks like we’ll have to wait for Windows Server 2016 but that’s for another tip.

Tip #543: Tipster guide to Dynamics CRM 2016 solution changes

Another Friday and the first video on 2016 release is here. CRM 2016 includes, among other things, a number of enhancements to the CRM solutions functionality. In this video we will look at the how to work with solution sub-components, and introduce the concept of solutions patches.

YouTube player

Give us your feedback, all of it: good, bad, and ugly, I’m sure we can take it. Suggest new topics either in comments or by sending your ideas to jar@crmtipoftheday.com.

Tip #542: Who do you think you are

One of the great features of Dynamics CRM is the ability to impersonate other users’ accounts. Just set CallerId property on CrmConnection object and go on with your business as usual. However, make sure that your code or the code that you call, do not rely on the current user identity obtained using WhoAmIRequest. As it turns out, CRM impersonates all requests with the exception of WhoAmIRequest. Store caller identity and use it explicitly.

CrmConnection connection = new CrmConnection("Xrm");
// impersonate known (to us) user account
connection.CallerId = 
      new Guid("64D74566-C903-E511-80BA-00155D003523");

using (OrganizationService service = 
   new OrganizationService(connection))
{
    // this call will return the original caller, 
    // not impersonated one
    var userId = service.Execute<WhoAmIResponse>(
       new WhoAmIRequest()).UserId;

    // account, however, will have 
    // impersonated user as createdby
    Guid accountId = service.Create(
      new Entity("account")
      {
        Attributes = { { "name", "Acme Inc" } }
      });
}