When writing a plug-in for CRM 2011 you have the ability to use two strings passed to the constructor of your plug-in, the Unsecure Configuration string and the Secure Configuration string. These configuration strings are stored in the message processing step that are registered to your plug-in. You could use these configuration strings to store data that is not to be hard-coded into your plug-in.
The differences between the Unsecure Configuration and the Secure Configuration are:
- The Unsecure Configuration information could be read by any user in CRM whereas the Secure Configuration information could be read only by CRM Administrators.
- When exporting a solution, the Unsecure Configuration information on an activated step that is included in that solution will be exported, whereas the Secure Configuration information will not be included in that solution.[1]
The difference described in (2) is logical: if it would be included in the exported solution, anyone getting their hands on the solution file could extract the Secure Configuration from the solution file. However, when deploying your solution in a DTAP street, this is a pain since you’re missing your Secure Configuration on your plug-in steps after deployment of the solution.
If you do not have a lot of plug-in steps with a Secure Configuration, you could manually update your Secure Configuration using the Plug-in Registration Tool provided in the CRM 2011 SDK. I was working on a project that had 240+ registered steps which all shared a single Secure Configuration. Since we deployed our solution to our Test environment on a daily base, manually updating the Secure Configuration was not a viable option.
Described here is a simple console application that reads a Secure Configuration text from a text file and update the required plug-in steps with this text. You could easily transform this logic into a WPF or Windows Forms application if you need something fancier!
First, create a new Console Application:
Second, add to following references to your project:
- microsoft.xrm.client
- microsoft.xrm.sdk
Third, add a CRM Connection String to your App.config
In order to connect to CRM, we use a connection string in the configuration file. Open your App.config, and add the
[xml]
<connectionStrings>
<add name="MyCRMConnection" connectionString="Authentication Type=Integrated; Server=http://192.168.1.12:5555/MyOrganization"/>
</connectionStrings>
[/xml]
section including the connection string to your CRM Server:
For more information on the connection string to connect to CRM, see MSDN article: Simplified Connection to Microsoft Dynamics CRM
Fourth, read the Secure Configuration from a file
In order to enter the Secure Configuration, we add a text file to our project named secureconfiguration.txt:
Make sure to set the Copy to Output Directory to Copy always, so the file will be deployed on building your project.
Now open Program.cs, find the Main function:
[csharp]
/// <summary>
/// The program’s main function.
/// </summary>
/// <param name="args">The program arguments</param>
public static void Main(string[] args)
{
[/csharp]
then add:
[csharp]
Console.WriteLine("Starting update of Secure Configurations");
var secureConfig = string.Empty;
using (var fileStream = File.OpenRead("secureconfiguration.txt"))
{
using (var streamReader = new StreamReader(fileStream))
{
secureConfig = streamReader.ReadToEnd();
Console.WriteLine("Configuration loaded:\r\n{0}\r\n", secureConfig);
}
}
[/csharp]
Fifth, connect to CRM
Next, we need to connect to CRM using the connection string:
[csharp]
// connect to CRM
var connection = new CrmConnection("MyCRMConnection");
Console.WriteLine("Connecting to server {0}", connection.ServiceUri);
using (var serviceContext = new CrmOrganizationServiceContext(connection))
{
[/csharp]
Sixth, filter which plug-ins to add a secure configuration to
You need a way to filter CRM’s sdkmessageprocessingsteps because CRM has a lot of it’s own and you possibly have built other plug-ins. Say now, we have a Plug-In Type with the following properties:
- Name: My.Name.Space.PluginName
- FriendlyName: MyPlugIn
- Assembly name: MyPlugInAssembly
and all of it’s registered steps need a Secure Configuration set to the value of the contents of our secureconfiguration.txt file.
In order to filter the plug-in steps we retrieve, we only look for plug-in steps that are registered to the plug-in defined above. We retrieve the id of this Plug-In Type using the following code (note that you can use either the name or the friendlyname property):
[csharp]
// Define plug-in type properties to retrieve correct plug-in type:
var pluginTypeName = "My.Name.Space.PluginName";
var assemblyname = "MyPlugInAssembly";
// Create the QueryExpression object to retrieve plug-in type
var query = new QueryExpression();
query.EntityName = "plugintype";
query.Criteria.AddCondition("name", ConditionOperator.Equal, pluginTypeName);
query.Criteria.AddCondition("assemblyname", ConditionOperator.Equal, assemblyname);
var retrievedPluginTypes = serviceContext.RetrieveMultiple(query);
if (retrievedPluginTypes.Entities.Count != 1)
{
Console.WriteLine("Error. Plug-in type was not found! Assembly: [{0}], Plug-in Type Name [{1}]", assemblyname, pluginTypeName);
Console.ReadKey();
return;
}
var pluginTypeId = (Guid)retrievedPluginTypes.Entities[0].Attributes["plugintypeid"];
Console.WriteLine("Retrieved plugin with Id = {0}", pluginTypeId);
[/csharp]
Seventh, find the plug-in steps associated with the previously found Plug-in Type
The following query will retrieve all the Plug-In Steps registered to the Plug-in Type.
[csharp]
// Create the QueryExpression object to retrieve your steps.
query = new QueryExpression();
// Set the properties of the QueryExpression object.
query.EntityName = "sdkmessageprocessingstep";
query.ColumnSet = new ColumnSet(new[] { "sdkmessageprocessingstepid", "sdkmessageprocessingstepsecureconfigid", "plugintypeid" });
query.Criteria.AddCondition(new ConditionExpression("plugintypeid", ConditionOperator.Equal, pluginTypeId));
var retrievedSteps = serviceContext.RetrieveMultiple(query);
[/csharp]
Last but not least, we iterate over all Plug-In Steps
Iterating over all Plug-In Steps, we either set the existing Secure Configuration to a new value or create a new Secure Configuration entity if one did not exist yet.
[csharp]
// Update all individual steps
foreach (var step in retrievedSteps.Entities)
{
// Test if plugin already has a Secure Configuration:
if (!step.Attributes.Contains("sdkmessageprocessingstepsecureconfigid"))
{
Console.WriteLine("Plugin has no Secure Configuration yet, so create one.");
// create new secure configuration record
var processingStepSecureConfiguration = new Entity("sdkmessageprocessingstepsecureconfig");
processingStepSecureConfiguration.Attributes.Add("secureconfig", secureConfig);
processingStepSecureConfiguration.Id = serviceContext.Create(processingStepSecureConfiguration);
// now attach secure configuration record to processing step
step.Attributes["sdkmessageprocessingstepsecureconfigid"] = processingStepSecureConfiguration.ToEntityReference();
serviceContext.Update(step);
}
else
{
Console.WriteLine("Plug-in step already has a Secure Configuration entity, so retrieve that entity and update the configuration.");
// Retrieve and update secure configuration record
var secureConfigurationReference = step.Attributes["sdkmessageprocessingstepsecureconfigid"] as EntityReference;
var secureConfiguration = serviceContext.Retrieve(secureConfigurationReference.LogicalName, secureConfigurationReference.Id, new ColumnSet(new[] { "sdkmessageprocessingstepsecureconfigid", "secureconfig" }));
secureConfiguration.Attributes["secureconfig"] = secureConfig;
serviceContext.Update(secureConfiguration);
}
}
[/csharp]
And we will close our console application with a friendly message 🙂
[csharp]
Console.WriteLine("… Secure Configuration succesfully uploaded to the steps.");
Console.WriteLine(string.Empty);
Console.WriteLine("All done. Press a key to continue.");
Console.ReadKey();
[/csharp]
And that’s it!
One final note: the secure configuration string is not passed to plug-ins running in the CRM Outlook Client!
Here is the complete code for Program.cs:
[csharp]
// ———————————————————————-
// <author>Tomas Schulkes</author>
// ————————————————————————
namespace PluginSecureConfigUpdater
{
using System;
using System.IO;
using Microsoft.Xrm.Client;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
/// <summary>
/// Main program
/// </summary>
public class Program
{
/// <summary>
/// The program’s main function.
/// </summary>
/// <param name="args">The program arguments</param>
public static void Main(string[] args)
{
Console.WriteLine("Starting update of Secure Configurations");
var secureConfig = string.Empty;
using (var fileStream = File.OpenRead("secureconfiguration.txt"))
{
using (var streamReader = new StreamReader(fileStream))
{
secureConfig = streamReader.ReadToEnd();
Console.WriteLine("Configuration loaded:\r\n{0}\r\n", secureConfig);
}
}
// connect to CRM
var connection = new CrmConnection("MyCRMConnection");
Console.WriteLine("Connecting to server {0}", connection.ServiceUri);
using (var serviceContext = new CrmOrganizationServiceContext(connection))
{
Console.WriteLine("Retrieving message steps…");
// Define plug-in type properties to retrieve correct plug-in type:
var pluginTypeName = "My.Name.Space.PluginName";
var assemblyname = "MyPlugInAssembly";
// Create the QueryExpression object to retrieve plug-in type
var query = new QueryExpression();
query.EntityName = "plugintype";
query.Criteria.AddCondition("name", ConditionOperator.Equal, pluginTypeName);
query.Criteria.AddCondition("assemblyname", ConditionOperator.Equal, assemblyname);
var retrievedPluginTypes = serviceContext.RetrieveMultiple(query);
if (retrievedPluginTypes.Entities.Count != 1)
{
Console.WriteLine("Error. Plug-in type was not found! Assembly: [{0}], Plug-in Type Name [{1}]", assemblyname, pluginTypeName);
Console.ReadKey();
return;
}
var pluginTypeId = (Guid)retrievedPluginTypes.Entities[0].Attributes["plugintypeid"];
Console.WriteLine("Retrieved plugin with Id = {0}", pluginTypeId);
// Create the QueryExpression object to retrieve your steps.
query = new QueryExpression();
// Set the properties of the QueryExpression object.
query.EntityName = "sdkmessageprocessingstep";
query.ColumnSet = new ColumnSet(new[] { "sdkmessageprocessingstepid", "sdkmessageprocessingstepsecureconfigid", "plugintypeid" });
query.Criteria.AddCondition(new ConditionExpression("plugintypeid", ConditionOperator.Equal, pluginTypeId));
var retrievedSteps = serviceContext.RetrieveMultiple(query);
// Update all individual steps
foreach (var step in retrievedSteps.Entities)
{
// Test if plugin already has a Secure Configuration:
if (!step.Attributes.Contains("sdkmessageprocessingstepsecureconfigid"))
{
Console.WriteLine("Plugin has no Secure Configuration yet, so create one.");
// create new secure configuration record
var processingStepSecureConfiguration = new Entity("sdkmessageprocessingstepsecureconfig");
processingStepSecureConfiguration.Attributes.Add("secureconfig", secureConfig);
processingStepSecureConfiguration.Id = serviceContext.Create(processingStepSecureConfiguration);
// now attach secure configuration record to processing step
step.Attributes["sdkmessageprocessingstepsecureconfigid"] = processingStepSecureConfiguration.ToEntityReference();
serviceContext.Update(step);
}
else
{
Console.WriteLine("Plug-in step already has a Secure Configuration entity, so retrieve that entity and update the configuration.");
// Retrieve and update secure configuration record
var secureConfigurationReference = step.Attributes["sdkmessageprocessingstepsecureconfigid"] as EntityReference;
var secureConfiguration = serviceContext.Retrieve(secureConfigurationReference.LogicalName, secureConfigurationReference.Id, new ColumnSet(new[] { "sdkmessageprocessingstepsecureconfigid", "secureconfig" }));
secureConfiguration.Attributes["secureconfig"] = secureConfig;
serviceContext.Update(secureConfiguration);
}
}
Console.WriteLine("… Secure Configuration succesfully uploaded to the steps.");
Console.WriteLine(string.Empty);
Console.WriteLine("All done. Press a key to continue.");
Console.ReadKey();
}
}
}
}
[/csharp]
[1] – Difference between Secure / Unsecure Configuration of Plugin Registration tool in CRM 2011