Create Folder in SharePoint from Dynamics 365 Online Plugin

Microsoft Dynamics 365 supports out of box integration with SharePoint, sometimes you need something more. I needed to create a SharePoint folder for each record in multiple locations (secure and unsecure) using elevated user identity because the user did not have permission to create folders in SharePoint.

This post describes how to integrate Dynamics 365 online plugin with SharePoint. How to create a folder in SharePoint using REST APIs and associate it with a Document Location.

This has two parts:

  • SharePoint Add-ins
  • Custom plugin for Dynamics 365

SharePoint Add-ins

There are lots of ways to access the SharePoint API and its resources. Authentication plays an important role in authorizing the access to get the information. For authentication we have to add request header values to the API request. Authentication and Authorization of SharePoint Add-Ins gives an overview of authorizing the Add-ins to access SharePoint resources by the APIs.

Authentication Policies:

SharePoint online uses three type of polices to authenticate the Add-In.

  • User Policy
  • Add-In Policy — We are using this policy to authenticate SharePoint
  • User + Add-In Policy

Request Headers:

We require the following information in various requests to authenticate with SharePoint online site.

  • Client Id
  • Client Secret
  • Tenant Id
  • Access Token

Register Add-In:

Follow the steps below to register the Add-In in SharePoint site.

  • Navigate and login to SharePoint online site.
  • Then navigate to the Register Add-In page by entering the URL.
  • https://<sitename>.SharePoint.com/_layouts/15/appregnew.aspx
  • On App Information section, click Generate button next to the Client Id and Client Secret textboxes to generate the respective values.
  • Enter Add-In Title in Title textbox
  • Enter AppDomian as a loclhost
  • Enter RedirectUri as a https://localhost
Register an Add-In
  • Click Create button, which registers the add-in and returns the success message with created information.
Add-In Registration Successful

Grant Permissions to Add-In:

After the Add-In is registered, set the permissions for the add-in to access the SharePoint data. Set the Manage permission level to web scope, so we will be able to create the folder in the SharePoint sites. For more information see Add-In permissions in SharePoint and add the xml according to your required access level.

  • Navigate to the SharePoint site
  • Then enter the URL in the browser. This will redirect to Grant permission page.
  • https://<sitename>.sharepoint.com/_layouts/15/appinv.aspx
  • Enter the Client ID(which we have generated earlier), in AppId textbox and click Lookup button. That will populate the value to other textboxes in Title, App Domain and Redirect Url.
Set Permissions to Add-In
  • Now enter the below permission request in XML format.
<AppPermissionRequests AllowAppOnlyPolicy=”true”><AppPermissionRequest Scope=”http://sharepoint/content/sitecollection/web" Right=”Manage” />
</AppPermissionRequests>
  • Then click Create button. This will redirect to you page, where we have to trust the add-in.
Trust Add-in

Custom Plugin for Dynamics 365

Once registered the Client Id and Secret with the permissions, we can implement the SharePoint folder creation code from CRM plugin.

You must know the Tenant ID and Resource Id. Get these and store in config. Store the Client Id, Client Secret and SharePoint Domain in the config to request authorization token.

Get Tenant Id and Resource:

Make a request to client.svc to get the details.

var sharepointSiteToPing = https://<sitename>/sharepoint.com/_vti_bin/client.svc/

string GetTenantId(string sharepointSiteToPing)
{
string tennantId = string.Empty;
WebRequest request = WebRequest.Create(sharepointSiteToPing);
request.Headers.Add(“Authorization: Bearer “);
try
{
using (request.GetResponse())
{
}
}
catch (WebException e)
{
string bearerResponseHeader = e.Response.Headers[“WWW-Authenticate”];
const string bearer = “Bearer realm=\””;
int bearerIndex = bearerResponseHeader.IndexOf(bearer, StringComparison.Ordinal);
int realmIndex = bearerIndex + bearer.Length;
string resource = string.Empty;
if (bearerResponseHeader.Length >= realmIndex + 36)
{
tennantId = bearerResponseHeader.Substring(realmIndex, 36);
Guid realmGuid;
if (Guid.TryParse(tennantId, out realmGuid))
{
}
}
const string client = “client_id=\””;
int clientIndex = bearerResponseHeader.IndexOf(client, StringComparison.Ordinal);
int clientIdIndex = clientIndex + client.Length;
if (bearerResponseHeader.Length >= clientIdIndex + 36)
{
resource = bearerResponseHeader.Substring(clientIdIndex, 36);
Guid resourceGuid;
if (Guid.TryParse(resource, out resourceGuid))
{
}
}
}
catch (Exception)
{
// Something else happened.Rethrow or log.
throw;
}
return tennantId;
}

Get Authorisation Token:

After getting the Tenant Id, make a POST request to below URL for requesting access token.

https://accounts.accesscontrol.windows.net/<TenantID>/tokens/OAuth/2

string GetAuthorisationToken(string sharepointDomain, string tenantId, string resource, string clientId, string clientSecret)
{
string access_token = string.Empty;
WebRequest request = WebRequest.Create(“https://accounts.accesscontrol.windows.net/" + tennantId + “/tokens/OAuth/2”);
request.Method = “POST”;
// Create POST data and convert it to a byte array. 
string postData = “grant_type=client_credentials” +
“&client_id=” + WebUtility.UrlEncode(clientId + “@” + tennantId) +
“&client_secret=” + WebUtility.UrlEncode(clientSecret) +
“&resource=” + WebUtility.UrlEncode(resource + “/” + sharepointDomain + “@” + tennantId);
byte[] byteArray = Encoding.UTF8.GetBytes(postData);
// Set the ContentType property of the WebRequest. 
request.ContentType = “application/x-www-form-urlencoded”;
// Set the ContentLength property of the WebRequest. 
request.ContentLength = byteArray.Length;
// Get the request stream. 
Stream dataStream = request.GetRequestStream();
// Write the data to the request stream. 
dataStream.Write(byteArray, 0, byteArray.Length);
// Close the Stream object. 
dataStream.Close();
try
{
using (WebResponse response = request.GetResponse())
{
// Display the status.
Console.WriteLine(“Status of Token Request: “ + ((HttpWebResponse)response).StatusDescription);
// Get the stream containing content returned by the server. 
dataStream = response.GetResponseStream();
// Open the stream using a StreamReader for easy access. 
StreamReader reader = new StreamReader(dataStream);
// Read the content. 
string responseFromServer = reader.ReadToEnd();
// Clean up the streams. 
reader.Close();
dataStream.Close();
//Get accesss token
const string accessToken = “access_token\”:\””;
int clientIndex = responseFromServer.IndexOf(accessToken, StringComparison.Ordinal);
int accessTokenIndex = clientIndex + accessToken.Length;
access_token = responseFromServer.Substring(accessTokenIndex, (responseFromServer.Length — accessTokenIndex — 2));
return access_token;
}
}
catch (WebException wex)
{
HttpWebResponse httpResponse = wex.Response as HttpWebResponse;
// resource was not modified.
Console.WriteLine(“Access token not retrieved: “ + httpResponse.StatusDescription);
// Something else happened. Rethrow or log.
throw;
}
catch (Exception)
{
// Something else happened. Rethrow or log.
throw;
}
}

Create SharePoint Folder:

We can make a POST request to pass this token in Authorization header with the SharePoint REST API to create folder.

void Createfolder(string accessToken, string sharePointSite, string library, string folder)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(sharePointSite + “/_api/lists/getByTitle(‘“ + library + “‘)/rootfolder/folders/add(url=’” + folder + “‘)”);
request.Method = “POST”;
request.Accept = “application/json;odata=verbose”;
request.Headers.Add(HttpRequestHeader.Authorization, “Bearer “ + accessToken);
request.ContentLength = 0;
try
{
using (WebResponse response = request.GetResponse())
{
// Do something if the resource has changed.
}
}
catch (WebException wex)
{
HttpWebResponse httpResponse = wex.Response as HttpWebResponse;
if (httpResponse.StatusCode == HttpStatusCode.NotModified)
{
// resource was not modified.
}
// Something else happened. Rethrow or log.
throw;
}
catch (Exception)
{
// Something else happened. Rethrow or log.
throw;
}
}

Create Document Location:

After successful creation of SharePoint folder we need to create document location record in CRM and associate with the entity record.

Guid CreateDocumentLocation(Guid parentLocationId, string documentLocationName, string relativeUrl, EntityReference entityRef)
{
Entity spDocLoc = new Entity(“sharepointdocumentlocation”);
spDocLoc.Attributes[“name”] = documentLocationName;
spDocLoc.Attributes[“parentsiteorlocation”] = new EntityReference(“sharepointdocumentlocation”, parentLocationId);
spDocLoc.Attributes[“relativeurl”] = relativeUrl;
spDocLoc.Attributes[“regardingobjectid”] = entityRef;
return _orgService.Create(spDocLoc);
}

The required helper methods are shown above for the SharePoint and CRM integration which you can use in your CRM plugin to integrate with SharePoint directly rather than relying on the out of box integration.

That’s all, Hope this helps!!