Thursday, December 10, 2015

[Solved] 401: Unauthorized errors for specific URLs in CRM

Hi all,

Today I faced this issue with one of my CRM instances, where many URLs specific to webresources and other js, css files for CRM were throwing 401 Unauthorized errors, which makes the browser to popup the login window annoyingly. This happens whenever you browse to specific portions in CRM. This may even stop some form event handlers from getting executed.

I tried out different things like reinstalling URL rewrite module, repairing CRM installation but nothing solved the issue. Finally the below configuration change in IIS resolved the issue.

1. Open IIS Manager
2. Select the server node 
3. In features windows, go to Authentication >> Windows Authentication >> Enable.
4. Next select Windows Authentication >> Advanced Settings >> Check the "Enable Kernel-mode authentication" checkbox.
5. Do an iisreset and browse to CRM.

Hope this helps someone.

Friday, November 20, 2015

[SOLVED] The Module DLL C:\Program Files\Microsoft\Exchange Server\V15\Bin\kerbauth.dll failed to load. The data is the error.

I recently came across this issue, when setting up a CRM Environment. I was not able to bring up the CRM website at all. After a long Google session, all I could find was a solution about editing the ApplicationHost file. (more can be read here.) This may work in your case.

But the above method didn't work for me. I noticed that in event viewer the "The Module DLL C:\Program Files\Microsoft\Exchange Server\V15\Bin\kerbauth.dll failed to load.  The data is the error." error is getting logged everytime, i do an iisreset or try to browse to website hosted in the local IIS.

I figured out it should be an issue with the IIS Manager configuration. Eventually it was. The fix is to remove the kerbauth.dll module from native modules for the server. Screenshots depicting the steps below. In the final screenshot, kerbauth.dll is not visible (as i have already removed the module :( ), just hightlighted the area. 

Hope it helps.








Monday, March 9, 2015

MS CRM Security Roles to Excel export

Hi guys,

I am working on a tool to export CRM security roles to an Excel workbook for documentation purposes. The tool is in alpha stage. Right now its working with CRM 2013 and 2011 On Premises with Active Directory Authentication only. Will soon be implementing authentication for Online, Claims and O365.

Any suggestions/comments are welcome. The code plex link is provided here.

https://mscrmrolesheetexporter.codeplex.com

[update]

I have added support for other authentication modes as well. Now it should work with IFD/AD on OnPremise and Office365/Live authentication in Online. The tool will also work with CRM 2015, but all the privileges may not be exported. Please let me know if you face any issue.

[update 11/12/2015]
Replace Office interop with OpenXML. Fixed other bugs.

Thanks,
John

CRM - Time in Current User's Timezone

I have come across some situations, when I am designing a HTML page for using as webresource in CRM and it primarily uses ODATA as communication protocol. One of the PITA scenario I have faces so far is handling dates with ODATA. ODATA is not an intelligent protocol as SOAP and I would say (not exactly true) that most of the data we get in ODATA will be a direct representation of the underlying DB table data.

This particular post presents a code snippet for converting a UTC time into time in current user's timezone using Javascript. The fundamental idea is to make use of CRM's internal time zone definition for time mapping.

I cannot take responsibility if there are any issues with CRM timezone definition though.
Jumping right into the code now.



GetLocalTimeFromUTC = function (dateObject) {
    //dateObject should be a Universal Time. As the function name suggest, this function returns the time in current user's time zone assuming the dateObject is in UTC.

    var yr = dateObject.getFullYear();
    var userSetting = null;

    function ErrorCallback(error) {
        alert(error.message);
    }

    function userSettingSuccessCallback(result) {
        userSetting = result;
    }

    var userId = Xrm.Page.context.getUserId();

    //Modified SDK.REST.js to post synchronous requests. Hope you can do the same or mimic with XMLHttpRequest.
    SDK.REST.retrieveRecord(userId, "UserSettings", "TimeZoneDaylightBias,TimeZoneDaylightDay,TimeZoneStandardMinute,TimeZoneCode,TimeZoneDaylightMinute,TimeZoneStandardDayOfWeek,TimeZoneStandardBias,TimeZoneDaylightSecond,TimeZoneStandardMonth,TimeZoneBias,TimeZoneDaylightHour,TimeZoneStandardDay,TimeZoneStandardHour,TimeZoneDaylightDayOfWeek,TimeZoneStandardSecond,TimeZoneStandardYear,TimeZoneDaylightMonth,TimeZoneDaylightYear", "", userSettingSuccessCallback, ErrorCallback);

    if (userSetting != null) {
        var dst_start = new Date(yr, userSetting.TimeZoneDaylightMonth - 1, (userSetting.TimeZoneDaylightDay * 7), userSetting.TimeZoneDaylightHour, userSetting.TimeZoneDaylightMinute, userSetting.TimeZoneDaylightSecond);
        var dst_end = new Date(yr, userSetting.TimeZoneStandardMonth - 1, (userSetting.TimeZoneStandardDay * 7), userSetting.TimeZoneStandardHour, userSetting.TimeZoneStandardMinute, userSetting.TimeZoneStandardSecond);

        // Calculcate the Daylight Savings start date for the year.
        var day = dst_start.getDay();
        dst_start.setDate((userSetting.TimeZoneDaylightDay * 7) - (day - userSetting.TimeZoneDaylightDayOfWeek));

        // Calculcate the Daylight Savings end date for the year.
        day = dst_end.getDay();
        dst_end.setDate((userSetting.TimeZoneStandardDay * 7) - (day - userSetting.TimeZoneStandardDayOfWeek));

        dateObject.setMinutes(dateObject.getMinutes() - (userSetting.TimeZoneBias));

        if (dateObject >= dst_start && dateObject < dst_end) {
            dateObject.setMinutes(dateObject.getMinutes() - (userSetting.TimeZoneDaylightBias));
        }
    }

    return dateObject;
}

Thursday, January 29, 2015

CRM LINQ returns NULL values

I faced this issue recently. The exact replication steps would be to initialize a CRM Service Context and use it to retrieve the same (set of) record(s) more than once, with first query retrieving a set of attributes and second query retrieving some other set of attributes. In this scenario, you would notice that the second query returns NULL values for almost all the attributes.

The reason for this behavior that CRM Service Context caches the results once retrieved. So when the first query returned a set of attributes, it cached the record with only those values. When the second query is executed, the query is not run on the DB at all, but on the cached data. Since the second query has a different set of attributes, it returns NULL values.

How to workaround this issue:

There are two options.

1. Re-create the service context everytime a query is executed. This makes the ServiceContext fetch the data from CRM everytime.

2. Or you could call the ClearChanges() method in the Service context. But just take care, that you don't clear the CRUD changes.

Tuesday, January 20, 2015

Exporting a CRM SSRS Report from .NET Code

We recently came across a requirement where I had to export some custom SSRS Report to Excel file on a schedule and store the file in a file system path. This will contain some parameters to be passed and I had to pass some dynamics values to these parameters. Hence report snapshot is not an option here. 

One option was to go with a console application which can be scheduled using Windows Task Scheduler and offers much more flexibility over the functionality. On searching around internet, I didn't find much help. The solution was scattered across many blog posts and forums. Consolidating the solution we implemented here. 

The steps involve

  1. Retrieving the report server URL from deployment properties using deployment service (But wasn't required for my case. So not including the code for that. I retrieved the report server URL from app.config)
  2. Retrieving current user ID and organization ID, using a WhoAmIRequest.
  3. Retrieving the report details from CRM (using report name probably). The report record in CRM contains the report name on SSRS, it will basically be GUID.
  4. Initialize a report viewer control (.NET Control) pointing to the above report.
  5. Save the report as Excel.
Posting a sample code. May not work as is, since I am just posting snippets of my production code. Please add reference to Microsoft.ReportViewer.WinForms assembly for ReportViewer control.
string reportServerUrl = "http://xrm.com"

WhoAmIRequest whoami = new WhoAmIRequest();
WhoAmIResponse iam = (WhoAmIResponse)orgService.Execute(whoami);
Organization orgObject = orgService.Retrieve(Organization.EntityLogicalName, iam.OrganizationId, new Microsoft.Xrm.Sdk.Query.ColumnSet(true)).ToEntity();

Guid? reportId = null;
using (Xrm serviceContext = new Xrm(orgService))
{
      reportId = (from r in serviceContext.ReportSet
                  where r.Name == reportNode.Attribute(XName.Get(nameAttribute)).Value
                  select r.ReportId).FirstOrDefault();
}

ReportViewer reportViewer = new ReportViewer();
reportViewer.ServerReport.ReportPath = @"/" + orgObject.Name + "_MSCRM/CustomReports/{" + reportId.ToString().ToLower() + "}";
reportViewer.ServerReport.ReportServerUrl = new Uri(reportServerUrl);
reportViewer.ProcessingMode = ProcessingMode.Remote;
reportViewer.ServerReport.Timeout = 1200000;
reportViewer.ServerReport.ReportServerCredentials.NetworkCredentials = credentials;
ReportDataSourceInfoCollection reportDataSources = reportViewer.ServerReport.GetDataSources();

DataSourceCredentials creds = new DataSourceCredentials();
creds.UserId = iam.UserId.ToString();
creds.Password = iam.OrganizationId.ToString();
creds.Name = reportDataSources[0].Name;

DataSourceCredentialsCollection credsCollection = new DataSourceCredentialsCollection();
credsCollection.Add(creds);

reportViewer.ServerReport.SetDataSourceCredentials(credsCollection);
reportViewer.ServerReport.SetParameters(new ReportParameter("statuscode", "1"));
reportViewer.ShowParameterPrompts = false;

string mimeType, encoding, extension;
string[] streams;
Warning[] warnings;
byte[] fileBytes = reportViewer.ServerReport.Render("Excel", string.Empty, out mimeType,
                            out encoding, out extension, out streams, out warnings);

string outputPath = reportNode.Attribute(XName.Get("OutputFilePath")).Value + "\\" + outputFileName;
using (FileStream fs = new FileStream(outputPath, FileMode.Create))
{
    fs.Write(fileBytes, 0, fileBytes.Length);
}