Change the CRMAppPool Identity

Most likely this is recorded in a thousand places, but I had a hard time finding info about what steps need to be taken to change the identity of the CrmAppPool. All you need to do is:
- Create a user in Active Directory
- Make sure the user has Domain User rights
- Add the user to the PrivUserGroup of CRM
- Make sure the user has 'log on as a service' priviledge

The last step can be executed on two approaches:
- Go to 'Local Security Policy' in the administrative tools ('Domain Controller Secuirty Settings' on a Domain Controller) and add the user to 'Log on as a service'
- Add the user to the CRM_WPG group in AD. This group is member of the 'Log on as a service' policy.

Hope this saves you some time, at least now that I have this documented here I can find it back *smile*



Automatic provisioning of Organizations

Microsoft CRM does have a separate webservice for doing deployment related activities. There is a Deployment SDK especially for this webservice. You can download that here. One of the activities you can execute is automatic provisioning of new organizations. In this post I'll dive into the code required for this as well as some of the common issues while doing so.

Basically following the next steps should be enough:
- create an application
- add a web reference to: http:///mscrmservices/2007/crmdeploymentservice.asmx
- write the following code:


Organization org = new Organization();
org.UniqueName = txtName.Text;
org.FriendlyName = txtName.Text;
org.SqlServerName = ConfigurationManager.AppSettings["SqlServerName"];
org.SrsUrl = ConfigurationManager.AppSettings["SrsUrl"];
org.BaseCurrencyCode = ConfigurationManager.AppSettings["BaseCurrencyCode"];
org.BaseCurrencyName = ConfigurationManager.AppSettings["BaseCurrencyName"];
org.BaseCurrencySymbol = ConfigurationManager.AppSettings["BaseCurrencySymbol"];

CreateRequest request = new CreateRequest();
request.Entity = org;

CrmDeploymentService crmDeployService = new CrmDeploymentService();
crmDeployService.Credentials = System.Net.CredentialCache.DefaultCredentials;

try
{
crmDeployService.Execute(request);
}
catch (SoapException se)
{
lblError.Text = String.Format("Creation of Organization failed with error:<br>{0}<br>Detail:<br>{1}", se.Message, se.Detail.InnerText);
}
catch (Exception ex)
{
lblError.Text = String.Format("Creation of Organization failed with error:<br>{0}", ex.Message);
}

When you don't want to use the default credentials, then you would supply the username and password like this:

crmDeployService.Credentials = new System.Net.NetworkCredential("administrator", "pass@word1", "litwareinc");

In some cases this might lead to a 401 Unauthorized error message though. This has to do with kerberos. When you do force your application to use NTLM though, then it works. You can do this by using this code:

CredentialCache credential = new System.Net.CredentialCache();
NetworkCredential netCred = new NetworkCredential("administrator", "pass@word1", "litwareinc");
credential.Add(new Uri("http://localhost:5555"), "NTLM", netCred);
crmDeployService.Credentials = credential;

Some other errors you might run into with their respective answers.

0x8004b001 Create new Organization (Name=orgname, Id=10d59133-ae34-de11-a5a9-0003ffab19fc) failed with Exception: System.Security.SecurityException: Requested registry access is not allowed.

This error occurs because the identity of the application pool which is used by Dynamics CRM has no rights to update the records in the registry hyve HKLM\Software\Microsoft\MSCRM. By default this is set to "Network Service". To get rid of the error, open the registry editor (regedit), browse to the MSCRM hyve, rightclick on the hyve and choose "Permissions". You can then give the correct user rights for updating the values.

Most likely you will now run into this error message:

Server was unable to process request.
Detail:
0x8004b001 Create new Organization (Name=orgname,
Id=2015e8d7-b434-de11-a5a9-000b2924ac91) failed with Exception: System.NullReferenceException: Object reference not set to an instance of an
object.

This message does show up because you are working with the Network Service as the Identity for the Application Pool. You should change the identity of the CrmAppPool (see my other blog post). Then do an iisreset and you should get passed this error message.

Do you run into this error message?

Server was unable to process request.
Detail:
0x8004b001 Create new Organization (Name=orgname, Id=2015e8d7-b434-de11-a5a9-0003ffab19fc) failed with Exception: Microsoft.Crm.CrmException: Invalid user auth.

Then you most likely have forgot to do an iisreset. Try that or even a complete server reboot.

Now your provisioning should be starting, but it won't finish due to this error message:

The operation has timed out

Creating a organization can take quite some time. The timeout should be increased so that the creation of the organization can finish successfully before the timeout has expired. Adding this line of code would help:

crmDeployService.Timeout = 6000000;


That should solve your issues regarding the automatically provisioning of organizations for Dynamics CRM.

There could be ofcourse many other issues like out of memory exceptions, but that you can 'easily' solve by adding memory. If you find any other issues with the respective solutions to this topic, please add them to the comments!



Walk through all elements on a form

In some cases it is required to set all attributes on a form to disabled or do some other logic like checking which attribute has been changed. To do this you do need to walk through each attribute of the CRM form. My fellow MVP Mitch Milam has posted the technique to do this more than a year ago:
http://blogs.infinite-x.net/2007/11/21/disabling-all-fields-on-a-crm-form/

The most important piece of this code is:


var iLen = crmForm.all.length;
for (i = 0; i < iLen; i++)
{
o = crmForm.all[i];
switch (o.tagName)
{
case "INPUT":
case "SELECT":
case "TEXTAREA":
case "IMG":
case "IFRAME":
if (o.id != "leadqualitycode")
{
o.disabled = true;
}
break;
default:
break;
}
}

This example does set all attributes to disabled, but another option would be to replace the o.disabled = true; line with something like:

if (o.IsDirty)
{
alert("The value of " + o.name + " has changed.");
}

This allows you to determine which attributes have been changed on the form.



401 Unauthorized when accessing the webservice

While there are many situations in which you can retrieve the error message 401: Unauthorized, there are some more difficult to find than others. Of course you have After you have checked the basic settings including:
- Are the default credentials used
- Does the user have an account in CRM
- Does the user have enough priviledges in CRM
- Is Windows Authentication enabled for the CRM website
- Is anonymous access disabled for the CRM website

If these settings do not solve the issue, then it's getting intereseting. One of the reasons I have experienced, has to do with two steps. In the first step, I've been using the CrmDiscoveryService to find the url's for the CrmService as well as the CrmMetadataService. The second step was using SPN's to allow the server to pass on the kerberos ticket to the server, even though it is a single server install.

Let me get a bit more in detail. The code which I used for the first step is:


CrmDiscoveryService disco = new CrmDiscoveryService();
disco.Credentials = cred;
disco.Url = server + "/MSCRMServices/2007/AD/CrmDiscoveryService.asmx";
RetrieveOrganizationsRequest orgRequest = new RetrieveOrganizationsRequest();
RetrieveOrganizationsResponse orgResponse = (RetrieveOrganizationsResponse)disco.Execute(orgRequest);
foreach (OrganizationDetail orgdetail in orgResponse.OrganizationDetails)
{
if (orgdetail.OrganizationName == org)
{
orgdetail.CrmServiceUrl;
}
}

This returns the CrmServiceUrl. Apparently the URL for the web service can have different servername than the url you have used in the code for accessing the CrmDiscoveryService. In my particular example there was the servername (srv-crm01), a dns name (crm) and of course the localhost that could be used to access the server. For accessing the CrmDiscoveryService the dns name was used. The service did return the servername as url for the crm web service.

With the help of a system engineer we found out that this server did have kerberos as authentication method specified. In the list of service principal names (SPN's) the servername was not listed for the http protocol. After this has been added for the servername as well as the fully qualified domain name, the error was gone. To add the SPN's execute the following commands:

setspn -A http/servername
setspn -A http/servername.fullyqualified.name

Don't forget to change the servername and servername.fullyqualified.name to the correct values of your environment.

If you happen to have the same error but find another solution, please share this by adding a comment to help your peers.



Hiding elements: the next evolution

One of my colleagues, Richard McCormick, has written a blog post for the Avanade internal employees. He has asked me to make this post publically available since it might be interesting for more people. Read on and get great code samples on how to hide attributes, elements and sections.

Hiding Links & Controls in CRM 4

Before I go on, I should point out that this solution is technically unsupported by Microsoft as it goes outside the CRM SDK, however the customization is small and easy to reverse to get into a state where Microsoft is supported.

With that said, this post aims to provide a quick JavaScript solution for hiding both controls on a CRM page and links in the left-hand navigation page on an Entity Form.

The code below provides three functions that can be used to show/hide specific items on a CRM form:


var HIDE = 'none';
var SHOW = 'block';

// Function to show/hide CRM controls on a CRM form
// such as text boxes, lookups, pick-lists etc.
function SetCrmControlVisible(elementName, visibility)
{
SetElementVisible(elementName + '_c', visibility);
SetElementVisible(elementName + '_d', visibility);
}

// Fuction to show/hide specific elements on a CRM form
// such as the left-hand link items (More Addresses, Workflows
// and even custom ISV links
function SetElementVisible(elementId, visibility)
{
var elem = document.getElementById(elementId);
if (elem != null)
{
elem.style.display = visibility;
}
}

// Function to show/hide Navigation "Sections" in the left-hand
// links (Such as Sales, Marketing and Service)
function SetParentElementVisible(elementId, visibility)
{
var elem = document.getElementById(elementId);
if (elem != null && elem.parentElement != null)
{
elem.parentElement.style.display = visibility;
}
}

Add the above code to the Entity OnLoad (and ensure that you enable the event) and after that you are ready to show/hide elements on the CRM form.

By using the IE Developer toolbar, you can retrieve the id of the element to be hidden (note in IE 8 this come built in and is not a seperate download). Open an entity record (in this case a Contact), press CTRL+N to open it in a new window, and then select IE Developer Toolbar.

Using the controls, select the item you want to hide and get the element id. In this case I have selected the “Opportunities” link and the id is “navOpps



Using this, I can call the SetElementVisible function like this to hide the Opportunities link:

SetElementVisible('navOpps', HIDE);

To hide the entire “Sales” section from the left-hand navigation pane, I can once again find the id, but this time is of the parent item which would result in the following call to hide it:

SetParentElementVisible('_NA_SFA', HIDE);

Lastly, to hide an element on the form such as the “Job Title” field, you once again should use the IE Developer Toolbar to retrieve the element id and then call the SetCrmControlVisible function:

SetCrmControlVisible('jobtitle', HIDE);

By selecting either the textbox or the label, for the Job Title, the returned id will be “jobtitle_c” or “jobtitle_d” – remove everything including and after the “_” and pass that to the SerCrmControlVisible function and you are done!

Happy coding!



CRMAsyncService using 100% CPU

With one of my projects I've experienced a situation in which the CRMAsyncService is requiring 99% CPU power. Since this doesn't leave much left for CRM, we needed to get this fixed. One of my colleagues, Maarten Smid, came up with the solution of installing the Service Pack 1 for .NET 3.0. But before this can be installed, some prerequisites needed to be installed.

The first step is to install Service Pack 1 for .NET 2.0:
NetFx20SP1_x86.exe: http://go.microsoft.com/fwlink/?LinkId=98103
NetFx20SP1_x64.exe: http://go.microsoft.com/fwlink/?LinkId=98104

The second step is to install Microsoft XML Paper Specification Essentials Pack (XPSEP): http://www.microsoft.com/downloads/details.aspx?FamilyId=B8DCFFDD-E3A5-44CC-8021-7649FD37FFEE&displaylang=en

And finally SP1 for .NET 3.0:
NetFx20SP1_x86.exe: http://go.microsoft.com/fwlink/?LinkId=98105
NetFx20SP1_x64.exe: http://go.microsoft.com/fwlink/?LinkId=98106



Plugin registration tools for Visual Studio 2005

The newest update of the SDK, version 4.0.8, includes updated projects for the plugin registration tool as well as the plugin developer tool. While I usually am pleased with updates, this update could cause some issues for some people. The updated projects are build in Visual Studio 2008 whereas they used to be build in Visual Studio 2005. This means that you will not be able anymore to run the projects on machines running VS2005 including the demo VPC.

I've luckily backupped the previous SDK and uploaded both the Visual Studio projects so that you can download them if neccesary:

Plugin Registration VS2005 (zip)
Plugin Developer VS2005 (zip)

Of course you could remove the solution file, create a new solution and add the current project as well, but the downloads work just as good.

Good luck with building your plugins!



Displaying inactive records in associated view

In some situations you do want to show the inactive records in an associated view as well. My fellow MVP George Doubinski wrote an article about how to solve this by using a plugin:
http://crm.georged.id.au/post/2008/03/07/Displaying-inactive-records-in-associated-view.aspx.

In this post I will show that there are more routes to Rome. I've looked into the option of editing the query which is being executed against CRM. This seemed to be more easy then expected. What I did is:
- Export Customizations for the specific entity
- Open the XML file with a text editor
- Look for the section ""
- Look for the saved query with the localized name of the associated view
- In that saved query look for the filter that is specified in the columnset
- Remove the filter
- Save the Customizations file
- Import the Customizations file
- Publish the Customizations

Here's an example:


<ImportExportXml version="4.0.0.0" languagecode="1043" generatedBy="OnPremise">
<Entities>
<Entity>
<Name .... />
....
<SavedQueries>
<savedquery>
<columnsetxml>
<columnset version="3.0">
<column>fullname</column>
<column>createdon</column>
<column>statuscode</column>
<column>subject</column>
<column>leadid</column>
<ascend>createdon</ascend>
<!-- Delete from here -->
<filter type="and">
<condition column="statecode" value="0" operator="eq" />
</filter>
<!-- Delete till here -->
</columnset>
</columnsetxml>
....
</savedquery>
</SavedQueries>
....
</Entity>
</Entities>
....
</ImportExportXml>

Keep in mind that you do add the attribute StateCode or StatusCode to the associated view as well, so that end users can see which records are current and which are inactive.

One of the locations that this is useful, is when you are looking into relationships. The out of the box entity does allow you to select party 1, party 2, role 1 and role 2, but no real extensibility options. You could recreate that entity and add useful attributes like startdate and enddate. As soon as the end date has passed, then the record can be deactivated by using workflow. The fact that a person had a professional relationship with a company is information that you do want to keep visible, especially in the associated view.