Calling the Dynamics Global Discovery Service

With the launch of Dynamics 365 comes a global version of the CRM Discovery Service that can be used to find instances across regions. This is a Web API only service and with this service you will no longer have to prompt users for their region, as the service can return a list of all instances from all regions at once. For developers building CRM applications this is an important service to be aware of as it can greatly improve the user experience of the the application since users no longer will have to indicate their region before querying for a list of instances.

You can read the documentation for the Global Discovery Service on the Microsoft Developer Network (MSDN) – Discover the URL for your organization using the Web API.

One of the key aspects of using the new Global Discovery Service is getting an authorization token for the Dynamics Web API that allows you to properly access this resource. This example will utilize the new Server-to-Server (S2S) authentication that has been made available in Dynamics 365 as well. To learn more and setup S2S authentication you can view the MSDN documentation, Build web applications using Server-to-Server (S2S) authentication.

Firstly you will need to initiate a OpenID challenge to get an authorization code that we can then use to acquire a token to use in Web API calls to Dynamics. You can do this using the OWIN method described in the in Walkthrough: Multi-tenant server-to-server authentication (note this can be followed even if not creating a multi-tenant application) or you can make this challenge manually without using OWIN. We use the authorization code returned to then request a token to a particular resource, like a Dynamics instance or the discovery service it self.

To call the global discovery service you will need to request a token for the resource https://disco.crm.dynamics.com/ not the global discovery service address https://globaldisco.crm.dynamics.com/. Below is an example that utilizes ADAL (Active Directory Authentication Library) to retrieve a token that will allow that request against the desired Azure AD tenant with client credentials (client id + secret), the authorization code, return URI as well as the resource indicated above.

// setup parameters for authentication context
var tenantId = "<guid-of-azure-ad-instance>";
var aadInstance = "https://login.microsoftonline.com";

// define auth context
var authContext = new Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext($"{aadInstance}{tenantId}");

// setup parameters to get token
var code = "<code-returned-from-OpenID-challenge>";
var credential = new ClientCredential("<ClientId>", "<ClientSecret>");
var uri = "<same-return-URI-from-OpenID-challenge>";
var resource = "https://disco.crm.dynamics.com/";

// request token with parameters
var authResult = await authContext.AcquireTokenByAuthorizationCodeAsync(code, uri, credential, resource);

Once you have your token we will now use it in a call to the global discovery service. The token you received back must be included in the request authorization header in the call to the global discovery service Web API endpoint https://globaldisco.crm.dynamics.com/api/discovery/v1.0/Instances.

// setup http client for GET of instances from global discovery service
var discoEndpoint = "https://globaldisco.crm.dynamics.com/api/discovery/v1.0/Instances";
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authResult.AccessToken);
HttpRequestMessage request = new HttpRequestMessage(new HttpMethod("GET"), discoEndpoint);

// send http client request
var response = await httpClient.SendAsync(request);

The first thing we are going to want to do with the response is validate that is was successful.

 // validate status code OK
if (response.StatusCode != HttpStatusCode.OK)
{
    throw new Exception($"Global Discovery Service response status code: {response.StatusCode}");
}

If it was not successful then review the WWW-Authenticate in the response header to check to see if it wants another resource to be specified in the token (it might also be as simple as you missed a trailing slash in the resource). If you do see another resource indicated in the response you will need to re-request a token with the resource specified in the response header then re-make your request to the global discovery service. With the refresh token that is included in the authentication result of the AcquireTokenByAuthorizationCodeAsync you can easily re-request a token for a new resource instead of requiring the authorization code. ADAL will cache all your tokens and you will build up a multi-resource token repository by using this method. Below is an example of using the refresh token to now access the Canadian discovery service after getting the following response with the WWW-Authenticate in the header.

Bearer authorization_uri=https://login.windows.net/common/oauth2/authorize, resource_id=https://disco.crm3.dynamics.com/
var newResource = "https://disco.crm3.dynamics.com/";
var authResultCRM4 = await authContext.AcquireTokenByRefreshTokenAsync(authResult.RefreshToken, credential, newResource);

Note: The tenants I have worked with thus far have all used the North American Discovery Service resource (even tenants started on CRM4 for instance).

Once you have successfully been able to get a response with a HTTP status code of OK using the correct token, the body of the response should be a JSON string that can be converted to an object for you to bind or return to other interfaces. The response JSON content if returning the entire instance object should deserialize into a list of the following DiscoInstanceModel class.

public class DiscoInstanceModel
{
    public Guid Id { get; set; }
    public string UniqueName { get; set; }
    public string UrlName { get; set; }
    public string FriendlyName { get; set; }
    public int State { get; set; }
    public string Version { get; set; }
    public string Url { get; set; }
    public string ApiUrl { get; set; }
    public DateTime LastUpdated { get; set; }
}

You can deserialize the data using Newtonsoft.Json.

// read response content to string
var data = await response.Content.ReadAsStringAsync();

// parse the string to a JObject and deserialize to list of DiscoInstanceModel from the content value
var dataValues = JObject.Parse(data); 
var discoInstances = JsonConvert.DeserializeObject<List<DiscoInstanceModel>>(dataValues["value"].ToString());

The service just like the other Dynamics Web API services provided support for $select, $filter as well as $metadata with the instances entity. So if for instance you wanted to get back only instances of a certain version you could do so with the following filter for all those v8.2.x instances.

https://globaldisco.crm.dynamics.com/api/discovery/v1.0/Instances?$filter=startswith(Version,'8.2.')

A lot of the code provided here is meant as a sample, it will need to be adapted to how you will use this service within your own applications. There are lots of ways of using ADAL to obtain and maintain a token to access the service, so it will require you to implement one that works for individual use cases.

Dynamics 365 trials now include portals!

If you create a new Dynamics 365 trial since around the holiday time frame you may have noticed that a portal can be configured as part of this trial. If not read on for instructions on how to configure your portal as part of the trial.

Once you have created your Dynamics 365 trial, (suggested to select the “All of these” option when provisioning your trial) you should be taken to your Dynamics 365 instance.

trial_options

You will need to get to the Dynamics 365 Administration Center to be able to configure your portal as part of the trial. You can do so by navigating to the Office 365 Admin Center or by directly going to the following URL – https://port.crm3.dynamics.com/g/manage/index.aspx. You may need to change crm3 to whichever region your instance resides in, ie CRM for NA/USA, CRM3 for Canada – the full list of regions can be found here – http://www.xrmcoaches.com/2016/01/current-list-of-dynamics-crm-online-regions/.

Once into the administration center, select Applications from the top and you should have a number of applications listed if you selected the “All of these” choice when provisioning your demo including a Not Configured portal! Highlight the portal application called “Portal Add-On” in the list and select Manage.

portal_applications

You should now be able to continue configuring your portal with your desired configuration for your demo or trial.

If you have not had a portal add-on added automatically to your trial you can still request a trial key from the the CRM Managed Trial site – https://crmmanagedtrials.dynamics.com/.

Thanks to Adoxio’s Kunal Tripathy for pointing out this great addition to trials.

Note: This post also appears on Adoxio Business Solutions Team Blog.

Using your Dynamics 365 portal in an iframe

There might be certain scenarios where you want to utilize certain parts of Dynamics 365 portals functionality within other applications. Most often this is where another content management system (CMS) is already in place but you want to enhance its abilities and add new functions that work with your CRM. Instead of writing customizations in that CMS it can be easy to configure your desired functionality with the out of box functionality of Dynamics 365 portals.

With Dynamics 365 portals through you might run into a issue with embedding the site in an iframe. If trying to embedding your portal in an iframe you will end up with a blank frame. Opening up Chrome’s developer tools and view the console you will notice the following error providing a hint as to the issue.

Refused to display 'https://mps155013.microsoftcrmportals.com' in a frame because it set 'X-Frame-Options' to 'SAMEORIGIN'.

X-Frame-Options is part of the HTTP response header and can be used by the web server to control who can display your content directly in an iframe. This is important if you want to prevent any sort of embeds of your site as well as limit it to an allowed list of sites. You can read more about X-Frame-Options on the Morilla Developer Network Documentation.

The following are the X-Frame-Options directive options:

  • DENY – blocks all iframe requests
  • SAMEORIGIN – allows iframe’s from the origin site
  • ALLOW-FROM https://example.com/ – allows iframe’s from the URL

You can manipulate the X-Frame-Options of your Dynamics 365 portal through the site setting HTTP/X-Frame-Options, inserting in the value one of the options above. New portals should include the setting by default in all portals with the default value set to SAMEORIGIN. If you do not have the site setting from an older portal that has upgraded, you can add it but it requires the supporting portal code version (v8.2).

Some other considerations when embedding your portal will also be the display of the page. Most likely the site your embedding into will already have a header with navigation and also a footer, so it might not make sense to include these type of components on the pages your iframe will display.

You can handle this in various ways with the portal, either through simply putting some CSS only on iframe pages that hide elements, keep in mind that the Bootstrap CSS uses the width of your frame and not the parent window so you may be in a mobile layout inside the iframe. Another option is to use the Page Template with the Use Website Header and Footer when using the Web Template type. Leaving this option set to false will only render the contents of the liquid template, it will leave out all portal scaffolding like Bootstrap CSS, and JavaScript libraries. If you need those type of components you will need to add them back through your own references in your web template. The web template option is great for data display type options, if you want to use entity form or list then you will require that portal scaffolding for those components to work.

usewebsiteheaderfooter

There is a hidden rewrite template that does not have a header or footer you can use if your only need is an entity form. Create a new page template with the type Rewrite and set the Rewrite Url to ~/Areas/Portal/Pages/Form.aspx. This page template is used by entity list to display modal dialog forms for entity list create and edit functions. This page template if you look at the v7 code only contains an entity form control and sets the content controls for header and footer to blank, it does not even include the page copy.

norewrite

Based on the functions you want to accomplish and the security you require for the content in your iframe you will need to select the best method for displaying your page.

What’s New in Dynamics 365 portals

A number of the Dynamics 365 application services; Field Service, Project Service and portals, all became generally available this week following the release of Dynamics 365 earlier this month. These all offer a number of improvements and new features from their previous iterations under Dynamics CRM. Previously called CRM portals, now Dynamics 365 portals, has received some significant updates, even some functionality that starts to bring the 3 application services together with Project and Field Service functionality now in some portals. Let’s take a look at some of the largest changes in this release:

  • Multilingual support for a single portal
    Any of the now 43 languages can be configured on a portal and no longer requires a completely separate portal service for each language.  This functionality also significantly differs from how multilingual support was included in the legacy Adxstudio Portals. You will find a new entity, Website Languages that adds an additional layer, this instead of duplicating the entire web site using the old web site copy tool.  The page copy section is no longer directly on the web page information form but now available through a localized content relationship which will display the web page in a new content mode and other content data entities like content snippet or site settings also have new relationships to the website language.

    mlp_webpage
    There are also new liquid extensions to support this functionality within your own templates, website.languages will provide a collection of all the language items, pages.languages is a list of languages for the current page available, and website.selected_language will identity what language the site is currently being shown in.
  • Charts displayed in the portal
    You can now embed your CRM/Dynamics 365 charts directly on the web with a new liquid extension.  For an example of charts, if you install the latest partner portal there is a new web template called “Partner Dashboard” that provides an example of embedding multiple charts, entity lists and other components on a single page to give a rich interface to portal users. The liquid extension makes it very easy to add a chart anywhere in a page, all you need is the chart ID and view ID the chart should use for its data source.

    {% chart id:"chart-guid-without-braces" viewid:"view-guid-without-braces" %}
    
    <!-- Est. Revenue by Est. Close Date (Day) - Opportunities Closing Next Month --> 
    {% chart id:"EB02E9AA-5580-E611-80D8-00155DFE75F9" viewid:"00000000-0000-0000-00AA-000010003002" %}
    

    portalcharts

  • Content Access Level Security for Knowledge Management Articles
    Content Access Level or CAL is a feature that has been added to allow for securing access to knowledge articles to different groups of users. In Dynamics 365 with a portal installed you will find some sample CALs but you can also define your own and associate them to contacts and articles. Article content will be secured regardless of how you try accessing the content, either browsing via navigation elements, direct URL or via the Portal Search, all will respect the security rules defined by the CAL.
  • Project Service and Field Service Portal Extensions
    If you provision a new Partner Portal and have either Project Service or Field Service installed you will see some new options available that allow you to enable portal functionality for these components, there are also new actions if you have an existing portal to install the new portal extensions.

    provision_psaThese extensions bring some components to the portal, with PSA your able to view customer and partner related projects or resources. With Partner Field Service extension you can see related field service agreements, assets, service requests, and work orders.
  • Partner Portal Improvements
    There are a number of improvements and new functionality added to the popular partner portal including a new partner onboarding/application, multi-partner collaboration, and also deal registration.

These are all great improvements for this release and continue to look forward to further updates with the monthly updates to Portal Service in the new year. If you want to check out these features and more you can try the latest version yourself by requesting a trial.

For more information about what’s new for Field Service and Project Service a breakdown is available on the What’s New for Dynamics 365 Help and Training site.

Note: This post also appears on Adoxio Business Solutions Team Blog.

Adxstudio Portals v7.0.0023 Released

A new version of Adxstudio Portals has just been released on the Adxstudio Community site. You can head over to download the latest version, 7.0.0023 here.

This version has a number of much needed fixes to bugs that were introduced in v7.0.0022. One in particular that was keeping a lot of implementations away from 7.0.0022 was the anonymous web forms bug. From the release notes posted they look to have addressed this and a number of other fixes.

Another major addition in this release is the web notifications can now be configured directly in CRM for what entities will cause a notification to the portal. The default in previous releases was all entities unless you manipulated this through the plugin registration tool. There is now a Web Notifications Entities that the web notification plugin will use to determine whether to send the message or not. This should be a huge boost to portal performance but now be-aware to add your new custom entities to this list if you want that data also invalidated on the portal. You can read more about this configuration here.

This release unfortunately does not fix the compatibility of Adxstudio v7 and Dynamics 365 but this new release is still very welcome with all the improvements and new cache invalidation functionality.

Update 12/1/2016: Andrew Chan from Adoxio –
Please be aware that a “settings.xml” file is packaged with the sample project under the App_Data folder. Please remove this file when you spin up a new project. Without doing so, this file will cause the Portals to try and connect to an already configured CRM organization that isn’t available, and it will ignore a CRM specified in the connection string in web.config.

Note: This post also appears on Adoxio Business Solutions Team Blog.