Tip #815: Make your Dynamics 365 portal speak local language

Swedish ChefAs of the time of writing, Dynamics 365 supports 45 languages. But there is only one English, one Spanish, and one French (to name a few) which makes people in UK, Australia, entire South America, and Canada fairly disappointed, considering that the list of locales contains about 230 entries.

We learned to deal with the absence of the localised flavours in Dynamics 365 but, as it turned out, we do not have to when it comes to the portals. Portals for Dynamics 365 have been released in 43 languages (all supported sans Arabic and Hebrew), and the process of enabling content in another language is straightforward:

  1. Enable additional languages in Dynamics 365
  2. Navigate to Portals > Websites in Dynamics 365 and open the Website record. Add Website Language record under Supported Languages
  3. All applicable entities, e.g. web pages, web link sets and content snippets, will now have multi-lingual content available

But, wait, there is more! When you add a Website Language record, language lookup field comes from the Portal Languages entities.

  1. Navigate to Portals > Portal Languages
  2. Add new record and enter the data for the localized language.
    Language of Australia
  3. The fields are as following:
    • Name: whatever you feel like
    • Display name: this part will be visible in the language selector
    • Description: meaningful one, of course (but nobody will ever see it)
    • Dynamics 365 language: this one is important. Must match one of the 43 supported languages
    • LCID: valid .NET language locale identifier (see the link to the list above)
    • Code: this will form part of the URL, as in
      https://contoso.com/en-AU/about-us
  4. Once language is there, follow the steps above to add it to the list of the supported portal languages.
  5. Deliver your content as required in the worlds of organisations, colours, and sebras.
    Portal language selector

Can’t wait for our Nordic friends to start delivering portals in sv-BORKBORK.

Tweet about this on TwitterShare on FacebookShare on Google+

Tip #814: Setting Regarding field in Microsoft Flow

When you use Microsoft Flow to create a Dynamics 365 activity record, e.g. task, it’s a good practice to relate the activity to another record by setting up Regarding field.

Use dynamic values and drop the related record into the Regarding box, right? If you do just that, your flow may fail with the following error

Body
{
  "status": 400,
  "message": "Ambiguous binding is present. 
             Resolve by sending lookup_type property.",
  "source": "127.0.0.1"
}

Aha! – you say, I noticed there is a Regarding Type field right next to Regarding so I just set that one and be done. But what do you need to set this field to? incident? 112 (entitytypecode value)?

Under the hood Microsoft Flow uses Web API to communicate with Dynamics CRM and, sadly, the decision was made to use plural names in Web API. So the correct value is incidents.

Set regarding field in Microsoft Flow

Task will be created, linked to the case, and they will live happily ever after.

Tweet about this on TwitterShare on FacebookShare on Google+

Tip #813: Renaming default app

I couldn’t believe that there is something in Dynamics 365 world that Scott “Mr. Ribbon Workbench” Durow doesn’t know!

Scott

When importing solutions from 8.1 into 8.2 or upgrading orgs – is there a trick to rename the default app from ‘Dynamics 365 – custom’ to something else without having to create a new app?

In the short term I have customers who don’t want to us the separate App sitemaps – and want to stick with the ‘old’ single site map – however they don’t like seeing ‘custom’ in the name.

I know that https://www.microsoft.com/en-us/dynamics/crm-customer-center/business-apps-in-dynamics-365.aspx describes this ‘custom app’ providing ‘access to the full suite of capabilities’ but all we need to do is drop the ‘custom’ bit from the name!

First to the rescue Andrhe “I’ve got 88 in my handle” Margono

Andre

If my memory serves me right, there is a settings under system settings to rename this default app.

Default app name

Settings > Administration > System Settings, and tucked at the very bottom, to be precise – t.j.

Tweet about this on TwitterShare on FacebookShare on Google+

Tip #812: CRM Undo

Occasionally I get the question, does Dynamics CRM/365 include an undo/redo feature.

While the application does not have an undo button, it does support the features of your operating system, and if that OS is Windows (any version), you can use the keyboard shortcuts CTRL+Z to undo, and CTRL+Y to redo.

Note that this is limited to undo/redo in the current field. It’s useful if you overwrite a field value by mistake, but keep in mind that if I tab or click to another field, it won’t undo what I did in the previous field.

Tweet about this on TwitterShare on FacebookShare on Google+

Tip #811: Multilingual portals and external authentication providers

After on and off hiatus caused by the festivities season we are back with another annual round of the daily tips.

If you have external authentication providers all worked out in your Dynamics 365 Portal, and then you enable multilingual feature so that your portal looks fine in, say, Kazakh (yes, it’s one of the officially supported 43 languages), you may find that the content now would have a different URL depending on the language. For example https://foobar.com/contact-us would become https://foobar.com/en-US/contact-us or https://foobar.com/kk-KZ/contact-us, depending on your audience choice.

That URL change will break authentication with the external providers and users trying to logon will see Page Not Found with the URL resembling https://foobar.com/en-US/signin-microsoft?code=etc.

Solution:

  1. Change site setting MultiLanguage/DisplayLanguageCodeInURL to False. That will ensure “old-style” navigation with the current language passed around in a cookie instead of a URL.
  2. If you are keen to retain language identifiers in your URLs, change callback/redirect URL on your provider side to include the code of any of the supported by your portal language code, e.g.
    https://foobar.com/kk-KZ/signin-microsoft, add the site setting
    Authentication/OpenAuth/Microsoft/CallbackPath and set it to
    /kk-KZ/signin-microsoft

 

Tweet about this on TwitterShare on FacebookShare on Google+

Tip #810: Using Fiddler with Xrm Tooling

Sometimes it’s very useful to see what requests are flying forth and back between your code and Dynamics 365. The tool of trade is, of course, Fiddler. Fiddler allows https traffic analysis by using man-in-the-middle interception using self-signed certificates.

If you are using Xrm Tooling and, specifically, CrmServiceClient then you quickly find that the following code:

var s = 
 @"Url=https://a.crm.dynamics.com;AuthType=Office365;
   UserName=foo@a.onmicrosoft.com;Password=strongone";

using (var crmSvc = new CrmServiceClient(s))
{
   Console.WriteLine($"{crmSvc.IsReady}");
   if(crmSvc.IsReady) 
   {
      Console.WriteLine(
      $"{crmSvc.ConnectedOrgFriendlyName}");
   }
   else 
   {
      Console.WriteLine("Doh!");
   }
}

generates a friendly “Doh!” instead of a friendly organization name.
The logs will show the following error: The remote certificate is invalid according to the validation procedure, and it’s fair enough as Fiddler certificates are self-signed.

If you do want that trace, there is a silver bullet and a brilliantly concise gun to fire it. Just add the following code before creating CrmServiceClient:

ServicePointManager
   .ServerCertificateValidationCallback +=
   (sender, cert, chain, sslPolicyErrors) => true;

Happy debugging!

Tweet about this on TwitterShare on FacebookShare on Google+

Tip #809: When You Can’t Remove Project Service

Say you installed the PSA solution in your sandbox environment, and you decided that you aren’t quite ready for it, so you want to remove it. However, when you try to uninstall the solution, the process fails, but the log gives you no indication about what is causing the conflict.

This was where we found ourselves recently. Thanks to some help from Microsoft support, we were able to find the problem.

It turns out that when the system was configured, a global option set was used for birthday day that was actually part of the Project Service solution. This dependency kept the solution from uninstalling. Once the option set was changed to another custom global option set, the solution uninstalled without issue.

Lesson learned: check your global option sets, as they may not show in the dependency list when uninstalling solutions.

Thanks to Jerry Martin for his second tip suggestion.

 

Tweet about this on TwitterShare on FacebookShare on Google+

Tip #808: Tracing with Xrm Tooling in Azure Functions

By now you should be able to get your Azure Function triggered from Dynamics 365 and connect back to Dynamics 365 to do some evil awesome things. Debugging Azure Functions however, is not a walk in a park – not like you can set breakpoints and step through the code.

Well, you should, of course, write some nice classes doing the job, test them using test harness or, at very least, a simple console application and then refer to those classes in your Azure Function code.

If something goes wrong in production and you need to get to the bottom of it, all Azure Functions entry points provide TraceWriter that you can use to dump the information required. That’s in your code but what about mysterious internals of Xrm Tooling black box? What if your code cannot connect to Dynamics 365 instance?

TraceWriter is an abstract class implemented in Azure WebJobs SDK while Xrm Tooling utilizes System.Diagnostics Debug/Trace/TraceListener model.

Create additional file ToolingListener.csx

using System;
using System.Diagnostics;

// Log writter for working with Azure Functions 
// for the Xrm.Tooling.Connector
public class Toolinglistener : TraceListener
{
  private TraceWriter _log;

  public Toolinglistener(string name, 
            TraceWriter logger) : base(name)
  {
    _log = logger;
  }

  public override void Write(string message)
  {
    _log?.Info(message);
  }

  public override void WriteLine(
            string message)
  {
    _log?.Info(message);
  }
}

Then add the following to the original function:

#load "ToolingListener.csx"

using System;
using System.Diagnostics;
using Microsoft.Xrm.Tooling.Connector;

public static void Run(string input, TraceWriter log)
{
    TraceControlSettings.TraceLevel = SourceLevels.All;
    TraceControlSettings.AddTraceListener(
		new ToolingListener(
			"Microsoft.Xrm.Tooling.CrmConnectControl", 
			log));

And you should see very detailed output in your function log:


Microsoft.Xrm.Tooling.Connector.CrmServiceClient Information: 8 : Using User Specified Server
Microsoft.Xrm.Tooling.Connector.CrmServiceClient Information: 8 : Trying Live Discovery Server, (North America) URI is = https://disco.crm.dynamics.com/XRMServices/2011/Discovery.svc
Microsoft.Xrm.Tooling.Connector.CrmServiceClient Start: 256 : QueryLiveDiscoveryServer()
Microsoft.Xrm.Tooling.Connector.CrmServiceClient Information: 8 : DiscoverOrganizations - Initializing Discovery Server Object with https://disco.crm.dynamics.com/XRMServices/2011/Discovery.svc
Microsoft.Xrm.Tooling.Connector.CrmServiceClient Verbose: 16 : DiscoverOrganizations - attempting to connect to CRM server @ https://disco.crm.dynamics.com/XRMServices/2011/Discovery.svc
Microsoft.Xrm.Tooling.Connector.CrmServiceClient Verbose: 16 : DiscoverOrganizations - created CRM server proxy configuration for https://disco.crm.dynamics.com/XRMServices/2011/Discovery.svc - duration: 00:00:11.1699811
Microsoft.Xrm.Tooling.Connector.CrmServiceClient Verbose: 16 : DiscoverOrganizations - proxy requiring authentication type : OnlineFederation
Microsoft.Xrm.Tooling.Connector.CrmServiceClient Verbose: 16 : DiscoverOrganizations - Authenticated via OnlineFederation. Auth Elapsed:00:00:03.2299574
Microsoft.Xrm.Tooling.Connector.CrmServiceClient Verbose: 16 : DiscoverOrganizations - service proxy created - total create duration: 00:00:14.4031174
Microsoft.Xrm.Tooling.Connector.CrmServiceClient Information: 8 : DiscoverOrganizations - Discovery Server Get Orgs Call Complete - Elapsed:00:00:15.9204376
Microsoft.Xrm.Tooling.Connector.CrmServiceClient Information: 8 : Found 1 Org(s)
Microsoft.Xrm.Tooling.Connector.CrmServiceClient Start: 256 : BuildOrgConnectUri CoreClass ()
Microsoft.Xrm.Tooling.Connector.CrmServiceClient Verbose: 16 : DiscoveryServer indicated organization service location = https://foobar.api.crm.dynamics.com/XRMServices/2011/Organization.svc
Microsoft.Xrm.Tooling.Connector.CrmServiceClient Stop: 512 : BuildOrgConnectUri CoreClass ()
Microsoft.Xrm.Tooling.Connector.CrmServiceClient Information: 8 : Organization Service URI is = https://foobar.api.crm.dynamics.com/XRMServices/2011/Organization.svc
Microsoft.Xrm.Tooling.Connector.CrmServiceClient Information: 8 : ConnectAndInitCrmOrgService - Initializing Organization Service Object
Microsoft.Xrm.Tooling.Connector.CrmServiceClient Information: 8 : ConnectAndInitCrmOrgService - Requesting connection to Org with CRM Version: 8.2.0.764
Microsoft.Xrm.Tooling.Connector.CrmServiceClient Information: 8 : ConnectAndInitCrmOrgService - Using ISerivceManagement
Microsoft.Xrm.Tooling.Connector.CrmServiceClient Verbose: 16 : ConnectAndInitCrmOrgService - attempting to connect to CRM server @ https://foobar.api.crm.dynamics.com/XRMServices/2011/Organization.svc
Microsoft.Xrm.Tooling.Connector.CrmServiceClient Verbose: 16 : ConnectAndInitCrmOrgService - created CRM server proxy configuration for https://foobar.api.crm.dynamics.com/XRMServices/2011/Organization.svc - duration: 00:00:37.6740783
Microsoft.Xrm.Tooling.Connector.CrmServiceClient Verbose: 16 : ConnectAndInitCrmOrgService - proxy requiring authentication type : OnlineFederation
Microsoft.Xrm.Tooling.Connector.CrmServiceClient Verbose: 16 : ConnectAndInitCrmOrgService - Authenticated via OnlineFederation. Auth Elapsed:00:00:00.5627215
Microsoft.Xrm.Tooling.Connector.CrmServiceClient Verbose: 16 : ConnectAndInitCrmOrgService - service proxy created - total create duration: 00:00:38.2390845
Microsoft.Xrm.Tooling.Connector.CrmServiceClient Information: 8 : ConnectAndInitCrmOrgService - Proxy created, total elapsed time: 00:00:38.2410960
Microsoft.Xrm.Tooling.Connector.CrmServiceClient Information: 8 : User Org (org358a83d1) found in Discovery Server NorthAmerica - ONLY ORG FOUND
Microsoft.Xrm.Tooling.Connector.CrmServiceClient Information: 8 : Beginning Validation of CRM Connection
Microsoft.Xrm.Tooling.Connector.CrmServiceClient Information: 8 : Validation of CRM Connection Complete, total duration: 00:00:08.3293161
Microsoft.Xrm.Tooling.Connector.CrmServiceClient Verbose: 16 : New Batch Manager Created, Max #of Batches:50000, Max #of RequestsPerBatch:5000

Of course, the code takes a shortcut with log.Info and requires a bit of tuning like mapping logging levels from Connector to TraceWriter but those are the details we can live without, right?

Tweet about this on TwitterShare on FacebookShare on Google+

Tip #807: Data refresh in Power BI using old endpoint fails

Today’s tip is from our almost regular Matt Johnson.

I was recently tasked with creating a Power BI dashboard based directly off the CRM (sorry, I mean Dynamics 365) data. We were in a bit of a rush and even though the performance isn’t great we only needed a few charts and it was only a POC. Using Power BI Desktop you can add a datasource and select Dynamics 365 form the list.

Power BI Get Data

Then it prompts you for the OData service (notice the format it is asking for)

Power BI URL Prompt

So you fill in your org as prompted, connect up as a user and away you go. You can build you dashboards/reports/datasets and refresh them to your hearts content.

Then when publish your report to the online Power BI service, it allows you to do it no problem. However, if you then try and refresh your dataset, you get an error,

Your data source can’t be refreshed because the credentials are invalid. Please update your credentials and try again.

Power BI Invalid Credentials

OK you might say, I’ll re-enter my credentials. So you change the Authentication Method from the default Anonymous to OAuth2 but you still get the error.

Power BI failed to update

After much searching I came across this TechNet article where it tells you that you must use the new web api in the format https://foo.api.crm.dynamics.com/api/data/v8.1 to connect to your org in PowerBI Desktop. Even though it prompts you for the older SOAP endpoints in the format https://bar.crm4.dynamics.com/XRMServices/2011/OrganizationData.svc.

Using the new web api allows you to get at all your data and publish the reports out to PowerBI online and then you can successfully connect and refresh your data.

Power BI Successfull Refresh
Power BI Successfull Connect using OAuth2

Thanks, Matt for the tip, as you said yourself – maybe it will save someone a lot of messing about!

Tweet about this on TwitterShare on FacebookShare on Google+

Tip #806: How to display portal version

The break was short-lived! Without further ado, first tip of 2017.

If you navigate to your Dynamics 365 Administration Center and display all solutions installed in your organization, you will be able to see the version of the solution(s) installed but not the version of the portal code itself.

Just add /_services/about to your portal url:

Portal version

In addition to the version, you’ll see a guid that identifies the portal product id within your O365 tenant. Both pieces of information would come handy when raising support requests for any issues related to your Dynamics 365 Portal.

Tweet about this on TwitterShare on FacebookShare on Google+