Tip #769: Careful who you use as certificate authority

Fake certificateSometimes we need to call web services from a plugin or a custom workflow activity. For CRM Online it means calling it from the sandbox and there are certain restrictions that apply to the network access.

These restrictions are clear and well understood, however, if you call service over https protocol, one addition restriction applies: the certificate of the site you’re connecting to must be issued by a trusted authority.

Not all authorities are equal and the fact that your client browser trusts a particular authority, does not mean that CRM server will follow the suit. If you try to access a web service over https using HttpClient (or any other client), and certificate issuing authority is not trusted, you will receive a generic error with the exception details containing some particulars:

The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel. System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure.

What certificates seems to be fine on the client side but may cause some drama when accessed in CRM Online? Those issued by (what turns out to be) dodgy authorities of WoSign and StartCom (yours truly was actually a fan of StartCom until they got into the dubious partnership with WoSign), and deservingly so. Unfortunately, certificates issued by new and cool kid on the block, Let’s Encrypt, also got caught, which is not cool at all.

Interestingly enough, all of these certificates are just fine on CRM On-premises (hosted on Windows 2012 R2 server).

Bottom line: if you are using a https web service in a plugin or a custom workflow activity in CRM Online, test it thoroughly.

Tip #768: Avoid running out of memory when using CRM context

Low memoryWhen you process large data in your code, email attachments, for example, there is always a danger of running out of the available memory. Consider this code dealing with the email attachments:

using (OrganizationServiceContext crmContext = new OrganizationServiceContext(crmSvc))
{
    var attachments = 
        from a in crmContext.CreateQuery("activitymimeattachment")
        select new {
          body = a.GetAttributeValue<string>("body")
        };
		
    foreach (var att in attachments)
    {
        ProcessAttachment(att);
    }
}

The danger here is running out of memory on retrieval of the file attached to the CRM notes. Even though from C# point of view everything is clear, the culprit here is OrganizationServiceContext that keeps all the data retrieved via LINQ queries in the context.

Possible solution to the problem include:

  • Use pagination and re-initialize context after a handful of pages processed. The main drawback is that the entire context is re-initialized, including the data you didn’t have to.

    while (recordsExist)
    {
        pageNumber++;
        recordsExist = true;
        using (OrganizationServiceContext crmContext 
    		= new OrganizationServiceContext(crmSvc))
        {
            var attachments = 
               from a in crmContext.CreateQuery("activitymimeattachment")
               select new {
                  body = a.GetAttributeValue<string>("body")
               };
            foreach (var att in attachments
                .Skip(pageNumber)
                .Take(pageSize))
            {
                recordsExist = true;
                ProcessAttachment(att);
            }
        }
    }
    
  • Remove (detach) entities from context immediately after processing. Argument for the function is an Entity and therefore you won’t be able to use anonymous types in LINQ select which could somewhat affect the performance.
    var attachments = 
        from a in crmContext.CreateQuery("activitymimeattachment")
        select a;
    foreach (var att in attachments)
    {
    	ProcessAttachment(att);
    	crmContext.Detach(att, true);
    }
    

Tip #767: Server-to-server authentication is here

HeadlessWoot, woot! At long last we can create passive clients – the ones that do not have someone sitting in front of them. Clients like web sites or services – and authenticate them without using username and password AND get the magic bearer token that is good to use in Web API.

The detailed walkthrough is available, describing in glorious details the process of creating a web site (MVC applicaton) that can talk to Dynamics CRM. Don’t be put off by references to the web sites – you don’t have to be building one to take advantage of the feature. In fact, substantially lesser efforts required to create an equivalent console application that can be deployed as a Web Job and run unattended as a service.

  1. Register your application on Azure AD. No need for multi-tenant, single-tenant will do. What I found is that if you are using https://portal.azure.com, it does not create the application instance locally requiring the logon and consent. Easy to say with the web site, not so much with a console application. If instance is not created you will not be able to create an application user (see below). There is probably another way to do it but using old portal does immediately create an instance.
    Local Azure app
  2. Add permissions to access CRM and generate your key (same instructions)
  3. Create an application user. This is where the good stuff happens, we’re allowing a specific application to access CRM without a consent from a user.
  4. Create console application in Visual Studio and add nuget packages
  5. You are good to go!
  6. The code looks like this:
static void Main(string[] args)
{
    string organizationUrl = "https://notarealone.crm.dynamics.com";
    string clientId = "6db9cae3-dead-beef-dead-7179d4958975";
    string appKey = "get you own";
    string aadInstance = "https://login.microsoftonline.com/";
    string tenantID = "your tenant id";

    ClientCredential clientcred = new ClientCredential(clientId, appKey);
    AuthenticationContext authenticationContext = new AuthenticationContext(aadInstance + tenantID);
    AuthenticationResult authenticationResult = authenticationContext.AcquireToken(organizationUrl, clientcred);
    var requestedToken = authenticationResult.AccessToken;
            
    using (var sdkService = new OrganizationWebProxyClient(GetServiceUrl(organizationUrl), false))
    {
        sdkService.HeaderToken = requestedToken;

        OrganizationRequest request = new OrganizationRequest()
        {
            RequestName = "WhoAmI"
        };

        WhoAmIResponse response = sdkService.Execute(new WhoAmIRequest()) as WhoAmIResponse;
        Console.WriteLine(response.UserId);
    }
}
static private Uri GetServiceUrl(string organizationUrl)
{
    return new Uri(organizationUrl + @"/xrmservices/2011/organization.svc/web?SdkClientVersion=8.2");
}

Last but not least, to quickly find your tenant id, select application in either Azure portal and ask for Endpoints, your tenant id will be splattered all over endpoint urls as a guid that follows the domain.

Tip #766: Drop your full text index the supported way

As reported in the Dynamics forum, there are known issues with upgrading CRM On Premises to CRM 2016 SP1 if you have full-text indexing enabled. You will get the following error:

System.Data.SqlClient.SqlException: Cannot drop index ‘cndx_PrimaryKey_OpportunityProduct’ because it enforces the full-text key for table or indexed view ‘OpportunityProductBase’.

Various posts include SQL scripts to remove the full text index from your CRM database. While these work, they are risky and not supported by Microsoft.

So how do you remove the full text index in a supported way?

  1. Go to Settings > Administration > System Settings >General tab.
  2. Select No next to Enable full-text search for Quick Find. Uncheck any entities that you previously selected to use full text indexing for Quick Find.
  3. Wait 24 hours. This is to allow the indexing service to drop the full text index.
  4. After 24 hours you should be able to successfully upgrade to SP1.

After upgrading, you can re-enable full-text Quick Find.

Tip #765: Dynamics 365 for Team Members – fine print

Team membersOne of the new licenses is Dynamics 365 for Team Members Enterprise Edition, starting at $10 per user per month. As attractive as it sounds, this license comes with some caveats. Before shelling out your credit card and getting this license for all of your users, read the Dynamics 365 Enterprise Edition Licensing Guide, and get a magnifying glass for small print. But here is what I think is important (as inspired by Jukka “Kalsarikännit” Niiranen‘s comments):

The Good

  • Full Read across all Dynamics 365 Applications. Very nice, no room to misinterpret this one.
  • Edit access to Accounts, Contacts, Activities, and Notes. “Glamorized” address book functionality but couple it with Outlook and suddenly it’s quite powerful mechanism for uniform tracking customers interactions in your organization.
  • Full access to Knowledge Management, Interactive Service Hub for KM. Nice, ability for everyone to contribute and publish.
  • Ability to participate in projects by recording time & expenses. Can apply for projects as well.
  • Full access to custom entities as long as they stay within the license restrictions, i.e. no sneaky plugins updating opportunities
  • PowerApps access (within the licensed entities boundaries)
  • Portal or API access Only: Submit cases and read/update Cases user has submitted. Great for ESS portals where users can submit tickets.

The Bad

  • Edit access, where it’s permitted, is not spelled out. Does that include Delete? What about Append as in create a new lookup field on a contact pointing to a custom entity?
  • Granted rights are “for their own use and not for, or on behalf of, other individuals”. “On behalf of” as in “John Unlicensed asked me to update an account”? Or as in “you cannot edit an account owned by another user”?
  • PowerApps and API access – overlap is not clear. Looks like I can create a custom mobile app that allows team members to submit cases but am I allowed to create a PowerApp doing the same?

The Ugly

This one is so confusing, it’s worth copying verbatim. “If the custom entity is based on or replicates the functionality of entities included in Microsoft Dynamics 365, or if the entity links to entities included in Microsoft Dynamics 365, then users accessing the custom entity must also be licensed to access the included or replicated entity. For example, users creating an entity that replicates the cases entity for a ticketing system would still require the user to be licensed for cases. In other words, customizations may only be performed against entities users are licensed to access.”

“Based” I understand, “links” I understand, “replicates” – this is so fuzzy I don’t know where to begin. Say, I create “Deal with it” custom entity with a title and a deadline. So far so good. Then I link it to the customer. Is it a ticketing system yet? How about if I add some queuing and deploy workflows to route this item between users. Have I replicated cases yet? Add hierarchical relationship, some status code transition rules. Are we there yet?

As Jukka pointed out, what about custom entities that are clearly out of the contention for now but may overlap with the functionality released by Microsoft in the future? Will you suddenly find yourself in a violation of the licensing agreement?

I can see the intent – nothing new here, this kind of licensing has been around since Microsoft Access 1.0 days – but definition of “replicating” desperately needs to be expanded and explained using more detailed samples.

Good again

Team members license is designed as a successor to the ambiguous essential license and, despite some confusion and somewhat fuzzy definitions, it is a huge improvement. My take? Get it for everyone in your organization, make people to share their customer communications via CRM and then see if anyone requires more – and there are plenty of other licenses to choose from.

Tip #764: How to customize schedule board slots

UPDATE 03-JUL-2018

Following the discussion in the comments section: since version 8.2 customized booking doesn’t apply when using the schedule board daily, weekly, or monthly views. The good news is that this feature is on the roadmap!

Original tip

Schedule boardSchedule board is one of the centerpieces of the Dynamics 365 for Field Service (yep, that’s the new name). But every time we deployed field service, we’ve been asked to change the default layout that includes Work Order number, and start and end times.

Field Service Documentation defines Scheduler Field Service slot text template as

Enter HTML code to define the text and format that is displayed in the Field Service bookings on the schedule board.

which is not much of a help without the knowledge how to refer to the fields of the Work Order and related entities.

Partner documentation is even more condenscending in the assessment of our abilities:

This is an advanced option! Do NOT try it unless you know HTML.

Well, duh, I know HTML, I’ve been using interwebs since 1988. Long story short:

  • The template is a <div>block. You can try other elements but we found that wrapping it as a div gives enough flexibility and containment.
  • Fields can be referred by logical name using {} notation.
  • Fields from the parent entities can be referred to using {relationship_name.field_name} notation. Nesting works up to two levels.
  • Primary entity is Booking Resource Booking, start there and work your way up. First step is to get to the Work Order which is msdyn_msdyn_workorder_bookableresourcebooking_WorkOrder relationship.
  • This information is to be read when user is looking at the board. The moment user starts interacting, tooltips and details panel become available to display any additional information.
  • Beware of the size of the slot. Whatever fits in hourly layout, most likely will be truncated in daily.
  • Changes are immediate, there is no publishing cycle.
  • If you get it wrong, default template will be used.

Schedule slotWe have a customer who has quotes linked to Work Order so their current template looks like this (and produces the layout in the image)

<div>
Quote: {msdyn_msdyn_workorder_bookableresourcebooking_WorkOrder.foobar_quoteid}<br/>
{msdyn_msdyn_workorder_bookableresourcebooking_WorkOrder.msdyn_city}<br/>
{msdyn_msdyn_workorder_bookableresourcebooking_WorkOrder.msdyn_account_msdyn_workorder_ServiceAccount.name}<br/>
{msdyn_msdyn_workorder_bookableresourcebooking_WorkOrder.msdyn_account_msdyn_workorder_ServiceAccount.telephone1}
</div>

P.S. Don’t listen to Neil “New CRM Release – New Country” Benson who will tell you he was the one helping me out to figure out the layout.

Tip #763: Video introduction to PowerApps

Mobile developmentIn this video we introduce PowerApps. We will look at how to create an app and working with basic controls including creating a button to invoke a flow.

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.

Don’t forget to subscribe to http://youtube.com/crmtipoftheday.

Tip #762: Good news for Campaign Monitor fans

VaporwareWe rarely if ever post vendors news but considering the official demise of Dynamics Marketing, this one is hard to ignore.

We’ve long been a big fan of Campaign Monitor, the (proudly Australian) vendor very much focused on helping to create beautiful and actionable emails “your customers can’t ignore”. And they just announced integration with Dynamics CRM. Focusing solely on building marketing lists and pulling customer information into Campaign Monitor itself, it does not look like a product in the same league as ClickDimensions but then, again, it’s not pretending to be.

If you have Dynamics CRM and are Campaign Monitor user/fan, this is something worth exploring and it certainly gives you a very good immediate (and inexpensive) alternative to the Dynamics 365 for Marketing that is not going to see the light of the day until Spring 2017.

Tip #761: Script error in main.aspx on line 1

FragileWhen creating fine-tuned roles for restricted access to your Dynamics CRM deployment, be very careful about privileges granted on Customization tab in role editor. Some of the privileges are easy to overlook and, if not granted, that can break the user experience.

What I learned today is that CRM is very sensitive about Process privileges. The restricted users did not need to access any processes at all so I removed all the privileges. And so it began:

  • Custom form scripts wouldn’t run
  • Navigating away from the form pops up an error message
  • Error details refer to script error in main.aspx (1,1) – i.e. line 1, column 1
  • Script debugging would stop in the file on the very first line which is, errr, blank?!

Long story short, granting read privilege in the user scope to Process fixed the issue. I think it’s related to the code that builds the ribbon and, even though users don’t need and don’t have access to any dialogs in the system, the code is not defensive enough and breaks when trying to render Start Dialog button. I suspect removing the button from the ribbon might help as well but read access to user-owned processes (and they owe none) seems harmless enough.

Tip #760: Scope your workflows

Baby activity centerAdmit it: when you create a new workflow, one of the first actions is to set the scope of the workflow to Organization. After all, when something changes, or new record is created, we want apply our business rules across the board, right?

Workflow scope

Most of the time you’d be correct. But this is an opportunity missed. Using a combination of security privileges related to the processes, activation of the real-time workflows, and execution of the workflow jobs you, as an administrator, can empower users to customize processes within the Dynamics CRM to suit their, or their business units needs.

Workflow security

For example, a user can create a private process to help them to deal with the workload; some people love email reminders – let them create as many as needed! A business unit dealing with customer support for VIP customers may use an escalation workflow to support their very important processes.

Would like to give users their own private real-time workflows but cannot trust them to get it right without blowing server to pieces? Take away Activate Real-time Processes privilege and activate the processes they create only after a thorough review.

Opportunities are limitless, just remember to plan and document the security permutations. A good introductory write-up on scoping the workflows is available, if you’d like to dig deeper.