{Knowhow} How to execute Web API calls to Microsoft Dynamics CRM from an external ASP.NET web application

Microsoft Web API has delighted all and specially if you have a penchant towards exploring the API features, then Web API must have thrilled you by this time. Many consultants and experts, including myself, have written blogs on how to leverage the Web API features in Dynamics CRM.

But all these far, all the examples have been written in javascript. To elaborate most of the examples are on

  • Using web API from within Microsoft Dynamics CRM webresources
  • Using web API from an external HTML page using javascript and adal.js

Web API uses OAuth 2.0 protocol for authorization. However when you are making Web API calls from within Dynamics CRM, the entire nuances of OAuth 2.0 authorization is hidden from you. All you need to do is send a well formatted request and get the response back, parse the json response and get the data back in the application to use.

For the second example, although it will expose you greatly to the nuances of OAuth 2.0 protocol  but there is a subtle difference when you execute Web API calls from browser based application (like javascript/ jquery) and from a server based application like ASP.NET web application. The main difference is that in the first case, you would not need the client secret to get the access token. The authentication is implicit and you get the access token in just one call to the authorization endpoint. For the second case you would need to first get the authorization code by asking the end-user to enter his credentials and then once you get the authorization code, you would need to make a second call to the token endpoint to get the access token using authorization code + client_id + client_Secret.

If you are not aware of the OAuth 2.0 mechanisms, I would strongly suggest to first check for the OAuth documentations or you may go through my below blog post.

https://debajmecrm.com/2016/02/23/understanding-in-depth-cross-origin-resource-sharing-cors-in-dynamics-crm-2016/

The above post first gives you an introduction to the OAuth 2.0 protocol and then how to use that to connect to Dynamics CRM Web API endpoint from an external HTML Page. The post also covers on how to register the application in azure AD and get the client id and the client secret. Before you read further I would strongly suggest you go through the above post and understand how OAuth 2.0 works from CRM perspective.

After I wrote the above blog post, I received lot of requests personally if I can provide and code of how to make the same call from ASP.NET web application. Well your wish is granted. Here I will show you end-end on how to fetch data from Dynamics CRM Web API from a ASP.NET web application.

 

  • Create a ASP.NET web application project with a single web page. I named it default.aspx.
  • Go to the design mode and the markup should look like below.

image

The markup should contain

  1. A Login button which when the user clicks will take him/ her to the authentication screen.
  2. The label will display the name of the user once the user is authenticated.
  3. Get Accounts Button will retrieve the list of accounts when clicked.
  4. GridView will show the list of accounts retrieved once the Get Accounts button is clicked.

The above link explains how to add Dynamics CRM AD in Azure and register the application in Dynamics AD and how to get the client id and the client secret. Please make a note of the client id and specially the client secret which is visible immediately after saving. Please note that the client secret is visible only for the first time. If you come back later, you won’t be able to view it.

  • For this demo, I will keep these values in the web.config file. However do not keep the client id and client secret in the web.config specially the client secret. It’s very sensitive information. You should have some better ways of managing the same.

<appSettings>
<add key=”client_id” value=”f8a39dc3-338b-4018-b67e-6dbaa846f469″/>
<add key=”client_secret” value=”<your client secret>”/>
<add key=”tenant” value=”xrmtr14.onmicrosoft.com”/>
<add key=”redirect_url” value=”
http://localhost:52432/default.aspx”/>
<add key=”resource” value=”
https://xrmtr14.crm.dynamics.com”/>
</appSettings>

  • Now add a reference to the Active Directory authentication library and NewtonSoft.json.dll. The best way to do this is using Nuget Packages. For Active Directory Authentication Library type ‘Microsoft IdentityModel’ in the search box.

image

image

  • Now come to code-behind file (default.aspx.cs) and copy the entire code from below.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

using System.Configuration;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System.Net;
using System.Web.Script.Serialization;
using Newtonsoft.Json;

namespace DynamicsCRMWebApiConnector
{
public partial class _default : System.Web.UI.Page
{
const string AuthorityUri = “
https://login.windows.net/{0}/oauth2/authorize”;
        const string CompleteAuthUri = “https://login.windows.net/{0}/oauth2/authorize?response_type=code&client_id={1}&resource={2}&redirect_uri={3}”;
        Configurations _configuration;

        protected void Page_Init(object sender, EventArgs e)
{
this.PopulateConfigurations();
}

        protected void Page_Load(object sender, EventArgs e)
{
var queryString = Request.QueryString;

            // means authorization code has been received
if(queryString != null && queryString[“code”] != null)
{
var authCode = queryString[“code”];

                // get the access token using the authorization code
TokenCache tokenCache = new TokenCache();
AuthenticationContext authContext = new AuthenticationContext(string.Format(AuthorityUri, _configuration.ADTenant), tokenCache);
ClientCredential clientCredentials = new ClientCredential(_configuration.ClientId, _configuration.ClientSecret);

                AuthenticationResult authResult = authContext.AcquireTokenByAuthorizationCode(authCode, new Uri(_configuration.RedirectUri), clientCredentials);

                if(authResult.UserInfo != null)
{
this.UserInfoLabel.Text = string.Format(“Welcome: {0} {1}”, authResult.UserInfo.GivenName, authResult.UserInfo.FamilyName);
Session[“AuthResult”] = authResult;
}
}
}

        protected void Login_Click(object sender, EventArgs e)
{
var authorityUri = this.GetAuthorizationUrl();

            Response.Redirect(authorityUri);
}

        protected void GetAccountsButton_Click(object sender, EventArgs e)
{
if (Session[“AuthResult”] != null)
{
var authResult = (AuthenticationResult)Session[“AuthResult”];
var webRequest = (HttpWebRequest)WebRequest.Create(new Uri(“
https://xrmtr14.crm.dynamics.com/api/data/v8.0/accounts?$select=name,address1_city&$top=10″));
webRequest.Method = “GET”;
webRequest.ContentLength = 0;
webRequest.Headers.Add(“Authorization”, String.Format(“Bearer {0}”, authResult.AccessToken));
webRequest.Headers.Add(“OData-MaxVersion”, “4.0”);
webRequest.Headers.Add(“OData-Version”, “4.0”);
webRequest.ContentType = “application/json; charset=utf-8”;

                using (var response = webRequest.GetResponse() as System.Net.HttpWebResponse)
{
//Get reader from response stream
using (var reader = new System.IO.StreamReader(response.GetResponseStream()))
{
var accounts = new List<Account>();
string responseContent = reader.ReadToEnd();

                        dynamic dynamicObj = JsonConvert.DeserializeObject(responseContent);

                        foreach(var data in dynamicObj.value)
{
var account = new Account
{
AccountName = data.name.Value,
City = data.address1_city != null ? data.address1_city.Value : string.Empty
};

                            accounts.Add(account);
}

                        AccountsGridView.DataSource = accounts;
AccountsGridView.DataBind();
}
}
}
}

        #region Methods

        private void PopulateConfigurations()
{
_configuration = new Configurations
{
ADTenant = ConfigurationManager.AppSettings[“tenant”],
ClientId = ConfigurationManager.AppSettings[“client_id”],
ClientSecret = ConfigurationManager.AppSettings[“client_secret”],
RedirectUri = ConfigurationManager.AppSettings[“redirect_url”],
Resource = ConfigurationManager.AppSettings[“resource”]
};
}

        private string GetAuthorizationUrl()
{
return string.Format(CompleteAuthUri, _configuration.ADTenant, _configuration.ClientId, _configuration.Resource, Server.UrlEncode(_configuration.RedirectUri));
}

        #endregion
}
}

public class Account
{
public string AccountName { get; set; }

        public string City { get; set; }
}

 

public class Configurations
{
public string ClientId { get; set; }

        public string ClientSecret { get; set; }

        public string ADTenant { get; set; }

        public string Resource { get; set; }

        public string RedirectUri { get; set; }

    }

  • I have created two classes ‘Account’ and ‘Configurations’ in the above the code. Check for the class definitions just above this line.
  • So let me explain the code a bit here. In the Page_Init method, I retrieve all the values from the web.config file and populate the Configurations class’s object with those values.
  • Next look for the code in the Login Button Click handler Login_Click. First we get the URI where the user would be redirected to authorize himself. In that URI we pass the redirect url as well so that when the authentication completes, the control comes back to this page only.
  • Once the user authenticates, the control will come back to this page and the Page_Load event would fire. The AuthorizationCode would be present in the querystring of the url with the key=code.
  • the Page_Load checks for the authorization code in the query string and then it send another request for the access token. This time it sends the authorization code + client id + client secret. The code makes use of the Microsoft Active Directory Authentication library to get the access token.
  • Finally when the ‘Get Accounts’ button is clicked, a HttpWebRequest is made with the token specified in the header of the request and we get the list of accounts. If everything works fine, your final thing would look something like below.

image

Hope this helps!

Show all Contacts whose Birthday falls this week in Microsoft Dynamics CRM 2016/ 2015 Online Update 1

I came across this interesting requirement today and hence thought of penning down the solution. Please note this solution is for only for CRM 2015 Online Update 1/ CRM 2016. This solution does not target versions below that.

So from the title of the topic, the requirement is very obvious. I need a view which would show all contacts who birthdate falls this week.

So let us together see, how we can reach a solution here.

  • · First I create a new field of type datetime called ‘Date of Birth’. You might be wondering there is already an existing called Birth Day in the contact form. Then why I did not use it? There is a reason for this. I will come back to this. As of now just keep going here. Below is the screenshot for the schema definition for this Date of Birth field.

clip_image002

  • · Now create a new field called Age of type Whole Number. This would be a calculated field. Below is the screenshot of the calculation for the age field.

clip_image004

  • · Create another field called ‘Upcoming Birthday’. The field would be of type ‘Date and Time’ and will also be a calculated field. The schema and calculation formula of the field is shown in the screenshot below.

clip_image006

clip_image008

  • · All set and done. Before you leave customizations window, make sure you put the custom field ‘Date of Birth’ on the contact form to capture date of birth of the contact and remove OOB field. Thinking again WHY? Don’t worry I won’t leave in you dark. Will come back to this.
  • · Now all you are left is create a view for the contacts with the following view definition

clip_image010

And voila you are done! Wasn’t that easy.

Now coming to the topic, why I did not use the OOB Birth Date field of the contact entity. The reason is because for age calculation I needed to use the Now() function (you can refer to the above screenshots). This Now() function can only be used with date time fields which have a behavior of ‘User Local’. The OOB Birth Day field has a behavior of Date only and hence we could not use it.

Hope this helps! Happy CRMing.

Understanding in Depth – Cross Origin resource sharing (CORS) in Dynamics CRM 2016

First don’t get overwhelmed by the title of the topic. All this means how to connect to CRM from a standalone HTML Page using the web-api. Prior to CRM 2016, you cannot connect to CRM OData service from any HTML Page outside dynamics CRM. However with CRM 2016 you are able to do this.

If you search by CORS in SDK, you would find entire walkthrough of how to do this. I am going to show the same example here. So why a repeat of already what’s there in SDK.

Well if you are expert in OAuth 2.0 and know how it works you can perhaps easily map the concept to the steps provided in the SDK. However if you are not so sure about OAuth 2.0 you may find extremely difficult to understand what’s going on behind the scenes. And believe me, some experienced Dynamics CRM consultants have walked up to my desk and asked for help to figure this stuff out. So penning it down so that other’s might find it useful too.

First I will give a brief introduction of how OAuth 2.0 works and then will draw an analogy of how the same works in Dynamics CRM. So let’s start with OAuth 2.0 first.

To explain OAuth let me use facebook example. Delighted? Well what more common example can I provide other than something related to ubiquitous facebook.

Frequently you access apps from facebook which prompts you that it would get your information from Facebook and then once you accept it, you have access to the app. If you are not aware, this is done using OAuth 2.0. Well lets’ view it in diagrammatic sense.

image

Pic courtesy (http://tutorials.jenkov.com/oauth2/overview.html)

So let’s summarize the steps with the help of some website which authenticates via Facebook

  • User tries to access the Website (client in the above diagram)
  • In the login screen, user clicks on ‘Login via Facebook’ which is a common scenario these days.
  • The website redirects the user to the Facebook authorization page, where you enter the facebook credentials.
  • The authorization server of Facebook redirects the user to the website with the authorization code
  • The website now sends another request with the Authorization Code + Client_Id + Client_secret. The client_id and the client_secret and generated when the website has registered itself with facebook. To explain in simple terms, client_id is like some unique identifier for the client and the client_secret is some secret shared between only facebook and the client )which is this case is the website).
  • The authorization server of Facebook validates the values and generates an access token.
  • From the next time onwards, the client just sends the access token in all its requests to act on behalf of the user.

 

So if we take the above scenario, we come up with four actors in OAuth 2.0

  • Client – The website which uses facebook for authentication.
  • Resource Owner – The end-user who is accessing the website
  • Resource server – Facebook
  • Authorization Server – The authorization server that Facebook uses for identity management. Sometimes the resource server and the authorization server might be same.

Now our requirement here is to access CRM data from a standalone HTML Page. Let’s map this requirement to the above example and see who the actors here are.

  • Client – Standalone HTML web Page
  • Resource Owner – End user accessing the HTML page through browser
  • Resource Server – Dynamics CRM Online Instance
  • Authorization Server – Azure Active Directory

Things getting clear? Delighted to go ahead? I am glad if you are feeling so.  Well let’s get started then. There is nothing fancy I will do in the code. I will just copy and paste the code from SDK. But I will explain each step as to why I am doing rather than what I am doing.

Below is the code for HTML file. Create a HTML file in Visual Studio. Name it ImplicitAuthorizationCRM.htm. And copy and paste the below code in the html file.Keep the file open in visual studio. We will come back to this soon.

<!DOCTYPE html>
<html>
<head>
 <title>Simple SPA</title>
 <meta charset="utf-8" />
 http://adal.js
 

 "use strict";
 //Set these variables to match your environment
 var organizationURI = "https://xrmtr14.crm.dynamics.com"; //The URL to connect to CRM Online
 var tenant = "xrmtr14.onmicrosoft.com"; //The name of the Azure AD organization you use
 var clientId = "b140fe85-6410-40ba-9aee-cfc37f630949"; //The ClientId you got when you registered the application
 var pageUrl = "http://localhost:64754/ImplicitAuthorizationCRM.html"; //The URL of this page in your development environment when debugging.
 var user, authContext, message, errorMessage, loginButton, logoutButton, getAccountsButton, accountsTable, accountsTableBody;
 //Configuration data for AuthenticationContext
 var endpoints = {
 orgUri: organizationURI
 };
 window.config = {
 tenant: tenant,
 clientId: clientId,
 postLogoutRedirectUri: pageUrl,
 endpoints: endpoints,
 cacheLocation: 'localStorage', // enable this for IE, as sessionStorage does not work for localhost.
 };
 document.onreadystatechange = function () {
 if (document.readyState == "complete") {
 //Set DOM elements referenced by scripts
 message = document.getElementById("message");
 errorMessage = document.getElementById("errorMessage");
 loginButton = document.getElementById("login");
 logoutButton = document.getElementById("logout");
 getAccountsButton = document.getElementById("getAccounts");
 accountsTable = document.getElementById("accountsTable");
 accountsTableBody = document.getElementById("accountsTableBody");
 //Event handlers on DOM elements
 loginButton.addEventListener("click", login);
 logoutButton.addEventListener("click", logout);
 getAccountsButton.addEventListener("click", getAccounts);
 //call authentication function
 authenticate();
 if (user) {
 loginButton.style.display = "none";
 logoutButton.style.display = "block";
 getAccountsButton.style.display = "block";
 var helloMessage = document.createElement("p");
 helloMessage.textContent = "Hello " + user.profile.name;
 message.appendChild(helloMessage)
 }
 else {
 loginButton.style.display = "block";
 logoutButton.style.display = "none";
 getAccountsButton.style.display = "none";
 }
 }
 }
 // Function that manages authentication
 function authenticate() {
 //OAuth context
 authContext = new AuthenticationContext(config);
 // Check For & Handle Redirect From AAD After Login
 var isCallback = authContext.isCallback(window.location.hash);
 if (isCallback) {
 authContext.handleWindowCallback();
 }
 var loginError = authContext.getLoginError();
 if (isCallback && !loginError) {
 window.location = authContext._getItem(authContext.CONSTANTS.STORAGE.LOGIN_REQUEST);
 }
 else {
 errorMessage.textContent = loginError;
 }
 user = authContext.getCachedUser();
 }
 //function that logs in the user
 function login() {
 authContext.login();
 }
 //function that logs out the user
 function logout() {
 authContext.logOut();
 accountsTable.style.display = "none";
 accountsTableBody.innerHTML = "";
 }
 //function that initiates retrieval of accounts
 function getAccounts() {
 getAccountsButton.disabled = true;
 var retrievingAccountsMessage = document.createElement("p");
 retrievingAccountsMessage.textContent = "Retrieving 10 accounts from " + organizationURI + "/api/data/v8.0/accounts";
 message.appendChild(retrievingAccountsMessage)
 // Function to perform operation is passed as a parameter to the aquireToken method
 authContext.acquireToken(organizationURI, retrieveAccounts)
 }
 //Function that actually retrieves the accounts
 function retrieveAccounts(error, token) {
 // Handle ADAL Errors.
 if (error || !token) {
 errorMessage.textContent = 'ADAL error occurred: ' + error;
 return;
 }
 var req = new XMLHttpRequest()
 req.open("GET", encodeURI(organizationURI + "/api/data/v8.0/accounts?$select=name,address1_city&$top=10"), true);
 //Set Bearer token
 req.setRequestHeader("Authorization", "Bearer " + token);
 req.setRequestHeader("Accept", "application/json");
 req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
 req.setRequestHeader("OData-MaxVersion", "4.0");
 req.setRequestHeader("OData-Version", "4.0");
 req.onreadystatechange = function () {
 if (this.readyState == 4 /* complete */) {
 req.onreadystatechange = null;
 if (this.status == 200) {
 var accounts = JSON.parse(this.response).value;
 renderAccounts(accounts);
 }
 else {
 var error = JSON.parse(this.response).error;
 console.log(error.message);
 errorMessage.textContent = error.message;
 }
 }
 };
 req.send();
 }
 //Function that writes account data to the accountsTable
 function renderAccounts(accounts) {
 accounts.forEach(function (account) {
 var name = account.name;
 var city = account.address1_city;
 var nameCell = document.createElement("td");
 nameCell.textContent = name;
 var cityCell = document.createElement("td");
 cityCell.textContent = city;
 var row = document.createElement("tr");
 row.appendChild(nameCell);
 row.appendChild(cityCell);
 accountsTableBody.appendChild(row);
 });
 accountsTable.style.display = "block";
 }
 
 <style>
 body {
 font-family: 'Segoe UI';
 }

 table {
 border-collapse: collapse;
 }

 td, th {
 border: 1px solid black;
 }

 #errorMessage {
 color: red;
 }

 #message {
 color: green;
 }
 </style>
</head>
<body>
 <button id="login">Login</button>
 <button id="logout" style="display:none;">Logout</button>
 <button id="getAccounts" style="display:none;">Get Accounts</button>
<table id="accountsTable" style="display:none;"> <thead><tr><th>Name</th><th>City</th></tr></thead> <tbody id="accountsTableBody"></tbody> </table> </body> </html>

 

Let us complete each of the steps for this requirement. I assume you have already a trial or a working environment of Dynamics CRM Online 2016. If not please create a trial org and then come back to this step.

 

Configure Azure AD:

This step adds the Dynamics CRM Active Directory to your azure subscription. It’s very logical right? After all to access Dynamics CRM, you need to validate the users using Dynamics CRM AD.

If you do not have any existing azure subscription, go to https://azure.microsoft.com/en-in/pricing/free-trial/. Create a free subscription and then navigate to https://manage.windowsazure.com

  • Click on Active Directory –> New (at the bottom of the screen) –> Directory –> Custom create. You should get a pop like the screenshot as shown below.

image

  • Select I am ready to be signed out and then proceed.

image

  • You would be asked to login where you enter the CRM organization credentials and confirm. Once you authenticate you are done and then in sign in back to the azure account and go to the Active Directory section again. You should be able to see the CRM active directory added.
  • Open the new added AD. You should see a screen like the one below.

image

  • Click on Domains in the top bar and note it down somewhere. You will need it soon. This is you Authorization server tenant. In my case it is xrmtr14.onmicrosoft.com.

image

  • Remember we need the client_id and the client_secret to get the access token as I explained above. So for that we need to register the HTML web Application in our Azure AD. Let’s do that.

 

Register Application with Azure

  • Click on Applications –> Add. Select the option highlighted below in the pop-up.

image

  • Enter a name. I enter ‘AzureAuthWebApp’ and select Web Application since our’s is a simple HTML page running in browser. Proceed to the next section.
  • image
  • You will be asked to enter the sign-on url and the App ID URI. So what are these? Sign-On url is typically the url of your page where you ask your users to sign in. Since we have only one page, to get the sign-on url, right click the HTML file opened in visual studio and then select ‘View in Browser’. Copy the URL and paste in the box. For me it is coming – http://localhost:64754/ImplicitAuthorizationCRM.html. For you the port number might be different.
  • App ID URI – remember anything you deploy in Azure, everything is a service which must be identified by a uri. This represents the Unique URI identifier for your application. I entered https://xrmtr14.onmicrosoft.com/AzureHtmlAuthentication. Typically this should be a valid URL and unique as well. I use the CRM Domain in the URL since I know using that will make it unique as there is no-one else who can use that domain.

image

  • The application is now added.
  • Now go to the application and click on configure.

image

 

  • Scroll down and you would get the client id.

image

  • Copy that and keep it. We will need that very soon.

 

Provide Web Application (client) access to Dynamics CRM (resource server)

  • Now what? Well this application will connect to your CRM organization and try to access the data right? For that we need to give permission to this application to access Dynamics CRM instance. Scroll down further and you would see the button add application. Click on that. You would get a screen like the one shown below. Click on Dynamics CRM Online

image

  • After the application is added, click on Delegated permissions and select – Access CRM Online Organization as Users. Remember to click save at the bottom of the screen.

image

 

So through the above steps what you have basically done is you have registered our Web App (client) with the Authorization Server (azure AD) to access resources from the resource server (Dynamics CRM Online). Are we done here? Am I missing something? Yes. Let me explain.

You remember, apart from the client id, I also told about the client secret which is some secret code in which the Application and the resource server talks. Then why I did not show you how to generate secret code here. Well in our case, the client is just some simple scripts implemented in browser. Passing the secret code through the browser is not secure as you can easily debug to find out the secret code.

In cases like this OAuth 2.0 uses the implicit flow.

The implicit grant is a simplified authorization code flow optimized for clients implemented in a browser using a scripting language such as JavaScript. In the implicit flow, instead of issuing the client an authorization code, the client is issued an access token directly (as the result of the resource owner authorization). The grant type is implicit, as no intermediate credentials (such as an authorization code) are issued (and later used to obtain an access token). For more details use this link http://tools.ietf.org/html/rfc6749

By default, implicit flow is not allowed in Azure AD. To enable implicit flow, you have to do the following.

Click on Manage Manifest. And then click on Download. A .json file would be downloaded. Using the manifest file, you can edit the properties of the application which are not editable through the azure management portal.

image

Open the json file. You would find a property called “oauth2AllowImplicitFlow”. Change the value from false to true and save the file. Upload the file back clicking on Manage Manifest again and this time selecting the upload option.

So now you are all set.

With the HTML that you opened previously, check for the properties highlighted in the screenshot. If you have followed all the steps above, you should be able to replace the properties with appropriate values. I leave that to you. For pageUrl you might use the same value as the Sign-on url for the application that you put on the azure.

image

To do some heavy query forming stuff and all, Microsoft provides you Active Directory authentication Library (adal.js) which I have used in this. You can download the same from this URl – https://secure.aadcdn.microsoftonline-p.com/lib/1.0.0/js/adal.min.js.

You may need to update the reference to the file in the HTML Page depending on where you have kept the adal.js. I have placed it in the same folder as my html page. If the reference in the script tag is not correct, this example will not work.

So all set. Hit the sign-on url in chrome or mozilla. Wondering why I did not tell IE. Well with IE you will be able to authenticate but once you try to retrieve the accounts you would get an error – “User account identifier is not provided”. This is because Adal.js uses iframes to get CORS API tokens for resources other than the SPA’s own backend. Iframe request needs to access the browser’s cookies to authenticate with AAD and get the access token. Cookies are not accessible when you run in localhost from IE. You need fully qualified domain names to access cookies from iframe in IE.

Click on the login button. You would be redirected to login.microsoftonline.com to authorize. Copy the url.  You would be able to see the client_id is being passed along with the url of our page. ADAL.js does the heavy work of forming the url for you.

Enter you CRM credentials and once you authenticate you would be redirected to your application. Click on Get Accounts and you get the list of accounts.

 

image

Curious to know how the access token looks. Well in the HTML file just put a debugger in the retrieveAccounts method and once you click on Get Accounts, you can see the access token in your debugger.

Hope this helps!

How to manage existing the access teams when Access template rights are changed in Microsoft Dynamics CRM

It’s been more than 3 years that access teams have been introduced and perhaps every project I visit, I find the use of access teams. However there are still couple of nuances in access teams which most of the teams find it difficult to manage.

The first of them being the migrating the access team templates across environments. Access team templates are not solution aware. For this I have created a utility in codeplex which might help you.

http://accessteamsmigrator.codeplex.com/

The other perennial question that I keep on getting here is that what will happen to the existing access team rights if I change the access templates rights. I am going to discuss in detail what exactly happens to the existing access teams when the access template rights are changed.

For this let me take the opportunity entity and create a template for Opportunity.

I created an Opportunity Team Template with Access Rights – Append, AppendTo, Read and Write.

image

The next step is to insert the access template grid in the opportunity form. I will not waste more time in this no-brainer stuffs. So let’ s assume that everything is set-up and I have added a member to the access team sub-grid.

When a member is added to the access team, a record actually gets created in the PrincipalObjectAccess table in the back-end with the following details.

  • PrincipalId – The Guid of the access team. The team name is in the following format  <record id> + <access template id>
  • ObjectId – The guid of the opportunity record
  • AccessRightsMask – Sum of the access rights provided in the template.

In case you want to know more details about the above stuffs, you may please explore my another blog post here – https://debajmecrm.com/2014/04/28/crm-2013-access-teams-demysitified/

Screenshot of the team from advanced find. A very important point here is it is a system managed access team. It is not a user created access team. I will come back to this.

image

I have developed a small console here to actually query this principal object record. As a filter I have passed on the guid of the above team in the filter to fetch the specific record from POA for the demo.

And following is the result from the Quick watch view of Visual Studio.

image

As you can see, accessrightsmask is 23. It’s the sum of the access provided to the template earlier (Append (4) + Append To (16) + Read (1) + Write (2))

Now the fun part. I go ahead and change the access rights of the template. Additionally I give Share access.

image

I go ahead and add another member in the same opportunity record.

The new member gets added to the access team which is expected. But if I query for the access rights mask value of Principal Object Record, it is still the same.

image

So I can see, the new access right are not reflected in the existing access teams. For a solution I go ahead and delete all the members from the Opportunity team Sub-grid in the opportunity form. Now if I come to advanced find, the team is still there. However If I open the team, the team members has been deleted. So remember here system managed access teams are not automatically dissolved when all the team members are deleted.

So its pretty clear that removing and adding the team members won’t work. So now what?

There are couple of ways you can make the changes reflect.

1. Update the access rights mask through SQL Update. This would be a very simple query. The access rights would be the sum of all the individual access rights. You can get the access rights value from this link.

https://msdn.microsoft.com/en-in/library/microsoft.crm.sdk.messages.accessrights.aspx

Off-course you are  limited to On-premise and also this method is unsupported.

2. The other way can be updating would be to create a new template and add it as sub-grid in the new opportunity form and remove the old access team sub-grid. Then write a console batch to move members from the old access team to the new access team for all the opportunity records.

 

Hope this helps!

Prepare your code for Disaster recovery in Dynamics CRM Online – Dynamics CRM 2016

Not a very familiar topic I would say, at-least from what the title says. But I feel this will become more and more prominent as Dynamics CRM online customers keeps on increasing every hour.

So how do you handle if there is  some unplanned service interruption for you CRM online customer. Unplanned interruption happens when something goes wrong in the Microsoft Data Center hosting your CRM cloud environment. Thinking why you need some disaster recovery for that? Isn’t the wonderful engineers at Microsoft already at their job to make the stuff up as soon as possible? The answer is- “yes they are”. But sometimes you need to handle that from your side too.

Let us take the example of a recent request by one of my clients. Our client had a Dynamics CRM Online environment and there are jobs scheduled in the windows task scheduler which runs at specified intervals and loads data from a specified system into CRM. It is kind of incremental data upload and my client was very particular about the uninterrupted working of the service.

So we were to handle all sorts of possible exceptions. But how to handle such service  interruption from the Microsoft Data Centers?

Need not worry. Microsoft has covered you back in this case. So let’s see how it can be done.

First of all I will construct the OrganizationServiceProxy class instance. This is the class you would normally use when you try to interact with CRM from external client apps.

var orgServiceUrl = new Uri("<org service url>");
var clientCredentials = new ClientCredentials();
clientCredentials.UserName.UserName = "<office 365 user name>";
clientCredentials.UserName.Password = "<Office 365 password>";

var proxy = new OrganizationServiceProxy(orgServiceUrl, null, clientCredentials, null);

 

Nothing fancy about the above stuff right? Now comes the fun part. The organization service has events which gets fired when a failover has happened. The events are

  • EndpointSwitched – Occurs when a failover recovery has completed and the organization’s current endpoint has switched to an alternate endpoint
  • EndpointSwitchRequired – Occurs when a failover has occurred and a switch from the organization’s current endpoint to alternate endpoint is required.

However these events only fire when the failover is enabled for your organization. Let’s check for the code below which first checks whether the organization’s failover is enabled and then registers the above mentioned events.

if (proxy.EndpointAutoSwitchEnabled)
{
                proxy.EndpointSwitched += proxy_EndpointSwitched;
                proxy.EndpointSwitchRequired += proxy_EndpointSwitchRequired;
}

The above code first checks for the property EndpointAutoSwitchEnabled. If the value for this property is true, failover is enable for this organization. Below are the event handler for the events.

static void proxy_EndpointSwitched(object sender, EndpointSwitchEventArgs e)
        {
            // make the service call again here. and this time it would succeed.
            var oldEndpoint = e.PreviousUrl;
            var newEndpoint = e.NewUrl;
        }

        static void proxy_EndpointSwitchRequired(object sender, EndpointSwitchEventArgs e)
        {
           
        }

You can get the new EndPoint Url of the organization and make the service call again. Since the failover is successful, the code would continue working.

So what if failover is not enabled. In that case subscribing to the above events does not make any value.

However you can handle the following type of exceptions in your code for the service interruption.

  • EndpointNotFoundException
  • FaultException<OrganizationServiceFault> where fault.Detail.ErrorCode == -2147176347.
  • TimeoutException

The above exceptions can happen in some other circumstances as well. However you can catch these exception and inside that put in some code to wait till the CRM service is up again.

Hope this helps!

Read all column values in sub-grid on the form using client API–Dynamics CRM 2016

Microsoft introduced the Xrm.Page object model to interact with the sub-grids on your forms from 2015 Online Update 1 and in CRM 2016 (for on-premise as well). Wonderful isn’t it. You can query the number of rows in the sub-grid, get the primary attribute value, guid and the logical name of each of the record in the sub-grid.

However one big limitation I could find is that, if your sub-grid is having multiple columns, except the primary attribute column, values of no other columns are returned by the existing API. Let us take an example here.

I have a contacts sub-grid on my account entity. The sub-grid has columns – email address, telephone and others.

image

The requirement here was to read all the column values in sub-grid. The recommended way to get to the grid row data is by the code below.

var rowData = Xrm.Page.getControl(“Contacts”).getGrid().getRows().get(<rowindex>).getData();

var entity = rowData.getEntity();

var entityReference = entity.getEntityReference();

var primaryAttributeValue = entity.getPrimaryAttributeValue();

 

The above code gets the data for the row at the specified <row index>. You can fetch the entity logical name, the guid of each row data as well as the primary attribute value for each record. But as you can see there is no way to get the other column values like email, telephone etc

Off-course we could get the ID of the data rows and fire a web-api query to get the other column values. And that is supported way.

However getting dirty with the code and playing with it excites me much and here also I tried to dig a bit deep.  Before I dig deeper, a statutory warning which should not be taken lightly.

** All the methods that I would be talking below is not documented in the SDK and hence unsupported and hence might now work in future versions of CRM. So if you implement, please make sure you get an approval from your client. **

Let us take the above code only. Till we get the entity, we would use the same code. However to get the attributes of the entity, we will use the getAttributes method of the entity. So let see how this looks in the developer toolbar.

image

So attributes returned us a collection of XrmGridEntityAttributes. Interesting isn’t it? So let’s dig a bit deeper.

image

And there you go! You have the email address and telephone of the contact at your disposal.

Hope this helps!