<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[CRM 2013 - Alexander Development]]></title><description><![CDATA[CRM 2013 - Alexander Development]]></description><link>https://alexanderdevelopment.net/</link><image><url>https://alexanderdevelopment.net/favicon.png</url><title>CRM 2013 - Alexander Development</title><link>https://alexanderdevelopment.net/</link></image><generator>Ghost 1.20</generator><lastBuildDate>Fri, 24 Apr 2026 14:18:17 GMT</lastBuildDate><atom:link href="https://alexanderdevelopment.net/tag/crm-2013/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[XrmToolBox plugin for moving access team templates]]></title><description><![CDATA[<div class="kg-card-markdown"><p>Late last year I created a console application for moving access team templates between Dynamics CRM organizations, and I described it in this <a href="https://alexanderdevelopment.net/post/2014/12/12/console-application-for-moving-dynamics-crm-access-team-templates/">blog post</a>. Following up on that, I've created an XrmToolBox plugin to make it even easier to move access team templates (with GUIDs) between CRM organizations. I've</p></div>]]></description><link>https://alexanderdevelopment.net/post/2015/10/20/xrmtoolbox-plugin-for-moving-access-team-templates/</link><guid isPermaLink="false">5a5837236636a30001b977d2</guid><category><![CDATA[Microsoft Dynamics CRM]]></category><category><![CDATA[utilities]]></category><category><![CDATA[CRM 2015]]></category><category><![CDATA[CRM 2013]]></category><dc:creator><![CDATA[Lucas Alexander]]></dc:creator><pubDate>Tue, 20 Oct 2015 22:04:35 GMT</pubDate><media:content url="https://alexanderdevelopment.net/content/images/2015/10/toolbox-01-1.png" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://alexanderdevelopment.net/content/images/2015/10/toolbox-01-1.png" alt="XrmToolBox plugin for moving access team templates"><p>Late last year I created a console application for moving access team templates between Dynamics CRM organizations, and I described it in this <a href="https://alexanderdevelopment.net/post/2014/12/12/console-application-for-moving-dynamics-crm-access-team-templates/">blog post</a>. Following up on that, I've created an XrmToolBox plugin to make it even easier to move access team templates (with GUIDs) between CRM organizations. I've also added functionality to automatically enable access teams on the relevant entities in the target organization if desired.</p>
<p>Let's take a look at the tool in action.</p>
<h4 id="beforeexecution">Before execution</h4>
<p>Here are the access team templates in the source organization.<br>
<img src="https://alexanderdevelopment.net/content/images/2015/10/source.png#img-thumbnail" alt="XrmToolBox plugin for moving access team templates"></p>
<p>Here are the access team templates in the target organization before we run the tool.<br>
<img src="https://alexanderdevelopment.net/content/images/2015/10/target-pre.png#img-thumbnail" alt="XrmToolBox plugin for moving access team templates"></p>
<p>Note the &quot;Dog&quot; entity does not have access teams enabled.<br>
<img src="https://alexanderdevelopment.net/content/images/2015/10/dog-pre.png#img-thumbnail" alt="XrmToolBox plugin for moving access team templates"></p>
<h4 id="runningthetool">Running the tool</h4>
<p>First open XrmToolBox. Look at that handsome dog!<br>
<img src="https://alexanderdevelopment.net/content/images/2015/10/toolbox-01.png#img-thumbnail" alt="XrmToolBox plugin for moving access team templates"></p>
<p>Next select the Access Team Template Mover plugin and connect to an organization. That organization is set as the source, but you can change it.<br>
<img src="https://alexanderdevelopment.net/content/images/2015/10/toolbox-02.png#img-thumbnail" alt="XrmToolBox plugin for moving access team templates"></p>
<p>Click the select target button to select the target organization.<br>
<img src="https://alexanderdevelopment.net/content/images/2015/10/toolbox-03.png#img-thumbnail" alt="XrmToolBox plugin for moving access team templates"></p>
<p>Once both source and target are set, the copy templates button will turn green and become enabled.<br>
<img src="https://alexanderdevelopment.net/content/images/2015/10/toolbox-04.png#img-thumbnail" alt="XrmToolBox plugin for moving access team templates"></p>
<p>You can also uncheck the option to automatically enable access teams for target organization entities. If you do, you will receive a confirmation prompt.<br>
<img src="https://alexanderdevelopment.net/content/images/2015/10/toolbox-05.png#img-thumbnail" alt="XrmToolBox plugin for moving access team templates"></p>
<p>Executing the copy with the enable access teams for target organization entities option checked copies three records in this scenario. It updates the access team template that already existed in the target and creates two new ones.<br>
<img src="https://alexanderdevelopment.net/content/images/2015/10/toolbox-06.png#img-thumbnail" alt="XrmToolBox plugin for moving access team templates"></p>
<h4 id="afterexecution">After execution</h4>
<p>Because the enable access teams for target organization entities option was selected, the &quot;Dog&quot; entity in the target now has access teams enabled.<br>
<img src="https://alexanderdevelopment.net/content/images/2015/10/dog-post.png#img-thumbnail" alt="XrmToolBox plugin for moving access team templates"></p>
<p>Finally here is the updated list of access team templates in the target organization.<br>
<img src="https://alexanderdevelopment.net/content/images/2015/10/target-post-1.png#img-thumbnail" alt="XrmToolBox plugin for moving access team templates"></p>
<h4 id="gettingtheplugin">Getting the plugin</h4>
<p>The source code for the plugin is available in my GitHub repository <a href="https://github.com/lucasalexander/AlexanderDevelopment.AccessTeamTemplateMover">here</a>.</p>
<p>The compiled plugin can be downloaded <a href="https://github.com/lucasalexander/AlexanderDevelopment.AccessTeamTemplateMover/releases/tag/v1.0">here</a>.</p>
</div>]]></content:encoded></item><item><title><![CDATA[Console application for moving Dynamics CRM access team templates]]></title><description><![CDATA[<div class="kg-card-markdown"><p>When Dynamics CRM 2013 was released, I thought access teams were the new killer feature in that version, and I even developed custom workflow activity code to make <a href="https://alexanderdevelopment.net/post/2014/01/09/managing-microsoft-dynamics-crm-2013-access-team-membership-using-connections-2/">managing access team membership easier by using connection records</a>. I have thus far not had an opportunity to use access teams in</p></div>]]></description><link>https://alexanderdevelopment.net/post/2014/12/12/console-application-for-moving-dynamics-crm-access-team-templates/</link><guid isPermaLink="false">5a5837226636a30001b9775d</guid><category><![CDATA[Microsoft Dynamics CRM]]></category><category><![CDATA[programming]]></category><category><![CDATA[C#]]></category><category><![CDATA[FetchXML]]></category><category><![CDATA[CRM 2013]]></category><dc:creator><![CDATA[Lucas Alexander]]></dc:creator><pubDate>Sat, 13 Dec 2014 00:00:00 GMT</pubDate><media:content url="https://alexanderdevelopment.net/content/images/2015/10/team-mover-cli.PNG" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://alexanderdevelopment.net/content/images/2015/10/team-mover-cli.PNG" alt="Console application for moving Dynamics CRM access team templates"><p>When Dynamics CRM 2013 was released, I thought access teams were the new killer feature in that version, and I even developed custom workflow activity code to make <a href="https://alexanderdevelopment.net/post/2014/01/09/managing-microsoft-dynamics-crm-2013-access-team-membership-using-connections-2/">managing access team membership easier by using connection records</a>. I have thus far not had an opportunity to use access teams in a real project, so I was disappointed to read <a href="https://crmbusiness.wordpress.com/2014/12/01/crm-2013-why-are-access-teams-marooned/">this blog post</a> by Ben Hosking (AKA &quot;The Hosk&quot;) about how Microsoft doesn't provide any out-of-the-box capabilities for moving access team templates between Dynamics CRM organizations. In that post, the Hosk says, &quot;It’s possible someone could build a console app to import the access team templates but as yet no one has created it.&quot; Challenge accepted.</p>
<p>My CRM Access Team Mover tool is available for download <a href="https://github.com/lucasalexander/CrmAccessTeamMover/releases/tag/v1.0">here,</a> and the source code is available on <a href="https://github.com/lucasalexander/CrmAccessTeamMover">GitHub</a>.</p>
<p>To copy/update access team templates from one organization to another using my tool, do the following:</p>
<ol start="2">
<li>Execute the tool from the command line.</li>
<li>When prompted to enter the the source connection string, supply a complete <a href="http://msdn.microsoft.com/en-us/library/gg695810.aspx">Dynamics CRM simplified connection string</a> for the source organization.</li>
<li>When prompted to enter the the target connection string, supply a complete <a href="http://msdn.microsoft.com/en-us/library/gg695810.aspx">Dynamics CRM simplified connection string</a> for the target organization.</li>
<li>The tool will attempt to update existing team templates based on their ids. If any source records don't already exist in the target environment, they will be created (with the identical id).</li>
<li>Any failures will be reported by the tool. Errors will be encountered if the target schema doesn't match the source schema for the relevant entities.</li>
</ol>
<p>Here's a screenshot of the tool being executed:</p>
<p><img src="https://alexanderdevelopment.net/content/images/2014/12/team-mover-cli.PNG#img-thumbnail" alt="Console application for moving Dynamics CRM access team templates"></p>
<p>And the image below shows access teams in a source and target system after I ran the tool. The source system has two access team templates for entities that don't exist in the target system, so they were not created.</p>
<p><img src="https://alexanderdevelopment.net/content/images/2014/12/team-mover.PNG#img-thumbnail" alt="Console application for moving Dynamics CRM access team templates"></p>
<h4 id="theapproachindetail">The approach in detail</h4>
<p>Access team templates are just regular Dynamics CRM records, and they can be accessed through the Dynamics CRM organization web service like most other records. (You can see a list of the messages and methods available for 2013 <a href="http://msdn.microsoft.com/en-us/library/dn481595%28v=crm.6%29.aspx">here</a>). Because of this, all I needed to do was query a source CRM organization for access teams, and then loop through the results to recreate each one in the target organization.</p>
<p>At first I tried to retrieve the teamtemplate records using a RetrieveMultiple query for all attributes, but that resulted in a strange service fault involving the issystem attribute that looks like a bug in CRM. I then decided to use a FetchXML query instead. To make it easy on myself, I first make a metadata request to retrieve all the teamtemplate attributes, and then I dynamically build a FetchXML query for everything except the issystem field. That query then gets executed. The code for all the source organization operations is below:</p>
<pre><code>using (OrganizationService service = new OrganizationService(sourceConn))  
{  
 try  
 {  
 //attributes to exclude from the query  
 List&lt;string&gt; IgnoredAttributes = new List&lt;string&gt; { &quot;issystem&quot; };  
  
 Console.WriteLine(&quot;Retrieving entity metadata . . .&quot;);  
 RetrieveEntityRequest entityreq = new RetrieveEntityRequest  
 {  
 LogicalName = &quot;teamtemplate&quot;,  
 EntityFilters = Microsoft.Xrm.Sdk.Metadata.EntityFilters.Attributes  
 };  
 RetrieveEntityResponse entityres = (RetrieveEntityResponse)service.Execute(entityreq);  
 string fetchXml = &quot;&lt;fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false'&gt;&quot;;  
 fetchXml += &quot;&lt;entity name='teamtemplate'&gt;&quot;;  
  
 foreach (AttributeMetadata amd in entityres.EntityMetadata.Attributes)  
 {  
 if (!IgnoredAttributes.Contains(amd.LogicalName))  
 {  
 fetchXml += &quot;&lt;attribute name='&quot; + amd.LogicalName + &quot;' /&gt;&quot;;  
 //Console.WriteLine(amd.LogicalName);  
 }  
 }  
 fetchXml += &quot;&lt;/entity&gt;&lt;/fetch&gt;&quot;;  
  
 Console.WriteLine(&quot;&quot;);  
 Console.WriteLine(&quot;Exporting data . . .&quot;);  
 exported = service.RetrieveMultiple(new FetchExpression(fetchXml));  
 }  
 catch (FaultException&lt;Microsoft.Xrm.Sdk.OrganizationServiceFault&gt; ex)  
 {  
 Console.WriteLine(&quot;Could not export data: {0}&quot;, ex.Message);  
 return;  
 }  
}
</code></pre>
<p>Once the teamtemplate records have been retrieved, they are then created in the target organization:</p>
<pre><code>using (OrganizationService service = new OrganizationService(targetConn))  
{  
 if (exported.Entities.Count &gt; 0)  
 {  
 foreach (Entity entity in exported.Entities)  
 {  
 try  
 {  
 //try to update first  
 try  
 {  
 service.Update(entity);  
 }  
 catch (FaultException&lt;Microsoft.Xrm.Sdk.OrganizationServiceFault&gt;)  
 {  
 //if update fails, then create  
 service.Create(entity);  
 }  
 }  
 catch (FaultException&lt;Microsoft.Xrm.Sdk.OrganizationServiceFault&gt; ex)  
 {  
 //if everything fails, return error  
 Console.WriteLine(&quot;Error: {0} - {1}&quot;, entity.Id, entity[&quot;teamtemplatename&quot;]);  
 }  
 }  
 }  
}
</code></pre>
<p>You'll note this code not only creates new records, but it also tries to update existing records, so if you have a teamtemplate that's changed, this will handle it if the record was created with the same GUID in the target system as in the source system.</p>
<p>What do you think about this approach? Would you have done anything differently?</p>
</div>]]></content:encoded></item><item><title><![CDATA[Lots of Dynamics CRM sample code now available on GitHub]]></title><description><![CDATA[<div class="kg-card-markdown"><p>Over the years, I've shared a lot of code as part of my blogging, but I didn't do a particularly good job of making the code easily available outside of the individual blog posts. I also realized I didn't even have all the code where I could easily get to</p></div>]]></description><link>https://alexanderdevelopment.net/post/2014/12/10/lots-of-dynamics-crm-sample-code-now-available-on-github/</link><guid isPermaLink="false">5a5837226636a30001b97744</guid><category><![CDATA[Microsoft Dynamics CRM]]></category><category><![CDATA[CRM 2013]]></category><dc:creator><![CDATA[Lucas Alexander]]></dc:creator><pubDate>Thu, 11 Dec 2014 00:00:00 GMT</pubDate><media:content url="https://alexanderdevelopment.net/content/images/2015/10/github.png" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://alexanderdevelopment.net/content/images/2015/10/github.png" alt="Lots of Dynamics CRM sample code now available on GitHub"><p>Over the years, I've shared a lot of code as part of my blogging, but I didn't do a particularly good job of making the code easily available outside of the individual blog posts. I also realized I didn't even have all the code where I could easily get to it from my PC!</p>
<p>To remedy those problems, I've consolidated all the non-trivial code I've shared in blog posts in a single repository and made it available on GitHub <a href="https://github.com/lucasalexander/Crm-Sample-Code">here</a>.</p>
<p>Many of the samples were originally written for Dynamics CRM 2011, but each solution has been updated to use the Microsoft Dynamics CRM 2013 Service Pack 1 Update Rollup 1 SDK (version 6.1.1 installed from <a href="http://www.nuget.org/packages/Microsoft.CrmSdk.Workflow/6.1.1">NuGet</a>). I have verified that each solution builds, but I have not necessarily deployed or tested, so the code is all &quot;use at your own risk.&quot;</p>
</div>]]></content:encoded></item><item><title><![CDATA[System.TimeoutException when importing translations with XrmToolBox]]></title><description><![CDATA[<div class="kg-card-markdown"><p>I've been using the completely awesome <a href="https://xrmtoolbox.codeplex.com/">XrmToolBox</a> to import translations for a large CRM organization, but I kept running into System.TimeoutException errors like the following:<br>
<em>System.TimeoutException: The HTTP request to '<a href="https://XXX/XrmServices/2011/Organization.svc">https://XXX/XrmServices/2011/Organization.svc</a>' has exceeded the allotted timeout of 00:02:00. The</em></p></div>]]></description><link>https://alexanderdevelopment.net/post/2014/12/02/system-timeoutexception-when-importing-translations-with-xrmtoolbox/</link><guid isPermaLink="false">5a5837226636a30001b97763</guid><category><![CDATA[Microsoft Dynamics CRM]]></category><category><![CDATA[CRM 2013]]></category><dc:creator><![CDATA[Lucas Alexander]]></dc:creator><pubDate>Wed, 03 Dec 2014 00:00:00 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><p>I've been using the completely awesome <a href="https://xrmtoolbox.codeplex.com/">XrmToolBox</a> to import translations for a large CRM organization, but I kept running into System.TimeoutException errors like the following:<br>
<em>System.TimeoutException: The HTTP request to '<a href="https://XXX/XrmServices/2011/Organization.svc">https://XXX/XrmServices/2011/Organization.svc</a>' has exceeded the allotted timeout of 00:02:00. The time allotted to this operation may have been a portion of a longer timeout.</em></p>
<p><em><img src="https://alexanderdevelopment.net/content/images/2014/12/easy-translator-timeout.PNG#img-thumbnail" alt=""></em></p>
<p>After trying a few different things (running from the CRM app server, only importing translations for one thing at a time, etc.), I kept running into problems, so I decided to modify the source code to add adjustable timeout values to the CRM connections.</p>
<p>This required making changes to the McTools.Xrm.Connection.ConnectionDetail class and the McTools.Xrm.Connection.WinForms.Connection form class. Here is a download link for my modified versions - <a href="https://alexanderdevelopment.net/Media/Default/attachments/XrmToolBox-connection-files.zip">XrmToolBox-connection-files.zip</a>. To update your copy, do the following:</p>
<ol start="2">
<li>Extract the contents of my attachment and place the files in their respective project folders under the XrmToolBox\Connection Projects directory.</li>
<li>Build the McTools.Xrm.Connection.WinForms project.</li>
<li>Copy McTools.Xrm.Connection.dll and McTools.Xrm.Connection.WinForms.dll files to your XrmToolbox directory. If prompted to overwrite the existing files, say yes.</li>
</ol>
<p>You will need to update your existing connections to use the new timeout value. The timeout is entered in the format of HH:MM:SS, so if you want to make the timeout 10 minutes, you would enter 00:10:00 in the timeout value box.</p>
<p><img src="https://alexanderdevelopment.net/content/images/2014/12/connection-window.PNG#img-thumbnail" alt=""></p>
</div>]]></content:encoded></item><item><title><![CDATA[Using IDOL OnDemand for text analysis in Dynamics CRM - part 3]]></title><description><![CDATA[<div class="kg-card-markdown"><p>In my <a href="https://alexanderdevelopment.net/post/2014/09/24/using-idol-ondemand-for-text-analysis-in-dynamics-crm-part-2/">last post</a> I provided a detailed walkthrough of how to perform sentiment analysis on incoming emails received in Microsoft Dynamics CRM by parsing them with <a href="https://www.idolondemand.com/" target="_blank" rel="nofollow">HP IDOL OnDemand’s</a> sentiment analysis API and then storing the calculated sentiment values on the email records. In today’s post, I</p></div>]]></description><link>https://alexanderdevelopment.net/post/2014/09/28/using-idol-ondemand-for-text-analysis-in-dynamics-crm-part-3/</link><guid isPermaLink="false">5a5837226636a30001b9773a</guid><category><![CDATA[Microsoft Dynamics CRM]]></category><category><![CDATA[CRM 2013]]></category><category><![CDATA[IDOL OnDemand]]></category><category><![CDATA[text analysis]]></category><dc:creator><![CDATA[Lucas Alexander]]></dc:creator><pubDate>Mon, 29 Sep 2014 00:00:00 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><p>In my <a href="https://alexanderdevelopment.net/post/2014/09/24/using-idol-ondemand-for-text-analysis-in-dynamics-crm-part-2/">last post</a> I provided a detailed walkthrough of how to perform sentiment analysis on incoming emails received in Microsoft Dynamics CRM by parsing them with <a href="https://www.idolondemand.com/" target="_blank" rel="nofollow">HP IDOL OnDemand’s</a> sentiment analysis API and then storing the calculated sentiment values on the email records. In today’s post, I will show a similar, but slightly more complex integration that enables &quot;find similar&quot; functionality for emails stored in Dynamics CRM.</p>
<p>Continuing with the scenario from my previous post, let’s assume you manage a Microsoft Dynamics CRM system (online or on-premise) for an organization that provides customer service and support via a variety of channels including phone, email and live chat. In order to help agents resolve inbound email cases more quickly, the operations team has asked you to add an embedded display on the email form that shows conceptually similar email records so the agents can easily view them. Just as before, we’re in luck because IDOL OnDemand has a <a href="https://www.idolondemand.com/developer/apis/findsimilar#overview" target="_blank" rel="nofollow">find similar API</a>.</p>
<h4 id="thesolutionapproach">The solution approach</h4>
<p>Unlike the sentiment analysis solution I discussed previously where we used a workflow to retrieve a sentiment score and write it directly to the email record, the find similar email display will require two different calls to the IDOL OnDemand service because to find similar items, IDOL OnDemand needs to have an index of items to search. We’ll need to populate this index with emails as they’re received, and then we’ll run the find similar search against the index when agents view an email record. Thus, the solution I am showing today has two pieces:</p>
<ol><li>First, there is a custom workflow activity that adds inbound emails to an IDOL OnDemand text index using the <a href="https://www.idolondemand.com/developer/apis/addtotextindex" target="_blank" rel="nofollow">add to text index API</a>.</li><li>Second, there is an HTML web resource that can be embedded on the email form that uses an AJAX call to the find similar API to retrieve similar emails from the index and then display summary data and a hyperlink to the actual CRM record for each result to the end user.</li></ol>
<h4 id="creatingtheindex">Creating the index</h4>
<p>Before we can populate an index with data, we need to have an index that can be populated. Although IDOL OnDemand offers APIs that can be used to manage indexes programmatically, it’s easier in this case to use the index management tools that are available on the <a href="https://www.idolondemand.com/account/account.html" target="_blank" rel="nofollow">main account screen</a>. On that screen, click the &quot;Create, Manage, Index and Search your own Text Indexes&quot; link, then click the &quot;create text index&quot; button on the next page. Give your index a name, and then just accept the default options for each of the following prompts in the wizard.* Remember the name you selected because you’ll need to use it later. My index name is &quot;lucas-test.&quot;</p>
<h4 id="theindexingprocess">The indexing process</h4>
<p>IDOL OnDemand can index JSON documents that contain both standard and custom fields, so we’re going to create a JSON object that contains the email body, the email subject line and the email activity id as the index reference (basically a primary key) field. Because an IDOL OnDemand index has a standard field called title that is, per the <a href="https://www.idolondemand.com/developer/docs/StandardFlavor.html" target="_blank" rel="nofollow">index developer documentation</a>, considered &quot;highly relevant,&quot; we’ll also set that to the value of the email subject. Our logical mappings will look like this:</p>
<ul>
<li>Email "body" (description) -&gt; Index "content"
</li><li>Email "subject" -&gt; Index "title"
</li><li>Email "subject" -&gt; Index "subject"
</li><li>Email "activityid" -&gt; Index "reference"
</li></ul>
<p>You can, of course, index lots of other fields like &quot;to,&quot; &quot;from,&quot; &quot;date,&quot; etc., and the more complete your index, the better your search results will be.</p>
<p>In order to populate the index with the fields above, the custom workflow activity needs input parameters for the email body, subject and activity id. Here's how we define them:<pre><code>[Input(&quot;Content&quot;)]<br>
public InArgument&lt;String&gt; Content { get; set; }<br>
[Input(&quot;Subject&quot;)]<br>
public InArgument&lt;String&gt; Subject { get; set; }<br>
[Input(&quot;Email&quot;)]<br>
[ReferenceTarget(&quot;email&quot;)]<br>
public InArgument&lt;EntityReference&gt; Email { get; set; }</code></pre></p>
<p>We also need to set up three variables to help us call IDOL OnDemand. They are the URL for the find similar API, the API key to authorize our requests and the name of the text index that will hold the email data:<pre><code>//address of the service to which you will post your json messages<br>
private string _webAddress = &quot;<a href="https://api.idolondemand.com/1/api/sync/addtotextindex/v1">https://api.idolondemand.com/1/api/sync/addtotextindex/v1</a>&quot;;<br>
//name of the text index<br>
private string _indexName = &quot;lucas-test&quot;;<br>
//idol ondemand api key<br>
private string _apiKey = &quot;XXXXXXXXXXXXXXXX&quot;;</code></pre></p>
<p>Now we’re ready to index the inbound email using the following steps:</p>
<ol><li>Check whether the email description field contains text.</li><li>If it does, strip HTML tags using a helper function.</li><li>Serialize the index request to a JSON object.</li><li>Pass the index request object to IDOL OnDemand with an HttpWebRequest. (In order to keep this example simple, we’re going to ignore the response from IDOL OnDemand, but you might want to do something with the response if you try this out on your own.)</li></ol>
<p>Here's the code that does all of this:</p>
<pre><code>string inputText = Content.Get(executionContext);
if (inputText != string.Empty)
{
	inputText = HtmlTools.StripHTML(inputText);
	IndexDocument myDoc = new IndexDocument
	{
	 Content = inputText,
	  Reference = (Email.Get(executionContext)).Id.ToString(),
	  Subject = Subject.Get(executionContext),
	  Title = Subject.Get(executionContext)
	};
	DocumentWrapper myWrapper = new DocumentWrapper();
	myWrapper.Document = new List&lt;IndexDocument&gt;();
	myWrapper.Document.Add(myDoc);
	//serialize the myjsonrequest to json
	System.Runtime.Serialization.Json.DataContractJsonSerializer serializer = new System.Runtime.Serialization.Json.DataContractJsonSerializer(myWrapper.GetType());
	MemoryStream ms = new MemoryStream();
	serializer.WriteObject(ms, myWrapper);
	string jsonMsg = Encoding.Default.GetString(ms.ToArray());
	//create the webrequest object and execute it (and post jsonmsg to it)
	HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(\_webAddress);
	//set request content type so it is treated as a regular form post
	req.ContentType = "application/x-www-form-urlencoded";
	//set method to post
	req.Method = "POST";
	StringBuilder postData = new StringBuilder();
	//HttpUtility.UrlEncode
	//set the apikey request value
	postData.Append("apikey=" + System.Uri.EscapeDataString(\_apiKey) + "&amp;");
	//postData.Append("apikey=" + \_apiKey + "&amp;");
	//set the json request value
	postData.Append("json=" + jsonMsg + "&amp;");
	//set the index name request value
	postData.Append("index=" + _indexName);
	//create a stream
	byte[] bytes = System.Text.Encoding.ASCII.GetBytes(postData.ToString());
	req.ContentLength = bytes.Length;
	System.IO.Stream os = req.GetRequestStream();
	os.Write(bytes, 0, bytes.Length);
	os.Close();
	//get the response
	System.Net.WebResponse resp = req.GetResponse();
	//deserialize the response to a ResponseBody object
	ResponseBody myResponse = new ResponseBody();
	System.Runtime.Serialization.Json.DataContractJsonSerializer deserializer = new System.Runtime.Serialization.Json.DataContractJsonSerializer(myResponse.GetType());
	myResponse = deserializer.ReadObject(resp.GetResponseStream()) as ResponseBody;
}</code></pre>
<p>The StripHTML function is the same function <a href="http://www.codeproject.com/Articles/11902/Convert-HTML-to-Plain-Text" target="_blank" rel="nofollow">from CodeProject</a> that used in my sentiment analysis post, and the code is included in the download at the end of this post.</p>
<p>Here are the classes used for serializing the JSON request and deserializing the JSON response:<pre><code>//DataContract decoration necessary for serialization/deserialization to work properly<br>
[DataContract]<br>
public class DocumentWrapper<br>
{<br>
//datamember name value indicates name of json field to which data will be serialized/from which data will be deserialized<br>
[DataMember(Name = &quot;document&quot;)]<br>
public List&lt;IndexDocument&gt; Document { get; set; }<br>
}<br>
//DataContract decoration necessary for serialization/deserialization to work properly<br>
[DataContract]<br>
public class IndexDocument<br>
{<br>
[DataMember(Name = &quot;title&quot;)]<br>
public string Title { get; set; }<br>
[DataMember(Name = &quot;reference&quot;)]<br>
public string Reference { get; set; }<br>
[DataMember(Name = &quot;subject&quot;)]<br>
public string Subject { get; set; }<br>
[DataMember(Name = &quot;content&quot;)]<br>
public string Content { get; set; }<br>
}<br>
//DataContract decoration necessary for serialization/deserialization to work properly<br>
[DataContract]<br>
public class ResponseBody<br>
{<br>
[DataMember(Name = &quot;index&quot;)]<br>
public string Index { get; set; }<br>
[DataMember(Name = &quot;references&quot;)]<br>
public List&lt;ResponseReference&gt; References { get; set; }<br>
}<br>
//DataContract decoration necessary for serialization/deserialization to work properly<br>
[DataContract]<br>
public class ResponseReference<br>
{<br>
[DataMember(Name = &quot;reference&quot;)]<br>
public string Reference { get; set; }<br>
[DataMember(Name = &quot;id&quot;)]<br>
public int Id { get; set; }<br>
}</code></pre></p>
<h4 id="theresultsdisplay">The results display</h4>
<p>The code above takes care of getting the email data into our IDOL OnDemand text index, but now we need an easy way to execute the find similar queries from a web resource on the email form. This is actually incredibly easy to do with a little bit of JavaScript and jQuery.</p>
<p>To keep things simple, we’re going to make an assumption that all CRM emails will be in the IDOL OnDemand index by the time an agent opens one, so we can tell IDOL OnDemand to find similar records using the email’s activityid field, which we are populating as the index reference field in the code above. This also means we can embed the web resource on the email form without having to use any custom code because the Dynamics CRM form designer lets us specify that an iframe will be passed the record id when the form loads.</p>
<p>If turned out that we couldn’t count on the emails always being indexed before the agents viewed them, we could search the index using the relevant fields from an email record instead of using an index reference, but this would require some changes to what I’m showing today.</p>
<p>Our web resource will do the following:</p>
<ol><li>Load the jQuery library (in my sample, I’m using the Google CDN).</li><li>Parse the record id from the value supplied in the query string that is passed to the iframe.</li><li>Execute a jQuery "getJSON" call to IDOL OnDemand’s find similar API.</li><li>Loop through the output results and write them to the screen.</li></ol>
<p>Let’s take a look at the code in detail.</p>
<p>First, we load jQuery:</p>
<pre><code>&lt;script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"&gt;&lt;/script&gt;</code></pre>
<p>Next we need a function that can extract query string values:<pre><code>function getParameterByName(name) {<br>
name = name.replace(/[[]/, &quot;\[&quot;).replace(/[]]/, &quot;\]&quot;);<br>
var regex = new RegExp(&quot;[\?&amp;]&quot; + name + &quot;=([^&amp;#]*)&quot;),<br>
results = regex.exec(location.search);<br>
return results == null ? &quot;&quot; : decodeURIComponent(results[1].replace(/+/g, &quot; &quot;));<br>
}</code></pre></p>
<p>Using the getParameterByName function, we parse the email’s activity id and prepare if for querying IDOL OnDemand like so:<pre><code>var entityId = getParameterByName('id');<br>
//strip brackets because we indexed a string value of the guid without them<br>
entityId = entityId.replace(&quot;{&quot;,&quot;&quot;).replace(&quot;}&quot;,&quot;&quot;);</code></pre></p>
<p>Finally we send the find similar request and process the results:</p>
<pre><code>var indexName = "lucas-test";
var apiKey = "f90f3fc7-0e36-40f0-b508-734f45fe9033";
$.getJSON("<a href="https://api.idolondemand.com/1/api/sync/findsimilar/v1?indexes=&quot;+indexName+&quot;&amp;apikey=&quot;+apiKey+&quot;&amp;" target="_blank" rel="nofollow">https://api.idolondemand.com/1/api/sync/findsimilar/v1?indexes="+indexName+"&amp;apikey="+apiKey+"&amp;</a> summary=context&amp;index\_reference="+entityId,function(data){
	var content = "";
	$(content).text("");
	$.each(data.documents, function(i,document){
		content += '&lt;p&gt;&lt;a href="/main.aspx?etn=email&amp;id={' + document.reference + '}&amp;newWindow=true&amp;pagetype=entityrecord" target="_blank"&gt;' + document.title + '&lt;/a&gt;';
		content += '&lt;br /&gt;Score: ' + document.weight + '';
		content += '&lt;br /&gt;' + document.summary + '&lt;/p&gt;';
	});
	$(content).appendTo("#results");
});</code></pre>
<p>This writes the results to a div element on the page with an id of &quot;results.&quot; You’ll see that each result shows the email subject (stored as &quot;title&quot; in our index) as a hyperlink to the actual email record, a similarity score value (weight) and a summary of the similar text.</p>
<p>So that’s it for getting started with IDOL OnDemand’s find similar API for Microsoft Dynamics CRM data. Full code for this is in my <a href="https://github.com/lucasalexander/CRM-IdolOnDemand-Tools">CRM-IdolOnDemand-Tools repository</a> on GitHub. Does this look like something you could use in your organization? If so, let us know your thoughts in the comments!</p>
<p><em>* If you want to learn more about IDOL OnDemand text indexes, I suggest you look at the text indexes section of the </em><a href="https://www.idolondemand.com/developer/docs/" target="_blank" rel="nofollow"><em>developer documentation</em></a><em>.</em></p>
<p><em>A version of this post was originally published on the HP Enterprise Services Application Services blog.</em></p>
</div>]]></content:encoded></item><item><title><![CDATA[Using IDOL OnDemand for text analysis in Dynamics CRM - part 2]]></title><description><![CDATA[<div class="kg-card-markdown"><p>In my <a href="https://alexanderdevelopment.net/post/2014/09/19/using-idol-ondemand-for-text-analysis-in-dynamics-crm-part-1/">last post</a> I provided an overview of <a target="_blank" href="https://www.idolondemand.com/" rel="nofollow">HP IDOL OnDemand</a> and discussed a couple of things you can do with it to process and analyze data stored in a Microsoft Dynamics CRM instance. Today I'll provide a detailed walkthrough of how perform sentiment analysis on incoming emails using</p></div>]]></description><link>https://alexanderdevelopment.net/post/2014/09/23/using-idol-ondemand-for-text-analysis-in-dynamics-crm-part-2/</link><guid isPermaLink="false">5a5837226636a30001b9773f</guid><category><![CDATA[Microsoft Dynamics CRM]]></category><category><![CDATA[CRM 2013]]></category><category><![CDATA[IDOL OnDemand]]></category><category><![CDATA[text analysis]]></category><dc:creator><![CDATA[Lucas Alexander]]></dc:creator><pubDate>Wed, 24 Sep 2014 00:00:00 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><p>In my <a href="https://alexanderdevelopment.net/post/2014/09/19/using-idol-ondemand-for-text-analysis-in-dynamics-crm-part-1/">last post</a> I provided an overview of <a target="_blank" href="https://www.idolondemand.com/" rel="nofollow">HP IDOL OnDemand</a> and discussed a couple of things you can do with it to process and analyze data stored in a Microsoft Dynamics CRM instance. Today I'll provide a detailed walkthrough of how perform sentiment analysis on incoming emails using IDOL OnDemand. First let's consider a typical scenario for sentiment analysis.</p>
<p>Assume you manage a Microsoft Dynamics CRM system (online or on-premise) for an organization that provides customer service and support via a variety of channels including phone, email and live chat. The business manager has asked you to set up a mechanism that will categorize the sentiment of incoming emails sent to the main &quot;support&quot; address so that they can be assigned to different queues for handling by agents. Fortunately for you, IDOL OnDemand offers a <a target="_blank" href="https://www.idolondemand.com/developer/apis/analyzesentiment" rel="nofollow">sentiment analysis API</a>!</p>
<p>This API parses a body of text and returns a text value for sentiment (positive, neutral or negative) and a decimal sentiment score between 1 and -1 (larger positive scores indicating positivity and larger negative scores indicating negativity)*. Let's look at how we can make use of that API inside Dynamics CRM.</p>
<h4 id="thesolutionapproach">The solution approach</h4>
<p>Communicating with IDOL OnDemand from Dynamics CRM obviously requires custom code, but whether that code is called from a plug-in or workflow doesn't materially affect the code. For the purpose of this example, I'll be showing an approach that uses a custom workflow activity called from an asynchronous workflow. When an email is received, the workflow will pass the text of the email to the custom workflow activity, which will then call out to IDOL OnDemand's sentiment analysis API to retrieve the sentiment and score. The workflow will then write these values to custom fields on the email record.</p>
<h4 id="thecode">The code</h4>
<p>The custom workflow activity needs one input parameter for email text and two output parameters for sentiment and score. Here's how we define them:<pre><code>[Input(&quot;Text Input&quot;)]<br>
public InArgument&lt;String&gt; TextInput { get; set; }<br>
[Output(&quot;Sentiment&quot;)]<br>
public OutArgument&lt;String&gt; Sentiment { get; set; }<br>
[Output(&quot;Score&quot;)]<br>
public OutArgument&lt;Decimal&gt; Score { get; set; }</code></pre></p>
<p>We also need to set up two variables to help us call IDOL OnDemand. The first is the URL for the sentiment analysis API:<pre><code>//address of the service to which you will post your json messages<br>
private string _webAddress = &quot;<a href="https://api.idolondemand.com/1/api/sync/analyzesentiment/v1">https://api.idolondemand.com/1/api/sync/analyzesentiment/v1</a>&quot;;</code></pre></p>
<p>The second is the API key to authorize our requests:<pre><code>//idol ondemand api key<br>
private string _apiKey = &quot;XXXXXXXXXXXXXXXX&quot;;</code></pre></p>
<p>Now that all the inputs and configuration parameters are set, you are ready to process the inbound email. We're going to do the following:<ol><li>Check whether the email description field contains text.</li><li>If it does, strip HTML tags using a helper function.</li><li>Pass the &quot;clean&quot; text to IDOL OnDemand with an HttpWebRequest.</li><li>Deserialize the JSON respsonse returned by IDOL OnDemand to a custom class object (SentimentResponse) using a DataContractJsonSerializer.</li><li>Return the sentiment value and score to the calling workflow.</li></ol></p>
<p>Here's the code that does all of this:<pre><code>//get the text we want to analyze<br>
string inputText = TextInput.Get(executionContext);<br>
if (inputText != string.Empty)<br>
{<br>
//use a helper function to strip out HTML tags<br>
inputText = HtmlTools.StripHTML(inputText);<br>
//create the webrequest object and execute it (and post jsonmsg to it)<br>
HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(_webAddress);<br>
//set request content type so it is treated as a regular form post<br>
req.ContentType = &quot;application/x-www-form-urlencoded&quot;;<br>
//set method to post<br>
req.Method = &quot;POST&quot;;<br>
//create a stringbuilder to build our request<br>
StringBuilder postData = new StringBuilder();<br>
//set the apikey request value<br>
postData.Append(&quot;apikey=&quot; + System.Uri.EscapeDataString(_apiKey) + &quot;&amp;&quot;);<br>
//set the text request value<br>
postData.Append(&quot;text=&quot; + System.Uri.EscapeDataString(inputText));<br>
//create a stream<br>
byte[] bytes = System.Text.Encoding.ASCII.GetBytes(postData.ToString());<br>
req.ContentLength = bytes.Length;<br>
System.IO.Stream os = req.GetRequestStream();<br>
os.Write(bytes, 0, bytes.Length);<br>
os.Close();<br>
//get the response<br>
System.Net.WebResponse resp = req.GetResponse();<br>
//deserialize the response to a SentimentResponse object<br>
SentimentResponse myResponse = new SentimentResponse();<br>
System.Runtime.Serialization.Json.DataContractJsonSerializer deserializer = new System.Runtime.Serialization.Json.DataContractJsonSerializer(myResponse.GetType());<br>
myResponse = deserializer.ReadObject(resp.GetResponseStream()) as SentimentResponse;<br>
//set output values from the fields of the deserialzed myjsonresponse object<br>
Score.Set(executionContext, myResponse.Aggregate.Score);<br>
Sentiment.Set(executionContext, myResponse.Aggregate.Sentiment);<br>
}</code></pre></p>
<p>The StripHTML function is taken from a <a target="_blank" href="http://www.codeproject.com/Articles/11902/Convert-HTML-to-Plain-Text" rel="nofollow">sample on CodeProject</a>.</p>
<p>Here are the classes used for deserializing the JSON response:<pre><code>//DataContract decoration necessary for serialization/deserialization to work properly<br>
[DataContract]<br>
public class SentimentResponse<br>
{<br>
//datamember name value indicates name of json field to which data will be serialized/from which data will be deserialized<br>
[DataMember(Name = &quot;positive&quot;)]<br>
public List&lt;SentimentEntity&gt; Positive { get; set; }<br>
[DataMember(Name = &quot;negative&quot;)]<br>
public List&lt;SentimentEntity&gt; Negative { get; set; }<br>
[DataMember(Name = &quot;aggregate&quot;)]<br>
public SentimentAggregate Aggregate { get; set; }<br>
public SentimentResponse() { }<br>
}<br>
//DataContract decoration necessary for serialization/deserialization to work properly<br>
[DataContract]<br>
public class SentimentAggregate<br>
{<br>
[DataMember(Name = &quot;sentiment&quot;)]<br>
public string Sentiment { get; set; }<br>
[DataMember(Name = &quot;score&quot;)]<br>
public decimal Score { get; set; }<br>
}<br>
//DataContract decoration necessary for serialization/deserialization to work properly<br>
[DataContract]<br>
public class SentimentEntity<br>
{<br>
[DataMember(Name = &quot;sentiment&quot;)]<br>
public string Sentiment { get; set; }<br>
[DataMember(Name = &quot;topic&quot;)]<br>
public string Topic { get; set; }<br>
[DataMember(Name = &quot;score&quot;)]<br>
public decimal Score { get; set; }<br>
[DataMember(Name = &quot;original_text&quot;)]<br>
public string OriginalText { get; set; }<br>
[DataMember(Name = &quot;normalized_text&quot;)]<br>
public string NormalizedText { get; set; }<br>
[DataMember(Name = &quot;original_length&quot;)]<br>
public int OriginalLength { get; set; }<br>
[DataMember(Name = &quot;normalized_length&quot;)]<br>
public int NormalizedLength { get; set; }<br>
}</code></pre></p>
<p>Although the scenario for this example mentions email, you'll note that nothing in the code is specific to emails, so you can use this same code without modifications to perform sentiment analysis on any record in your Dynamics CRM system. Full code for this is in my <a href="https://github.com/lucasalexander/CRM-IdolOnDemand-Tools">CRM-IdolOnDemand-Tools repository</a> on GitHub.</p>
<p>That's all for now. Join me next time to see how you can index Dynamics CRM records and execute &quot;find similar&quot; queries using IDOL OnDemand.</p>
<p><em>* Actually the sentiment analysis API also returns separate sentiment and score values for different phrases in the text, but we're going to disregard those here and only work with the overall sentiment and score values.</em></p>
<p><em>A version of this post was originally published on the HP Enterprise Services Application Services blog.</em></p>
</div>]]></content:encoded></item><item><title><![CDATA[Using IDOL OnDemand for text analysis in Dynamics CRM - part 1]]></title><description><![CDATA[<div class="kg-card-markdown"><p>Inside any organization's Microsoft Dynamics CRM system, there is a wealth of raw data that can be turned into actionable intelligence if it can be effectively analyzed. This data can be found in emails, case notes, attachments and other records that are created and stored in the course of day-to-day</p></div>]]></description><link>https://alexanderdevelopment.net/post/2014/09/18/using-idol-ondemand-for-text-analysis-in-dynamics-crm-part-1/</link><guid isPermaLink="false">5a5837226636a30001b97735</guid><category><![CDATA[Microsoft Dynamics CRM]]></category><category><![CDATA[CRM 2013]]></category><category><![CDATA[IDOL OnDemand]]></category><category><![CDATA[text analysis]]></category><dc:creator><![CDATA[Lucas Alexander]]></dc:creator><pubDate>Fri, 19 Sep 2014 00:00:00 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><p>Inside any organization's Microsoft Dynamics CRM system, there is a wealth of raw data that can be turned into actionable intelligence if it can be effectively analyzed. This data can be found in emails, case notes, attachments and other records that are created and stored in the course of day-to-day business. In this three-part series, I will show how you can unlock this data using HP IDOL OnDemand's powerful text analysis capabilities. Specifically I will show how you can perform sentiment analysis on incoming emails and also how you can index Dynamics CRM records so that agents can easily see similar, relevant records. Before we get into the technical details, let's take a closer look at IDOL OnDemand.</p>
<h4 id="whatishpidolondemand">What is HP IDOL OnDemand?</h4>
<p><a target="_blank" href="https://www.idolondemand.com/" rel="nofollow">IDOL OnDemand</a> is a collection of web service APIs that expose a subset of the functionality offered by HP Autonomy. These APIs allow developers to write code that can &quot;understand and engage with human information, including text, images, social media, and app data.&quot; Currently IDOL OnDemand offers more than 20 different APIs including sentiment analysis, image recognition, face detection and text indexing/searching. A full list of IDOL OnDemand APIs is available here: <a target="_blank" href="https://www.idolondemand.com/developer/apis" rel="nofollow">https://www.idolondemand.com/developer/apis</a>. Despite the breadth of functionality available in IDOL OnDemand, there are definite differences between it and the on-premise HP IDOL version. You can read more about those differences <a target="_blank" href="http://www.autonomy.com/technology/idol-on-demand" rel="nofollow">here</a>.</p>
<h4 id="howdoweconnectdynamicscrmandidolondemand">How do we connect Dynamics CRM and IDOL OnDemand?</h4>
<p>Because all IDOL OnDemand APIs are RESTful web services that return responses as JSON-formatted messages, custom workflow activities or plug-ins can communicate with IDOL OnDemand via the System.Net.HttpWebRequest class, but first you need to sign up for an IDOL OnDemand developer account at <a target="_blank" href="https://www.idolondemand.com/signup.html" rel="nofollow">https://www.idolondemand.com/signup.html</a>. Once you sign up for a developer account, you'll be given an API key that grants access to run IDOL OnDemand APIs. I'll be showing detailed code examples in parts two and three of this series, but in the meantime I suggest you go ahead and take a look at the IDOL OnDemand <a target="_blank" href="https://www.idolondemand.com/developer/docs/HowTo_GettingStarted.html" rel="nofollow">getting started documentation</a>.</p>
<p>That's all for today. In my next post, I'll show how to analyze inbound emails for customer sentiment. See you then!</p>
<p><em>A version of this post was originally published on the HP Enterprise Services Application Services blog.</em></p>
</div>]]></content:encoded></item><item><title><![CDATA[Remote Microsoft Dynamics CRM 2013 server administration with PowerShell]]></title><description><![CDATA[<div class="kg-card-markdown"><p>When Microsoft Dynamics CRM 2011 was released, the on-premise installation included a set of PowerShell cmdlets for administering configuration and deployments. While these were incredibly powerful, the out-of-the-box cmdlets only worked on the same server that was running CRM. In CRM 2013, these cmdlets have been updated to run from</p></div>]]></description><link>https://alexanderdevelopment.net/post/2014/01/30/remote-microsoft-dynamics-crm-2013-server-administration-with-powershell-2/</link><guid isPermaLink="false">5a5837236636a30001b97790</guid><category><![CDATA[Microsoft Dynamics CRM]]></category><category><![CDATA[CRM 2013]]></category><dc:creator><![CDATA[Lucas Alexander]]></dc:creator><pubDate>Thu, 30 Jan 2014 18:00:00 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><p>When Microsoft Dynamics CRM 2011 was released, the on-premise installation included a set of PowerShell cmdlets for administering configuration and deployments. While these were incredibly powerful, the out-of-the-box cmdlets only worked on the same server that was running CRM. In CRM 2013, these cmdlets have been updated to run from remote systems, so you can use them to administer your on-premise CRM 2013 deployments without needing to be logged on to the actual application server. In this post I will show you how to set up your local system to run the CRM 2013 PowerShell cmdlets and then walk you through an example scenario.</p>
<h4 id="settingupyoursystem">Setting up your system</h4>
<p>First, you need to get files that contain the Microsoft.Crm.PowerShell cmdlets. If you have access to the CRM 2013 install media, you can find them in the &quot;\server\amd64\PFiles\MSCRM\Tools&quot; directory. If don't have the install media handy, you can find them in the &quot;\Program Files\Microsoft Dynamics CRM\tools&quot; directory on your CRM application server. You'll need to copy three files to your local system. They are:<ol><li>Microsoft.Crm.PowerShell.dll</li></ol></p>
<li>Microsoft.Crm.PowerShell.dll-Help.xml
</li><li>Microsoft.Crm.PowerShell.Strings.dll

<p>Once you copy these files to your system, make sure to unblock them by right-clicking on each file, then selecting properties-&gt;unblock. If you don't unblock them, you will not be able to load the cmdlets in a PowerShell console.</p>
<p>After you've unblocked the files, open up a PowerShell console, navigate to the directory where you downloaded the Microsoft.Crm.PowerShell files and import the cmdlets to your session by executing the following: <em>Import-Module .\Microsoft.Crm.PowerShell.dll</em>.</p>
<p>(Alternatively, you can register the Microsoft.Crm.PowerShell cmdlets on your local system with the Installutil.exe installer tool if you have Visual Studio or the Windows SDK installed. The syntax for that command is <em>[directory-containing-installutil.exe]\installutil.exe [&quot;directory-containing-cmdlets&quot;]\Microsoft.Crm.PowerShell.dll</em>. Then instead of using the Import-Module command to load the cmdlets, you will execute <em>Add-PSSnapin &quot;Microsoft.Crm.PowerShell&quot;</em> to load them in your PowerShell console.)</p>
<p>Running <em>Get-Command -module &quot;Microsoft.Crm.PowerShell&quot;</em> will list all the CRM-related cmdlets that are now available.</p>
<h4 id="arealworldscenario">A real-world scenario</h4>
<p>Now that you have the Dynamics CRM cmdlets loaded, let's actually do something useful. A few weeks ago, <a href="https://alexanderdevelopment.net/post/2014/01/09/managing-microsoft-dynamics-crm-2013-access-team-membership-using-connections-2/">I discussed the new Dynamics CRM 2013 access teams feature in a post on this blog</a>. One default limitation on access teams is that CRM will only allow you to set up two auto-created access team templates per entity. Fortunately, this value can be changed with just a few lines of PowerShell scripting. In the following example, I will show you how to change the value from two to five.</p>
<h4 id="settinguptheserverconnection">Setting up the server connection</h4>
<p>When using the CRM PowerShell cmdlets directly on the CRM application server, PowerShell uses the credentials of the logged-in user and assumes you want to use the local CRM deployment web service (DWS) by default. When working from a remote machine, however, PowerShell needs you to specify those values every time you execute a CRM cmdlet. While you could input the user credentials and DWS URL for every operation, I find it's easier to set variables at the beginning of my session and then reuse them. To populate a credential variable, run the following command: <em>$mycred = Get-Credential</em></p>
<p>This will open a prompt for you to supply your username and password. In my case I have a user named &quot;lucas&quot; who is a member of a domain called &quot;lucas.&quot; After inputting your details and hitting &quot;OK,&quot; you'll now be able to reuse the $mycred variable for the rest of the session.</p>
<p>Setting the DWS URL is even easier because it's just a simple string variable assignment:<em>$dwsurl = &quot;<a href="https://URL-TO-YOUR-SERVER-HERE">https://URL-TO-YOUR-SERVER-HERE</a>&quot;</em></p>
<h4 id="updatingthecrmserver">Updating the CRM server</h4>
<p>After those variables are set up, now we can update the CRM configuration. The following PowerShell script will do the following:</p>
<ol><li>Load a local $setting object with the CRM server's "TeamSettings" values.
</li><li>Print the contents of the $setting object value to the local console window.
</li><li>Update the MaxAutoCreatedAccessTeamsPerEntity attribute of the local $setting object to 5.
</li><li>Update the server TeamSettings values from the local $setting object.
</li><li>Print the CRM server's TeamSettings values to the local console window to make sure the change was successful.
</li></ol>
<pre>&#35;1. Load a local $setting object with the CRM server's "TeamSettings" values.
$setting = Get-Crmsetting -settingtype teamsettings -credential $mycred -dwsserverurl $dwsurl
&#35;2. Print the contents of the $setting object value to the local console window.
$setting
&#35;3. Update the MaxAutoCreatedAccessTeamsPerEntity attribute of the local $setting object to 5.
$setting.MaxAutoCreatedAccessTeamsPerEntity = 5
&#35;4. Update the server TeamSettings values from the local $setting object.
Set-Crmsetting -Setting $setting -credential $mycred -dwsserverurl $dwsurl
&#35;5. Print the CRM server's TeamSettings values to the local console window to make sure the change was successful.
Get-Crmsetting -settingtype teamsettings -credential $mycred -dwsserverurl $dwsurl</pre>
<p>That’s all there is to it. The new CRM 2013 PowerShell cmdlets make it easier than ever before to administer your on-premise CRM deployments. Have you tried PowerShell for Dynamics CRM administration? If so, what did you think? Let us know in the comments!</p>
<p><em>A version of this post was originally published on the HP Enterprise Services Application Services blog.</em></p>
</li></div>]]></content:encoded></item><item><title><![CDATA[Working with key-value pair data inside Microsoft Dynamics CRM workflows – part 2]]></title><description><![CDATA[<div class="kg-card-markdown"><p>In my <a href="https://alexanderdevelopment.net/post/2014/01/14/working-with-key-value-pair-data-inside-microsoft-dynamics-crm-workflows/">last post</a> I discussed how key-value pair data can be used to store configuration-related items in Microsoft Dynamics CRM, and I also showed a design for an entity to store key-value pair data. In today's post I will show how to retrieve and consume the data inside a</p></div>]]></description><link>https://alexanderdevelopment.net/post/2014/01/16/working-with-key-value-pair-data-inside-microsoft-dynamics-crm-workflows-part-2/</link><guid isPermaLink="false">5a5837226636a30001b97731</guid><category><![CDATA[Microsoft Dynamics CRM]]></category><category><![CDATA[process automation]]></category><category><![CDATA[CRM 2013]]></category><dc:creator><![CDATA[Lucas Alexander]]></dc:creator><pubDate>Thu, 16 Jan 2014 18:00:00 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><p>In my <a href="https://alexanderdevelopment.net/post/2014/01/14/working-with-key-value-pair-data-inside-microsoft-dynamics-crm-workflows/">last post</a> I discussed how key-value pair data can be used to store configuration-related items in Microsoft Dynamics CRM, and I also showed a design for an entity to store key-value pair data. In today's post I will show how to retrieve and consume the data inside a workflow using a custom workflow activity.</p>
<h4 id="thekeyvaluepairentity">The Key-Value Pair entity</h4>
<p>As I mentioned previously, I have a custom entity called Key-Value Pair (new_keyvaluepair) that stores the key in the name field (new_name), and it has the following custom fields to store value data:</p>
<ol>
<li>String value (new_stringvalue)
</li><li>Decimal value (new_decimalvalue)
</li><li>Integer value (new_integervalue)
</li><li>Date value (new_datevalue)
</li></ol>
<h4 id="thecustomworkflowactivity">The custom workflow activity</h4>
<p>I’ve created a custom workflow called GetKeyValue that takes the key name as an input parameter, and it returns all four value fields as output parameters. I would prefer to not have to return all the value fields, but because Dynamics CRM doesn't have a way to consume an output parameter with an object data type, that won't work.</p>
<p>Here are how the parameters are set up in code:<pre><code>[Input(&quot;Key name&quot;)]<br>
public InArgument&lt;string&gt; KeyName { get; set; }<br>
[Output(&quot;String value&quot;)]<br>
public OutArgument&lt;string&gt; StringValue { get; set; }<br>
[Output(&quot;Integer value&quot;)]<br>
public OutArgument&lt;Int32&gt; IntegerValue { get; set; }<br>
[Output(&quot;Decimal value&quot;)]<br>
public OutArgument&lt;decimal&gt; DecimalValue { get; set; }<br>
[Output(&quot;Date value&quot;)]<br>
public OutArgument&lt;DateTime&gt; DateValue { get; set; }</code></pre></p>
<p>Because we're just doing a simple lookup on the entity name, I've chosen to use a CRM QueryExpression, but you could just as easily use FetchXML or Linq instead if you'd prefer. Here's the code that retrieves the key-value record:</p>
<pre><code>try
{
	string keyName = KeyName.Get(executionContext);
	//look up team template by name
	QueryByAttribute querybyexpression = new QueryByAttribute("new\_keyvaluepair");
	querybyexpression.ColumnSet = new ColumnSet("new\_name",
		"new\_stringvalue",
		"new\_decimalvalue",
		"new\_integervalue",
		"new\_datevalue",
		"new\_valuetype"
		);
querybyexpression.Attributes.AddRange("new\_name");
	querybyexpression.Values.AddRange(keyName);
	EntityCollection retrieved = service.RetrieveMultiple(querybyexpression);
	//if we find something, we're set
	if (retrieved.Entities.Count &gt; 0)
	{
		Entity kvp = retrieved.Entities[0];
		StringValue.Set(executionContext, (string)kvp["new\_stringvalue"]);
		IntegerValue.Set(executionContext, (int)kvp["new\_integervalue"]);
		DecimalValue.Set(executionContext, (decimal)kvp["new\_decimalvalue"]);
		DateValue.Set(executionContext, (DateTime)kvp["new\_datevalue"]);
	}
	else
	{
		//throw exception if unable to find a matching template
		throw new Exception("could not find key-value pair for: " + keyName);
	}
}</code></pre>
<p>Although this example is focused on use in a workflow, you could use this custom workflow activity in a Dynamics CRM action process, or you could use the same business logic inside a plug-in. Full code for this post is available <a href="https://github.com/lucasalexander/Crm-Sample-Code/tree/master/CrmKeyValueManager">here</a>.</p>
<p>How do you manage and consume data like this in your Dynamics CRM deployments? Does this approach look like it would be something worth using? Please share your thoughts in the comments!</p>
<p><em>A version of this post was originally published on the HP Enterprise Services Application Services blog.</em></p>
</div>]]></content:encoded></item><item><title><![CDATA[Working with key-value pair data inside Microsoft Dynamics CRM workflows]]></title><description><![CDATA[<div class="kg-card-markdown"><p>Dynamics CRM workflows are a great way to enable business processes, and with the real-time capabilities introduced in CRM 2013 they can replace plug-ins in many scenarios. One significant drawback that workflows do have, though, is they lack the ability to easily retrieve and work with data from inside Dynamics</p></div>]]></description><link>https://alexanderdevelopment.net/post/2014/01/14/working-with-key-value-pair-data-inside-microsoft-dynamics-crm-workflows/</link><guid isPermaLink="false">5a5837226636a30001b9772d</guid><category><![CDATA[Microsoft Dynamics CRM]]></category><category><![CDATA[process automation]]></category><category><![CDATA[CRM 2013]]></category><dc:creator><![CDATA[Lucas Alexander]]></dc:creator><pubDate>Tue, 14 Jan 2014 18:00:00 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><p>Dynamics CRM workflows are a great way to enable business processes, and with the real-time capabilities introduced in CRM 2013 they can replace plug-ins in many scenarios. One significant drawback that workflows do have, though, is they lack the ability to easily retrieve and work with data from inside Dynamics CRM that is not related to their primary entities.</p>
<p>For example, let's say I have a workflow that creates a task if a contact's credit limit is greater than $5,000. In a completely custom software application, I would store this threshold value in some sort of configuration data store from which it could be retrieved at runtime, but Dynamics CRM doesn’t support that pattern out of the box. This requires that the $5,000 value be hardcoded in the workflow. If the credit limit threshold is reduced next month to $4,000, the workflow will need to be deactivated, then the value will need to be changed, and the workflow will need to be reactivated. Potentially more troubling, if this credit limit threshold value is used in multiple steps (or even multiple workflows), it's possible for it not to be updated correctly in all of them.</p>
<p>In this two-part series I will show how to create a solution for managing and consuming configuration data that uses a key-value pair data store in Dynamics CRM. In addition to solving workflow-specific issues, this basic approach could easily be leveraged in plug-ins, actions and other custom CRM-interfacing code.</p>
<h4 id="gettingstarted">Getting started</h4>
<p>First, what exactly is a key-value pair? From the Wikipedia <a target="_blank" href="http://en.wikipedia.org/wiki/Attribute%E2%80%93value_pair" rel="nofollow">&quot;attribute-value pair&quot; entry</a>:</p>
<p><em>A name–value pair, key–value pair, field–value pair or attribute–value pair is a fundamental data representation in computing systems and applications. Designers often desire an open-ended data structure that allows for future extension without modifying existing code or data. In such situations, all or part of the data model may be expressed as a collection of tuples &lt;attribute name, value&gt;; each element is an attribute–value pair.</em></p>
<h4 id="storingthedata">Storing the data</h4>
<p>To store key-value pair data in Dynamics CRM, all we need is a custom entity that stores the key in its name field and the value data in a value field. Because CRM doesn't have an &quot;object&quot; data type, we need to create a different value field for every possible data type we want to store. In this example, I have a custom entity called Key-Value Pair (new_keyvaluepair) that has the following custom fields to hold value data:</p>
<ol>
<li>String value (new_stringvalue)
</li><li>Decimal value (new_decimalvalue)
</li><li>Integer value (new_integervalue)
</li><li>Date value (new_datevalue)
</li></ol>
<p>Additionally, I have an optionset field called &quot;Value type&quot; (new_valuetype) that indicates what type of value the key represents. When the value type is selected, CRM business rules set the corresponding value field on the form to business required and the other fields to not required.</p>
<p>If I had selected a decimal type instead of a string, the decimal value field would be required instead of the string value field.</p>
<p>That's all for now. In my next post I will show how retrieve and consume the data inside a workflow using a custom workflow activity. See you then!</p>
<p><em>A version of this post was originally published on the HP Enterprise Services Application Services blog.</em></p>
</div>]]></content:encoded></item><item><title><![CDATA[Managing Microsoft Dynamics CRM 2013 access team membership using connections]]></title><description><![CDATA[<div class="kg-card-markdown"><p>Dynamics CRM 2013 includes a great new feature called access teams, which makes ad-hoc sharing of records much easier than in previous versions. The basic idea is that an administrator can create one or more team templates for an entity that function sort of like security roles, but for a</p></div>]]></description><link>https://alexanderdevelopment.net/post/2014/01/09/managing-microsoft-dynamics-crm-2013-access-team-membership-using-connections-2/</link><guid isPermaLink="false">5a5837236636a30001b9778c</guid><category><![CDATA[Microsoft Dynamics CRM]]></category><category><![CDATA[CRM 2013]]></category><category><![CDATA[C#]]></category><dc:creator><![CDATA[Lucas Alexander]]></dc:creator><pubDate>Thu, 09 Jan 2014 18:00:00 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><p>Dynamics CRM 2013 includes a great new feature called access teams, which makes ad-hoc sharing of records much easier than in previous versions. The basic idea is that an administrator can create one or more team templates for an entity that function sort of like security roles, but for a specific record. When a user is assigned rights on a record via a team template, if a corresponding access team doesn't already exist, CRM will create one on the fly.</p>
<p>While access teams are a great feature from a security perspective, they don't have the same flexibility as connections for representing different relationships between particular records and users. For example, if you have complex sales opportunities that require multiple users to work together on opportunity-specific sales teams in different roles (team lead, proposal writer, researcher, for example), creating specific connection roles and then creating connections between opportunities and users is the best way to represent those relationships in Dynamics CRM.</p>
<p>Unfortunately, if you use both connections and access teams, the out-of-the-box Dynamics CRM interface will require that each be managed separately. In this post, I will show how you can tie connections and access team membership together using workflows and custom workflow activities so that access team membership is granted and revoked when connections are created and deleted.</p>
<h4 id="theapproach">The approach</h4>
<p>In this example, I have a custom entity called &quot;Important Something&quot; (new_importantsomething). I have created two team templates called &quot;Important Something - Manager&quot; and &quot;Important Something - Member.&quot; I have also created two connection roles for Important Somethings and Users. These roles are also called &quot;Important Something - Manager&quot; and &quot;Important Something - Member.&quot; We will be creating one real-time workflow that executes when a connection record is created and another real-time workflow that executes when a connection record is deleted. These workflows will check whether the &quot;connected to&quot; role is one of the new &quot;important something&quot; roles, and then they will pass the connection details and the role name to a custom workflow activity to handle the user's addition to or removal from the specific access team. Although a single custom workflow activity could be created to do either the addition or removal based on an input parameter flag, in this example we'll be using two separate ones called AssignUserToAccessTeamByName and RemoveUserFromAccessTeamByName.</p>
<h4 id="thecustomworkflowactivities">The custom workflow activities</h4>
<p>To add a user to an access team, the AssignUserToAccessTeamByName custom workflow activity will need to execute an AddUserToRecordTeamRequest via the organization web service. This message requires three input parameters:</p>
<ol>
<li>The user id
</li><li>The entity record id
</li><li>The team template
</li></ol>
The user id and the entity record id are both stored on the connection record, so the AssignUserToAccessTeamByName activity can get those via an EntityReference input parameter for the connection. The team template can be specified either as an EntityReference or as a string name value to look up the id in the activity method. I prefer the string method because I think it offers more flexibility for workflow design, but there is of course a slight performance hit from having to look up the template id by its name.
<p>Here are the input parameters for the custom workflow activity:</p>
<pre><code>[Input("Connection")]
[ReferenceTarget("connection")]
public InArgument&lt;EntityReference&gt; Connection { get; set; }
[Input("Team Template name")]
public InArgument&lt;string&gt; TeamTemplateName { get; set; }</code></pre>
<p>Here's the code that executes the business logic:</p>
<pre><code>Guid connectionId = Connection.Get(executionContext).Id;
Entity connection = service.Retrieve("connection", connectionId, new ColumnSet("record1id", "record2id"));
EntityReference connectedFromId = (EntityReference)connection["record1id"];
EntityReference connectedToId = (EntityReference)connection["record2id"];
//only run this if the connection is to a user record
if (connectedToId.LogicalName.ToUpper() == "SYSTEMUSER")
{
	Guid teamTemplateId;
	string teamTemplateName = TeamTemplateName.Get(executionContext);
	//look up team template by name
	QueryByAttribute querybyexpression = new QueryByAttribute("teamtemplate");
	querybyexpression.ColumnSet = new ColumnSet("teamtemplatename", "teamtemplateid");
	querybyexpression.Attributes.AddRange("teamtemplatename");
	querybyexpression.Values.AddRange(teamTemplateName);
	EntityCollection retrieved = service.RetrieveMultiple(querybyexpression);
	//if we find something, we're set
	if (retrieved.Entities.Count &gt; 0)
	{
		teamTemplateId = retrieved.Entities[0].Id;
	}
	else
	{
		//throw exception if unable to find a matching template
		throw new Exception("could not find team template named: " + teamTemplateName);
	}
	AddUserToRecordTeamRequest teamAddRequest = new AddUserToRecordTeamRequest();
	teamAddRequest.Record = connectedFromId;
	teamAddRequest.SystemUserId = connectedToId.Id;
	teamAddRequest.TeamTemplateId = teamTemplateId;
	AddUserToRecordTeamResponse response = (AddUserToRecordTeamResponse)service.Execute(teamAddRequest);
}</code></pre>
<p>One thing to note is that a connection record in CRM is actually two separate records (the to/from records are reversed in each), so the workflow will actually fire twice. There's no real problem with the workflow firing twice, but just to reduce unnecessary processing, you'll note that there's a check to make sure the custom workflow activity business logic only executes when a user is the &quot;to&quot; record.</p>
<p>The RemoveUserFromAccessTeamByName custom workflow activity is almost identical to the AssignUserToAccessTeamByName activity, except it executes a RemoveUserFromRecordTeamRequest instead of an AddUserToRecordTeamRequest. The parameters for each method are the same. Full code for each method is available <a href="https://github.com/lucasalexander/Crm-Sample-Code/tree/master/CrmTeamConnection">here</a>.</p>
<h4 id="theworkflows">The workflows</h4>
<p>As mentioned earlier, the workflows for adding and removing access team members first check to see if the created or deleted connection has a particular connection role, and then they executes their corresponding custom workflow activity. Here's a screenshot of the &quot;Add access team member from connection&quot; workflow:<br>
<img src="https://alexanderdevelopment.net/content/images/2015/10/workflow01.png#img-thumbnail" alt="workflow definition"></p>
<p>And here's a screenshot of the custom step input properties for the AssignUserToAccessTeamByName activity:<br>
<img src="https://alexanderdevelopment.net/content/images/2015/10/workflow02.png#img-thumbnail" alt="input properties"></p>
<p>And that's it. Now you can manage access team membership and connections in a way to best represent relationships and provide the right level of data access to your users. Are you using access teams yet? If not, do you plan to? Let us know in the comments.</p>
<p><em>A version of this post was originally published on the HP Enterprise Services Application Services blog.</em></p>
</div>]]></content:encoded></item><item><title><![CDATA[Microsoft Dynamics CRM 2013 "actions" - a solution in search of a problem? Part 2]]></title><description><![CDATA[<div class="kg-card-markdown"><p>In my <a href="https://alexanderdevelopment.net/post/2014/01/02/microsoft-dynamics-crm-2013-actions-a-solution-in-search-of-a-problem-part-1/">last post</a>, I talked about what Dynamics CRM 2013 actions are and how I think they're of limited utility in enterprise deployments. I did suggest two scenarios in which I think actions could be useful, and I'm sure other folks may have come up with some ideas of</p></div>]]></description><link>https://alexanderdevelopment.net/post/2014/01/06/microsoft-dynamics-crm-2013-actions-a-solution-in-search-of-a-problem-part-2/</link><guid isPermaLink="false">5a5837226636a30001b9771c</guid><category><![CDATA[Microsoft Dynamics CRM]]></category><category><![CDATA[CRM 2013]]></category><dc:creator><![CDATA[Lucas Alexander]]></dc:creator><pubDate>Tue, 07 Jan 2014 00:00:00 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><p>In my <a href="https://alexanderdevelopment.net/post/2014/01/02/microsoft-dynamics-crm-2013-actions-a-solution-in-search-of-a-problem-part-1/">last post</a>, I talked about what Dynamics CRM 2013 actions are and how I think they're of limited utility in enterprise deployments. I did suggest two scenarios in which I think actions could be useful, and I'm sure other folks may have come up with some ideas of their own. In this post I want to outline some technical considerations to keep in mind when using actions.</p>
<h4 id="security">Security</h4>
<p>First and foremost is security. An action executes in the context of the calling user, so the calling user has to have permission to execute each step in an action. Let's say an action creates a custom entity. Any user can call the action's custom message via the CRM organization web service, but if the user is not a member of a security role with create rights for that entity, an error will be returned. On one hand, this is useful because it keeps administrators from having to worry about who can execute actions. On the other hand, this is a limitation when actions are compared to workflows that can run in the security context of the workflow owner.</p>
<h4 id="testability">Testability</h4>
<p>As those of you who read my posts on <a href="https://alexanderdevelopment.net/tag/unit-testing/">unit testing</a> already know, automated unit testing is a subject near and dear to my heart. Automated unit testing of plug-ins and custom workflow activities is relatively easy. If business logic is moved from those to actions, it becomes much more difficult to test. In fact, I believe the only way to test actions using Visual Studio's unit testing or NUnit would be to export the XAML definition for an action and then set up a test similar to what I suggested for <a href="https://alexanderdevelopment.net/post/2013/10/17/unit-testing-custom-microsoft-dynamics-crm-code-part-5">custom workflow activities</a>.</p>
<h4 id="interfaceinteroperabilitydesign">Interface/interoperability design</h4>
<p>Generally speaking, I avoid designing custom interfaces in which an external system interacts directly with the Dynamics CRM organization web service. I find that using a custom intermediate &quot;proxy&quot; service is usually a better approach for several reasons:</p>
<ol>
<li>A custom proxy service allows for a more loosely coupled solution than when calling the organization web service directly.
</li><li>A custom proxy service can execute complex business logic in the interface layer.
</li><li>A custom proxy service supports a variety of authentication options.
</li></ol>
<p>Using actions, however, encourages the design of interfaces that rely directly on the organization web service. Also if actions are used for interfaces with external systems, the CRM administrator must consider the impact of potential downtime for editing actions. Like with standard workflows, actions must be deactivated before they can be edited. If custom code attempts to execute a deactivated action, an error message will be returned.</p>
<h4 id="wrappingitup">Wrapping it up</h4>
<p>So, after thinking about how they can be used and technical considerations to keep in mind when using them, my initial feeling that actions aren't a great tool for enterprise Dynamics CRM 2013 implementations stands. Agree/disagree? Please share your thoughts in the comments.</p>
<p><em>A version of this post was originally published on the HP Enterprise Services Application Services blog.</em></p>
</div>]]></content:encoded></item><item><title><![CDATA[Microsoft Dynamics CRM 2013 "actions" - a solution in search of a problem? Part 1]]></title><description><![CDATA[<div class="kg-card-markdown"><p>I finally set up a personal Dynamics CRM 2013 sandbox a couple of weeks ago so I could start trying out some of its new features. Although I had been waiting for most of the Fall to get a look at real-time workflows, I was also intrigued by the new</p></div>]]></description><link>https://alexanderdevelopment.net/post/2014/01/02/microsoft-dynamics-crm-2013-actions-a-solution-in-search-of-a-problem-part-1/</link><guid isPermaLink="false">5a5837236636a30001b977cf</guid><category><![CDATA[CRM 2013]]></category><category><![CDATA[Microsoft Dynamics CRM]]></category><dc:creator><![CDATA[Lucas Alexander]]></dc:creator><pubDate>Thu, 02 Jan 2014 18:00:00 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><p>I finally set up a personal Dynamics CRM 2013 sandbox a couple of weeks ago so I could start trying out some of its new features. Although I had been waiting for most of the Fall to get a look at real-time workflows, I was also intrigued by the new action process. After a few days of experimenting with actions, my early impression is that they're not terribly useful in enterprise Dynamics CRM implementations. In this post, I'll explain what exactly CRM actions are and how I think they might be useful. In my next post, I'll outline some technical considerations to keep in mind when using them.</p>
<h4 id="whatisadynamicscrmaction">What is a Dynamics CRM &quot;action&quot;?</h4>
<p>According to the documentation included in the Dynamics CRM 2013 SDK:<br>
<em>You can extend the functionality of Microsoft Dynamics CRM by creating custom messages known as &quot;actions.&quot; These actions will have associated request/response classes. Presently, actions are available to business applications and extensions through organization web service calls only. Actions are typically used to add new domain specific functionality to the organization web service or to combine multiple organization web service message requests into a single request. For example, in a support call center, you may want to combine the Create, Assign, and Setstate messages into a single new &quot;Escalate&quot; message.</em></p>
<p>Basically this means that an action is a real-time workflow that you can call via the CRM organization web service (and only via the CRM organization web service). Unlike a custom workflow activity, you cannot trigger an action from directly inside a workflow or a dialog. You can, however, write a custom workflow activity to trigger the action.</p>
<p>Although actions are essentially workflows, they do differ from actual workflows in a few ways. First, actions do support input and output parameters. Second, plug-ins can be registered to fire for actions just like with stock CRM messages. Finally, global actions can be created that don't correspond to any particular entity.</p>
<h4 id="whatsthepoint">What's the point?</h4>
<p>Based on everything I've read about actions, it seems the underlying rationale is to allow non-programmers to extend the core CRM platform using a variation on the workflow designer. Because the custom action messages will only ever be called from external integrations or custom code embedded in CRM, this seems to be of limited utility. That is to say, custom code is required to use an action, so if you're to that point, why not go ahead and put your business logic in code instead of trying to force it to live inside the CRM platform?</p>
<h4 id="potentialapplications">Potential applications</h4>
<p>As I mentioned at the beginning of this post, I don't think actions will play a major role in enterprise deployments of Dynamics CRM 2013, but I can think of two potential uses for them:</p>
<ol>
<li>Organizations that don't have programmers with a deep understanding of CRM functionality but do have knowledgeable super users and business analysts who can create actions for the programmers to simply "consume" in their integrating systems could leverage actions to deploy functionality more quickly. The ability for non-programmers to edit actions also allows for business processes to easily evolve over time.
</li><li>An "empty" action could be used to trigger a plug-in in response to a particular custom message.</li></ol>
<p>In my next post, I'll take a look at some technical considerations if you do decide to use actions. In the meantime, what do you think? Have you come up with some good uses for actions? Please let us know in the comments.</p>
<p><em>A version of this post was originally published on the HP Enterprise Services Application Services blog.</em></p>
</div>]]></content:encoded></item></channel></rss>