Skype for Business (Lync) Web App

When you have the Skype for Business app installed on your machine you cannot open the meeting in a browser (web app) without it trying to force you to use the full application. This can be frustrating because you may want to join a meeting for client who is not part of your business. In my scenario:

  • I was logged out of my SfB desktop app
  • Opened IE and pasted in the URL for the meeting.
  • This then tries to open the desktop app and fails.
  • Since Edge was my default browser the system then tries to open Edge and takes you to the download page for the Lync Web App. Since Edge does not work with SfB then I cannot access my meeting.

The solution comes in the form of a query string parameter appended to the URL. Open IE, enter the URL to the conference call and append ?SL=1 to the end and press ENTER.

CRM 2011 – 404 Login Error

Background

  • CRM 2011 with Rollup 18
  • Using an existing CRM 2011 database backup, restore a copy of the database with a new name.
  • Import that database using the Deployment Manager to create a new organisation. The small number of existing users were automatically mapped across to the new organisation.

The Problem

When I attempted to login to the organisation I was presented with the following:

404-Login-Error

I knew the problem wasn’t with the domain account because I could use it to log into other CRM organisations on the same server. I could also login to the newly created organisation with a different domain user; doing this allowed me to confirm that the problem domain account did exist in the list of active users. Something was missing for this user in the new organisation.


The solution

After some digging around I got some pointers to the MSCRM_CONFIG database and the SystemUserOrganizations table, which links your System User to Organisations on your CRM server.

Disclaimer: The steps below interact directly with the CRM databases. If you aren’t familiar with these databases, please do not attempt these steps as many things can go wrong if you make a mistake.

  1. Find the OrganizationId and SystemUserId from the SystemUserBase table in the Organisation database.
  2. Use the following query to identify to which Organisations the account is linked;
    select
    sua.AuthInfo
    ,sua.UserId
    ,suo.CrmUserId
    ,suo.OrganizationId
    from SystemUserAuthentication sua
    inner join SystemUserOrganizations suo on sua.UserId = suo.UserId
    where suo.CrmUserId = '<my-systemuserid>'
    order by suo.OrganizationId, sua.UserId, sua.AuthInfo
  3. Each Organisation should have two entries, one for the Active Directory SID and one for the CRM username. In my case there were only two entries for the original organisation and none for the newly imported organisation.
  4. Create the new entry in the SystemUserOrganizations table to link the existing System User to the newly imported Organisation.
    insert into SystemUserOrganizations (
    CrmUserId
    ,DirectoryObjectId
    ,Id
    ,LastAccessTime
    ,OrganizationId
    ,UniqueifierId
    ,UserId
    ,IsDeleted
    ,IsDisabled
    ,IsLicensed)
    values (
    '<SystemUserId-from-SystemUserBase table>'
    ,NULL
    ,NEWID()
    ,NULL
    ,'<OrganizationId-from-SystemUserBase table>'
    ,NULL
    ,'<UserId-from-SystemUserAuthentication table>'
    ,0
    ,NULL
    ,NULL)
  5. The domain account is now able to access the new organisation.

The remote server returned an error: (401) Unauthorized.

Here is the scenario:

  • .NET Web Api running on an IIS Server (8.5 – although the problem was recreated on 7.5 too)
  • v4.0 Integrated Application Pool with a domain account identity.
  • Only Windows Authentication is enabled. No anonymous access is allowed.
  • Making requests to the Web Api  works in the following configuration:
    • Using a different domain account to the one used by the Application Pool AND
    • From a different machine and using a browser or the Invoke-RestMethod PowerShell command
  • Making requests to the Web Api does not work in the this configuration and results in a 401:Unauthorized error being displayed.
    • Using a different domain account from the same server OR
    • Using the same domain account to the one used by the Application Pool AND
    • From either the same or different machine, using a browser or the Invole-RestMethod PowerShell command

The problem was that a Schedueld Job, created as a PowerShell script, was being run from the same server and the Invoke-RestMethod was generating the 401:Unauthorized error.

The solution was to set the relevant Service Principle Names for the IIS Server, but instead of doing it for the specific domain user account it was configured for the server. This included SPN’s for the computers NetBIOS name and the FQDN as well as the host name of the Web Api.

Oracle Java JDK – A more recent version of this software is required

I had previously installed Visual Studio 2015 Community Edition to do some Cordova development but was getting this message;

Oracle Java SDK A more recent version of this software is required

I downloaded the updated JDK but it wasn’t being recognised in my system. Information about this can be found here – https://msdn.microsoft.com/en-us/library/dn757054(v=vs.140).aspx#AdditionalTasks

If you modify any of the tools you are using then you need to ensure that your Environment Variables are configured correctly. A list of these variables are available here – https://msdn.microsoft.com/library/dn771551%20(v=vs.140).aspx#env_var

I chose to override the JAVA_HOME environment variable, instead of modifying them in the Computer Control Panel, to the latest version of the JDK I had installed – C:\Program Files (x86)\Java\jdk1.8.0_45. This resolved the issue for me.

CRM – Creating report attachments for emails in Workflows

I had been asked by a client to generate a report, based on an Account, attach this report to an email and send this email as part of a workflow. My server setup was fairly standard; separate servers for the CRM front-end, back-end, database and reporting. The workflow would consist of a number of steps:

  1. Create an email entity with the necessary content.
  2. Run some custom workflow code to generate the report with the following Inputs. These would include the Email created as part of Step #1, the Report to generate and attach to the email, the Primary Contact for the Account and the URL to the report server.Custom Step Inputs
  3. Run some additional custom code to send the email with the following inputsCustom Step Inputs

The problem I had to solve was that when I tried to generate the report in code using the ServerReport Render method I would get the following error:

An error has occurred during report processing (rsProcessingAborted)  Cannot create a connection to data source ‘CRM.’ (rsErrorOpeningConnection)  Guid should contain 32 digits with 4 dashes (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx).

As it turns out, this KB article gave me the clue to solving the problem. This error occurs in Microsoft Dynamics CRM 2011 because the Microsoft Dynamics CRM Reporting Extensions requires the user’s SystemUserId value as the Log in name and the user’s OrganizationId value as the password. When a report runs inside Microsoft Dynamics CRM, these values are passed automatically. The OrganizationId and SystemUserId can be pulled from the IWorkflowContext interface.  I then needed to get details of the DataSource and apply the credentials. This can be done in a few easy steps:

  1. Use the ServerReport GetDataSources method to retrieve the ReportDataSourceInfoCollection; since my reports only have one DataSource we can just pull the first item from the collection.
  2. Create a DataSourceCredentials object to set the username (SystemUserId), password (OrganizationId) of the DataSource. Add that object to the DataSourceCredentialsCollection.
  3. Use the ServerReport SetDataSourceCredentials method to apply the credentials to the ServerReport.

The code snippet below provides a bit more detail on the information from these steps.

ReportViewer viewer = new ReportViewer();
			
Warning[] warnings;
string[] streamids;
string mimeType;
string encoding;
string fileExt;

ActivityMimeAttachment attachment = new ActivityMimeAttachment()
{
    MimeType = "application/octet-stream",
    FileName = attachmentName
};	

viewer.ServerReport.ReportServerUrl = new Uri(reportServerUrl);
viewer.ServerReport.ReportPath = string.Format(@"/{0}_MSCRM/CustomReports/{1}", organisatioName, reportName);

ReportDataSourceInfoCollection reportDataSources = viewer.ServerReport.GetDataSources();
if (reportDataSources == null || reportDataSources.Count == 0)
{
    throw new InvalidPluginExecutionException(string.Format("The report {0} does not contain a valid datasource", reportName));
}
DataSourceCredentials dsc = new DataSourceCredentials() { Name = reportDataSources[0].Name, Password = organisationId.ToString(), UserId = systemUserId.ToString() };
DataSourceCredentialsCollection dscc = new DataSourceCredentialsCollection();
dscc.Add(dsc);

viewer.ServerReport.SetDataSourceCredentials(dscc);
viewer.ServerReport.SetParameters(reportParams);
viewer.ServerReport.Refresh();

byte[] bytes = viewer.ServerReport.Render("PDF", null, out mimeType, out encoding, out fileExt, out streamids, out warnings);
attachment.Body = Convert.ToBase64String(bytes);

CRM 2011 – Error deserializing XAML

I was experiencing this error when trying to update an existing solution with a new version. It didn’t really tell me much and the changes I had made in this version of the solution were only minor. The other odd thing was that a number of my colleagues using the same CRM server were experiencing other issues performing their own operations. A couple of them were receiving this error:

System.ServiceModel.FaultException: The formatter threw an exception while trying to deserialize the message: There was an error while trying to deserialize parameter http://schemas.microsoft.com/xrm/2011/Contracts/Services:request.

After trying the obvious of restarting services, I decided to turn on the trace functionality in CRM, specifically looking at the Platform_Import category, and ran my import process again. As expected it failed, so I searched the trace file for the error shown in my Import log file – Error deserializing XAML – and further along the error I found this message:

System.IO.FileNotFoundException: Could not load file or assembly ‘Microsoft.SharePoint.Client.ServerRuntime, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c’ or one of its dependencies

I have come across this error before when using other assembly’s with CRM so the solution was to add the missing assembly to the CRMWeb\bin folder on the CRM web server. This time, running the import again successfully updated the solution as well as solving other problems that were being experienced by my colleagues using the same server.

Accessing secure web services – invalid certificate errors

Calling a secure web service from within an ASP.NET application was generating the following exception:

The remote certificate is invalid according to the validation procedure.

Digging further down into the exception provided a little more detail:

Could not establish trust relationship for the SSL/TLS secure channel with authority 'companyaddress.com'

But, this still didn’t give me too many clues as to the problem. I then came across this very useful MSDN blog post that gave me some very valuable steps to follow to resolve the issue. My first problem was that there were a couple of intermediate certificates involved that needed to be installed to the relevant locations on the server where my ASP.NET app was running; but this didn’t solve the problem so I set up the tracing file and ran the request again. This time the error was a bit more helpful:

Certificate name mismatch

It turns out that my problem was the endpoint address in my web.config file did not match the address in the certificate. My endpoint address was entered as companyaddress.com but the certificate was issued to www.companyaddress.com. The final part of the solution was to change the endpoint address in the web.config to match that in the certificate.