PowerShell scripting for Microsoft Dynamics CRM – Rename attribute using a cmdlet

PowerShell scripting for Microsoft Dynamics CRM – Rename attribute using a cmdlet

Although most of the customizations and developments in Dynamics CRM can be packaged and imported from a solution, some customizations may need manual actions during the delivery process. For example, association mappings may need an extra and manual effort to set-up.

PowerShell is a great way to perform repetitive or custom actions against a Dynamics CRM organization through the available Web services requests. The Microsoft SDK provides a library in order to make the task of writing scripts easier. My previous blog post on the Microsoft Tooling library presents a detailed set of examples and the possibilities it offers.

However, custom actions may require multiple Web service requests or a specific implementation. For example, this article presents a simple implementation of a custom PowerShell cmdlet for updating the display name of an attribute. This action requires two Web service calls: one to retrieve the attribute metadata and another to update the attribute metadata.

Renaming labels, a real life scenario

A best practice consists in renaming the display names of unused attributes by adding the prefix “ZZZ_” to their display name. Now, consider applying this rule to an organization with several unused attributes from different entities.

This real life scenario can be problematic. Although updating display names is possible via a solution, it would mean to update the metadata, forms and views of all impacted entities. Moreover, performing the operations manually is quite time consuming. PowerShell allows to perform only the intended actions and automate the task.

Before we begin

In order to implement and test this custom cmdlet, you may need to set-up your development environment accordingly. The requirements for developing PowerShell cmdlets for Dynamics CRM are described in a previous blog post.

Moreover, you will need to instantiate a service object using the proper credentials. This object will be passed as an argument to our cmdlet to perform Web service calls against the CRM organization. The implementation of the connection cmdlet is explained in a previous post: A first cmdlet to connect. If you are not familiar with the PowerShell terminology, this post defines key terms as well, such as cmdlet and pipeline.

Update the display name of an attribute using a cmdlet

Here are the steps required to implement a cmdlet to update the display name of an attribute.

  1. Open the project created in the previous blog post.
  2. Reference the System.Runtime.Serialization assembly for performing Web service calls.
  3. Create a class named Rename_Attribute.cs, and write the following code:
using System;
// Also needs a reference on System.Runtime.Serialization assembly

// PowerShell Cmdlet base class namespace 
using System.Management.Automation;

// Dynamics CRM client namespace 
using Microsoft.Xrm.Client.Services;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Messages;

namespace Hiyoko.Crm.PowerShell
{
    // Windows PowerShell uses a verb-and-noun name pair to name cmdlets.
    // The couple defined here is used by PowerShell runtime. 
    [Cmdlet(VerbsCommon.Rename, "Attribute")]

    // Derive from the Cmdlet base class.
    public class Rename_Attribute : Cmdlet
    {
        // Define parameter as mandatory. This check is performed by the PowerShell runtime.
        [Parameter(Mandatory = true, HelpMessage = "CRM Organization service")]

        // Validate that the parameter is not null or empty. This check is performed by the PowerShell runtime.
        [ValidateNotNullOrEmpty]
        public OrganizationService Service { get; set; }

        [Parameter(Mandatory = true, HelpMessage = "Entity Logical Name")]
        [ValidateNotNullOrEmpty]
        public string EntityLogicalName { get; set; }

        [Parameter(Mandatory = true, HelpMessage = "Attribute Logical Name")]
        [ValidateNotNullOrEmpty]
        public string AttributeLogicalName { get; set; }

        [Parameter(Mandatory = true, HelpMessage = "Display Name")]
        [ValidateNotNullOrEmpty]
        public string DisplayName { get; set; }

        [Parameter(Mandatory = true, HelpMessage = "Language Code")]

        // Validate that the parameter's value is in a possible range of values.
        // Source: https://msdn.microsoft.com/en-us/library/ms912047%28WinEmbedded.10%29.aspx
        [ValidateRange(1025, 20490)]

        public int LanguageCode { get; set; }

        ///  
        /// Implement the ProcessRecord method to provide record-by-record processing functionality for the cmdlet. 
        /// 
        protected override void ProcessRecord()
        {
            // Retrieve CRM attribute metadata
            var attributeRequest = new RetrieveAttributeRequest
            {
                EntityLogicalName = EntityLogicalName,
                LogicalName = AttributeLogicalName,
                RetrieveAsIfPublished = false
            };
            var attributeResponse = (RetrieveAttributeResponse)Service.Execute(attributeRequest);

            if (attributeResponse == null)
            {
                var exception = new Exception("An error occured while updating attribute metadata: response is null.");
                var errorRecord = new ErrorRecord(exception, exception.Message, ErrorCategory.InvalidResult, attributeResponse);

                /*
                 * When a cmdlet encounters a terminating error, call this method rather than simply throwing an exception.
                 * Calling this method allows the cmdlet to attach additional error record information that describes 
                 * the condition that caused the terminating error. 
                 * When this method is called, the Windows PowerShell runtime catches the error record and 
                 * then starts shutting down the pipeline.
                 */
                ThrowTerminatingError(errorRecord);
            }

            if (attributeResponse.AttributeMetadata == null)
            {
                var exception = new Exception("An error occured while updating attribute metadata: attribute metadata is null.");
                var errorRecord = new ErrorRecord(exception, exception.Message, ErrorCategory.InvalidResult, attributeResponse);

                ThrowTerminatingError(errorRecord);
            }

            var retrievedAttributeMetadata = attributeResponse.AttributeMetadata;

            // Update the retrieved attribute metadata.
            retrievedAttributeMetadata.DisplayName = new Label(DisplayName, LanguageCode);

            // Send the updated attribute metadata to the CRM server.
            var updateRequest = new UpdateAttributeRequest
            {
                Attribute = retrievedAttributeMetadata,
                EntityName = EntityLogicalName,
                MergeLabels = false
            };
            Service.Execute(updateRequest);

            // Write a verbose output to the PowerShell prompt.
            WriteVerbose(string.Format("Attribute: {0} of entity: {1} updated with new display name: {2} for language code: {3}",
                AttributeLogicalName,
                EntityLogicalName,
                DisplayName,
                LanguageCode));
        }
    }
}

A few important things to notice:

  • While implementing cmdlets, always allow PowerShell engine to validate the parameters when possible.
    For more information about parameter validation, see about_Functions_Advanced_Parameters.
  • Report terminating and non terminating errors to the pipeline using the appropriate error handling method.
    For more information, see Cmdlet Error Reporting.

Call the cmdlet in PowerShell

Once the Hiyoko.Crm.PowerShell project is compiled, create a PowerShell script in the same directory as the DLL output file using PowerShell ISE:

# Get the current script directory path
$ScriptPath = $(Split-Path -Path $script:MyInvocation.MyCommand.Path)

# Add the definition of the objects contained in the Microsoft.Xrm.Client namespace.
Add-Type -Path $($ScriptPath + "\microsoft.xrm.sdk.dll")

# Import the Get_OrganizationService class.
Import-Module -Name $($ScriptPath + "\Hiyoko.Crm.PowerShell.dll")

# Define the CRM connection string
$CrmConnectionString = "Url=https://crm.company.org/organization;"

# Retrieve the corresponding OrganizationService object.
$organizationService = Get-OrganizationService -CrmConnectionString $CrmConnectionString

# Test connection
if($organizationService.InnerService.IsAuthenticated)
{
    # The Verbose switch enables Verbose output from the cmdlet.
    Rename-Attribute -Service $organizationService -EntityLogicalName "account" -AttributeLogicalName "name" -DisplayName "Company name" -LanguageCode 1033 -Verbose:$true
}

Click on Execute script button or F5 to test the script.

The code source of this tutorial is available on GitHub: https://github.com/ThomasCanaple/posh-msdyncrm.

What’s next?

Using Web service requests and PowerShell, it is possible to implement a complete and complex delivery process : data migration, plugin steps impersonation, …

Please do not hesitate to share your thoughts about this blog post!

Leave a Reply

Your email address will not be published.