Have you heard of the Microsoft.Xrm.Data.PowerShell module? If you have aspirations involving or are doing work with Dynamics 365/the Common Data Service and DevOps, then the functionality that this provides could speed you along. Utilising the capabilities found within the Dynamics 365 SDK, the module allows you to:

  • Retrieve details regarding your Dynamics 365 organisation and the metadata that exists within.
  • Add new language packs to your instance.
  • Enable new email addresses provisioned onto the instance.
  • Create, update, delete or retrieve records.

In short, if you need to automate any boring or repetitive tasks, there is bound to be a set of cmdlets within this module that will meet your requirements.

One particularly useful feature of the module is its ability to work with solutions and to export out entire solution files in either an unmanaged or managed state. When used in conjunction with the Adoxio.Dynamics.DevOps module and, specifically, the Expand-CrmSolution cmdlet, it’s possible to compile together a script similar to the below to export out multiple solutions from a Dynamics 365 environment, and then extract out the raw definition files within the .zip file. The script outlined below can then be easily called as part of an Azure DevOps pipeline or similar, or configured to execute on a build agent of your choosing:

param (    
    [Parameter(Mandatory=$True)][ValidateNotNull()][array]$solutionsName,      
    [string]$username = "[email protected]",    
    [string]$password = "password123",    
    [string]$exportLocation = "C:\DEV\projectname",    
    [string]$instance = "https://org1234.crm3.dynamics.com/",
    [string]$region = "CAN",    
    [string]$type = "Office365"
    ) 

# Installs package provider and required PS modules, then imports to current session
Install-PackageProvider -Name NuGet -Force -Scope CurrentUser
Install-Module -Name Microsoft.Xrm.Data.Powershell -Force -Verbose -Scope CurrentUser
Install-Module -Name Adoxio.Dynamics.DevOps -Force -Verbose -Scope CurrentUser

Import-Module Microsoft.Xrm.Data.Powershell, Adoxio.Dynamics.DevOps

#Install SDK

Set-Location $exportLocation

$sourceNugetExe = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe"
$targetNugetExe = ".\nuget.exe"
Remove-Item .\Tools -Force -Recurse -ErrorAction Ignore
Invoke-WebRequest $sourceNugetExe -OutFile $targetNugetExe
Set-Alias nuget $targetNugetExe -Scope Global -Verbose

##
##Download Plugin Registration Tool
##
./nuget install Microsoft.CrmSdk.XrmTooling.PluginRegistrationTool -O .\Tools
mkdir .\Tools\PluginRegistration
$prtFolder = Get-ChildItem ./Tools | Where-Object {$_.Name -match 'Microsoft.CrmSdk.XrmTooling.PluginRegistrationTool.'}
Move-Item .\Tools\$prtFolder\tools\*.* .\Tools\PluginRegistration
Remove-Item .\Tools\$prtFolder -Force -Recurse

##
##Download CoreTools
##
./nuget install  Microsoft.CrmSdk.CoreTools -O .\Tools
mkdir .\Tools\CoreTools
$coreToolsFolder = Get-ChildItem ./Tools | Where-Object {$_.Name -match 'Microsoft.CrmSdk.CoreTools.'}
Move-Item .\Tools\$coreToolsFolder\content\bin\coretools\*.* .\Tools\CoreTools
Remove-Item .\Tools\$coreToolsFolder -Force -Recurse

##
##Download Configuration Migration
##
./nuget install  Microsoft.CrmSdk.XrmTooling.ConfigurationMigration.Wpf -O .\Tools
mkdir .\Tools\ConfigurationMigration
$configMigFolder = Get-ChildItem ./Tools | Where-Object {$_.Name -match 'Microsoft.CrmSdk.XrmTooling.ConfigurationMigration.Wpf.'}
Move-Item .\Tools\$configMigFolder\tools\*.* .\Tools\ConfigurationMigration
Remove-Item .\Tools\$configMigFolder -Force -Recurse

##
##Download Package Deployer 
##
./nuget install  Microsoft.CrmSdk.XrmTooling.PackageDeployment.WPF -O .\Tools
mkdir .\Tools\PackageDeployment
$pdFolder = Get-ChildItem ./Tools | Where-Object {$_.Name -match 'Microsoft.CrmSdk.XrmTooling.PackageDeployment.Wpf.'}
Move-Item .\Tools\$pdFolder\tools\*.* .\Tools\PackageDeployment
Remove-Item .\Tools\$pdFolder -Force -Recurse

##
##Download Package Deployer PowerShell module
##
./nuget install Microsoft.CrmSdk.XrmTooling.PackageDeployment.PowerShell -O .\Tools
$pdPoshFolder = Get-ChildItem ./Tools | Where-Object {$_.Name -match 'Microsoft.CrmSdk.XrmTooling.PackageDeployment.PowerShell.'}
Move-Item .\Tools\$pdPoshFolder\tools\*.* .\Tools\PackageDeployment.PowerShell
Remove-Item .\Tools\$pdPoshFolder -Force -Recurse

##
##Remove NuGet.exe
##
Remove-Item nuget.exe

#Set environment variable for SDK path

[Environment]::SetEnvironmentVariable("CRM_SDK_PATH", ($exportLocation + "\Tools"), "User")

# Connect to Dynamics 365/CDS

$securePassword = ConvertTo-SecureString $password -AsPlainText -Force
$credentials = New-Object System.Management.Automation.PSCredential ($username, $securePassword)

$CRMConn = Connect-CrmOnline -credential $credentials -ServerUrl $instance

#Iterate through Solution name list, export and then expand each solution in an unmanaged state

foreach($solution in $solutionsName) {
    #Export unmanaged solution
    Write-Host "Exporting $solution..."
    Export-CrmSolution $solution $exportLocation -SolutionZipFileName $solution"_Unmanaged.zip" -conn $CRMConn
    Write-Host "$solution exported successfully!"

    #Unpack solution
    Write-Host "Expanding $solution..."
    Expand-CrmSolution -ZipFile $solution"_Unmanaged.zip" -PackageType Unmanaged -Folder $solution
    Write-Host "$solution expanded successfully!"
}

Credit for helping to compile the above script should go to Nick Doelman, who has published an excellent blog post showing you how to achieve a simple application lifecycle management (ALM) process involving Dynamics 365 solutions. I’d highly recommend reading this blog post and subscribe if you haven’t already. The above script differs slightly, in that it accepts a comma-separated list of solution files to extract, which are then iterated through to export and then expand out the raw solution files. Why do we expand out the raw files? So that Git can more easily detect changes, highlighting, where necessary, when and where an individual component (e.g. the metadata for the Account entity) was modified.

The above script will, in most cases, work fine and dandy. But occasionally, you might hit errors like this, especially when working with larger Dynamics 365 solutions:

Export-CrmSolution : System.Management.Automation.RuntimeException: ************ TimeoutException – ExportSolution : User Defined |=> The request channel timed out while waiting for a reply after 00:02:00. Increase the timeout value passed to the call to Request or increase the SendTimeout value on the Binding. The time allotted to this operation may have been a portion of a longer timeout.

By default, we must contend with a two-minute timeout for all connections into CRM using this module. If exceeded at all, the script will immediately throw the above error. The answer to getting around this should be fairly obvious – increase the timeout when using the Export-CrmSolution or Connect-CrmOnline cmdlets. However, none of these cmdlets contains an appropriate parameter we can call to increase this. Upon further inspection of the list of available cmdlets within the module, the answer becomes apparent when we review details about the Set-CrmConnectionTimeout cmdlet:

The solution becomes pretty self-explanatory after reviewing this – add in the following cmdlet before iterating through the list of solutions:

# Increase timeout for all connections to 20 minutes

Set-CrmConnectionTimeout -conn $conn -TimeoutInSeconds 1200

You can adjust the timeout to suit your particular needs, but 20 minutes should be more than sufficient for even the largest solution files.

As usual, the Dynamics 365 wider community delivers continual value, with some fantastic tools released for free. The Microsoft.Xrm.Data.PowerShell and Adoxio.Dynamics.DevOps modules surely sit amongst the likes of the XrmToolBox, CRM Rest Builder and more. They are an essential addition to your toolbox when attempting to apply DevOps within Dynamics 365/the Common Data Service.

Share This