Though Microsoft Dynamics 365 does not offer any built-in feature to export the CRM data to external portals so far, there is a simple way to reach there. If we create a web API as an intermediate service using Azure Cloud Services, it can easily export the data to external portals via FTP.
The process of configuring the Cloud Service and Storage in order to export CRM data to an external portal is described below.
Solution Architecture
Technical Execution of entire configuration is described in three steps:
- Create Storage Account (Classic) in Portal
- Create Cloud Service in Portal
- Enable the Azure Services from the CRM Plugin
1. Create Storage Account (Classic) in Portal
- Use the URL https://portal.azure.com and search for ‘storage’ in the search panel. Select the Storage accounts (classic) from the displayed search results.
- Once you are navigated to ‘Storage accounts’ dashboard, click ‘Add’ in order to create a new storage account.
- Either key in or select from drop-down options the relevant details for to each field.
- Name
- Deployment Model: Classic – Must be classic
- Account kind: General purpose
- Performance: Standard
- Replication: Keep Default
- Subscription: Usage-Based
2. Create Cloud Service in Portal
-
- Visit https://portal.azure.com and opt for Cloud services (classic).
- Either key in or select from drop-down options the relevant details for to each field.
- DNS Name
- Subscription: Usage-Based
- Resource group: Select the resource group
- Packages
- Deployment Label: Key in deployment label
- Storage Account: Select previously created account
- Upload package and configuration
- Packages will be given with the solution
- Environment: Production
- Visit https://portal.azure.com and opt for Cloud services (classic).
Once you have created the Storage account and Cloud services in the portal, you are ready to move ahead.
3. Integrate the Azure Services from the CRM Plugin
I have followed the underneath high-level structure to enable the Azure Services from the CRM Plugin.
Services CreateTextFile(FTPFile file) CreateImageFile(FTPFile file) Objects FTPFile FileName PathToCreate Data FTPConfiguration ForceCreate //For Image Process Type ImageResizeHeight ImageResizeWidth AllowsResize FTPConfiguration ServerAddress Username Password PortalId
Code Citation
Models
namespace MyProject.AzureCloudService.Models { [DataContract] public class FTPConfiguration { [DataMember] public string ServerAddress { get; set; } [DataMember] public string Username { get; set; } [DataMember] public string Password { get; set; } //[DataMember] //public string AgencyId { get; set; } } [DataContract] public class FTPFile { [DataMember] public string Name { get; set; } [DataMember] public string Path { get; set; } [DataMember] public string Data { get; set; } [DataMember] public FTPConfiguration FTPConfiguration { get; set; } public bool ForceCreate { get; set; } //Image Process [DataMember] public string Type { get; set; } [DataMember] public int ImageResizeHeight { get; set; } [DataMember] public int ImageResizeWidth { get; set; } [DataMember] public bool AllowsResize { get; set; } } public class MyProjectResponse<T> { public int ResponseCode { get; set; } public string ErrorMessage { get; set; } public T Data { get; set; } public static MyProjectResponse<T> GetSuccessResponse() { MyProjectResponse<T> oResponse = new MyProjectResponse<T>() { Data = default(T), ResponseCode = (int)HttpStatusCode.OK }; return oResponse; } public static MyProjectResponse<T> GetNoDataFoundResponse() { MyProjectResponse<T> oResponse = new MyProjectResponse<T>() { Data = default(T), ResponseCode = (int)HttpStatusCode.NoContent }; return oResponse; } public static MyProjectResponse<T> GetInternalServerErrorResponse() { MyProjectResponse<T> oResponse = new MyProjectResponse<T>() { Data = default(T), ResponseCode = (int)HttpStatusCode.InternalServerError }; return oResponse; } public static MyProjectResponse<T> GetBadRequestResponse() { MyProjectResponse<T> oResponse = new MyProjectResponse<T>() { Data = default(T), ResponseCode = (int)HttpStatusCode.BadRequest }; return oResponse; } public static MyProjectResponse<T> GetDataAlreadyExistsResponse() { MyProjectResponse<T> oResponse = new MyProjectResponse<T>() { Data = default(T), ResponseCode = (int)HttpStatusCode.Conflict }; return oResponse; } public static MyProjectResponse<T> GetUnauthorizedResponse() { MyProjectResponse<T> oResponse = new MyProjectResponse<T>() { Data = default(T), ResponseCode = (int)HttpStatusCode.Unauthorized }; return oResponse; } } }
Create Text File Function
#region Function : CreateTextFile (1) [HttpPost] [Route("api/ftpintegration/createtextfile")] public MyProjectResponse<string> CreateTextFile(FTPFile ftpFileCreate) { MyProjectResponse<string> response; FtpWebResponse ftpResponse = null; try { FtpWebRequest request = (FtpWebRequest)WebRequest.Create(ftpFileCreate.FTPConfiguration.ServerAddress + ftpFileCreate.Path + ftpFileCreate.Name); request.CachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.CacheIfAvailable); request.Method = WebRequestMethods.Ftp.UploadFile; request.Credentials = new NetworkCredential(ftpFileCreate.FTPConfiguration.Username, ftpFileCreate.FTPConfiguration.Password); //To manage forcefully creation of the file if (ftpFileCreate.ForceCreate == false) { MyProjectResponse<string> readFileResponse = GetTextFileData(ftpFileCreate); if (readFileResponse.ResponseCode == (int)HttpStatusCode.OK) { readFileResponse.Data = readFileResponse.Data.Replace("\r\nEOF\r\n", ""); readFileResponse.Data = readFileResponse.Data.Replace("EOF\r\n", ""); readFileResponse.Data = readFileResponse.Data.Replace("\r\nEOF", ""); readFileResponse.Data = readFileResponse.Data.Replace("EOF", ""); ftpFileCreate.Data = readFileResponse.Data + ftpFileCreate.Data; } } byte[] fileContents = Encoding.UTF8.GetBytes(ftpFileCreate.Data); request.KeepAlive = false; request.UseBinary = true; request.UsePassive = true; request.ContentLength = fileContents.Length; Stream requestStream = request.GetRequestStream(); requestStream.Write(fileContents, 0, fileContents.Length); requestStream.Close(); ftpResponse = (FtpWebResponse)request.GetResponse(); ftpResponse.Close(); response = MyProjectResponse<string>.GetSuccessResponse(); response.Data = ftpResponse.StatusDescription + "\nFile Created Successfully."; } catch (Exception ex) { Console.WriteLine(ex.Message.ToString()); response = MyProjectResponse<string>.GetInternalServerErrorResponse(); response.ErrorMessage = ex.Message.ToString(); } finally { if (ftpResponse != null) { ftpResponse.Close(); } } return response; } #endregion
Create Image File Function
#region Function : CreateImageFile (1) [HttpPost] [Route("api/ftpintegration/createimagefile")] public MyProjectResponse<string> CreateImageFile(FTPFile ftpFileCreate) { MyProjectResponse<string> response; FtpWebResponse ftpResponse = null; try { // Image Process WebRequest azureRequest = WebRequest.Create(ftpFileCreate.Data); WebResponse azureResponse = azureRequest.GetResponse(); // Stream stream = response.GetResponseStream(); MemoryStream memoryStream = new MemoryStream(); byte[] buffer = new byte[0x1000]; using (Stream responseStream = azureResponse.GetResponseStream()) { int bytes; while ((bytes = responseStream.Read(buffer, 0, buffer.Length)) > 0) { memoryStream.Write(buffer, 0, bytes); } buffer = memoryStream.GetBuffer(); } if (ftpFileCreate.AllowsResize) { Image img = Image.FromStream(memoryStream); Image resized = ResizeImage(img, new Size(ftpFileCreate.ImageResizeWidth, ftpFileCreate.ImageResizeHeight)); MemoryStream memStream = new MemoryStream(); resized.Save(memStream, ImageFormat.Png); buffer = memStream.GetBuffer(); } // Image Process FtpWebRequest request = (FtpWebRequest)WebRequest.Create(ftpFileCreate.FTPConfiguration.ServerAddress + ftpFileCreate.Path + ftpFileCreate.Name); request.CachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.CacheIfAvailable); request.Method = WebRequestMethods.Ftp.UploadFile; request.Credentials = new NetworkCredential(ftpFileCreate.FTPConfiguration.Username, ftpFileCreate.FTPConfiguration.Password); request.KeepAlive = false; request.UseBinary = true; request.UsePassive = true; request.ContentLength = buffer.Length; Stream requestStream = request.GetRequestStream(); requestStream.Write(buffer, 0, buffer.Length); requestStream.Close(); ftpResponse = (FtpWebResponse)request.GetResponse(); ftpResponse.Close(); response = MyProjectResponse<string>.GetSuccessResponse(); response.Data = "File Created Successfully."; } catch (Exception ex) { Console.WriteLine(ex.Message.ToString()); response = MyProjectResponse<string>.GetInternalServerErrorResponse(); response.ErrorMessage = ex.Message.ToString(); } finally { if (ftpResponse != null) { ftpResponse.Close(); } } return response; } #endregion
Conclusion
In this post, I have detailed out the process of configuring the Azure Cloud Service and Storage along with a glimpse of enabling Azure Service from CRM Plugin. This configuration will enable you to export CRM data to the external portals in the desired format in a single click.
Last modified: October 06, 2022