Blog Archives

Dynamics CRM Parallel Development with Solution Packager

The Solution Packager is a very useful tool that has been released with Dynamics CRM 2011 Update Rollup 10 and is currently also available in Dynamics CRM 2013 Sdk. Traditionally you can only move CRM components around between two environments using CRM Solutions. You will soon start to see the limitations of this if you are doing one of the below:

  • You are working on a team where each developer has their own virtual machine or CRM organization for isolation purposes
  • You are on a project with one or more parallel streams of work where each stream has their own environment and different release schedule but working on the same CRM Solution

In all of the above cases you will need to bring all the pieces together once the work is ready and release them into your main CRM environment for testing or further packaging as CRM solutions. You will find that the only way to merge these changes with the existing solution is to place your changes into a new/existing solution and deploy that to your main CRM environment that contains the existing changes (Note these could have moved on since you started to work your specific feature). Below are a few issues you might encounter with that approach:

  • When you deploy the CRM Solution containing your changes this might potentially override customizations that are already in there. You might end up re-doing some of work manually in two places.
  • It is difficult to track what changes were made to implement that specific feature. You can track your source control items such as JavaScript files and CSharp code files but not your CRM customisations such as changes to data model, workflows, user interface and others.

The solution packager allows to extract your customizations from your own/team environment after you are done with a feature and check-in your changes into source control along with changes you have made to the files that are normally in source control such as JavaScript & other code files for your plug-ins for example. By default when you run the extract operation this will include everything that is contained in the solution zip files so it will include things like your plug-in assemblies and JavaScript that you already have in source control. The solution packager allows you to provide a mapping file so that during the extract operation it can skip the files you already have in source control and use the files you already have in source control and use the newly complied assemblies from the latest source code. Below is a diagram so you can picture this.

Extract Solution

To help make this easier I have created a sample PowerShell script that automates some of the activities discussed above.This is included in the latest xRM CI Framework along with the required assemblies. This script will do the below.

  • Check-out your existing extracted files from TFS (optional)
  • Export your managed and unmanaged solution from CRM
  • Find the deltas and mark the files under TFS with add/delete/check-out accordingly
  • Check-in the updated files back into TFS (optional)

You can use the sample script below and make any changes as required. Don’t forget to download and install the assemblies for the xRM CI Framework or plug-in your own utilities.

Code Snippet
  1. # Filename: ExtractCustomizations.ps1
  2. param([string]$solutionPackager, #The full path to the solutionpackager.exe
  3. [string]$solutionFilesFolder, #The folder to extract the CRM solution
  4. [string]$mappingFile, #The full path to the mapping file
  5. [string]$solutionName, #The unique CRM solution name
  6. [string]$connectionString, #The connection string as per CRM Sdk
  7. [switch]$checkout, #Optional – pass if you want to Check Out the existing files in the extract location
  8. [switch]$checkin) #Optional – pass if you want to Check In the updated files after extraction
  9. $ErrorActionPreference = “Stop”
  10. Write-Host “Solution Packager: $solutionPackager”
  11. Write-Host “Solution Files Folder: $solutionFilesFolder”
  12. Write-Host “Mapping File: $mappingFile”
  13. Write-Host “ConnectionString: $connectionString”
  14. Write-Host “Check In: $checkin”
  15. Write-Host “Check Out: $checkout”
  16. # CI Toolkit
  17. $scriptPath = split-path -parent $MyInvocation.MyCommand.Definition
  18. $xrmCIToolkit = $scriptPath + “\..\PowerShell Cmdlets\Xrm.Framework.CI.PowerShell.dll”
  19. Write-Host “Importing CIToolkit: $xrmCIToolkit”
  20. Import-Module $xrmCIToolkit
  21. #TF.exe
  22. $tfCommand = “C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\tf.exe”
  23. #Check Pending Changes
  24. $pendingChanges = & “$tfCommand” status /recursive /noprompt “$solutionFilesFolder”
  25. if ($pendingChanges -like “*no pending changes*”)
  26. {
  27.     Write-Output $pendingChanges
  28. }
  29. else
  30. {
  31.     Write-Output $pendingChanges
  32.     Write-Error “Pending Changes Detected. Undo your changes and try again.”
  33.     return
  34. }
  35. #Before Files
  36. [string[]]$beforeFiles = [System.IO.Directory]::GetFileSystemEntries($solutionFilesFolder, “*”, [IO.SearchOption]::AllDirectories)
  37. Write-Host “Before Files: ” $beforeFiles
  38. #Check Out
  39. if ($checkout -and ($beforeFiles.Length -gt 0))
  40. {
  41.     & “$tfCommand” checkout /recursive /noprompt “$solutionFilesFolder”
  42. }
  43. #Export Solutions
  44. Write-Host “Exporting Solutions to: ” $env:TEMP
  45. $unmanagedSolution = Export-XrmSolution -ConnectionString $connectionString -Managed $False -OutputFolder $env:TEMP -UniqueSolutionName $solutionName
  46. Write-Host “Exported Solution: $unmanagedSolution”
  47. $managedSolution = Export-XrmSolution -ConnectionString $connectionString -Managed $True -OutputFolder $env:TEMP -UniqueSolutionName $solutionName
  48. Write-Host “Exported Solution: $managedSolution”
  49. #Solution Packager
  50. $extractOuput = & “$solutionPackager” /action:Extract /zipfile:”$env:TEMP\$unmanagedSolution” /folder:”$solutionFilesFolder” /packagetype:Both /errorlevel:Info /allowWrite:Yes /allowDelete:Yes /map:$mappingFile
  51. Write-Output $extractOuput
  52. if ($lastexitcode -ne 0)
  53. {
  54.     throw “Solution Extract operation failed with exit code: $lastexitcode”
  55. }
  56. else
  57. {
  58.     if (($extractOuput -ne $null) -and ($extractOuput -like “*warnings encountered*”))
  59.     {
  60.         Write-Warning “Solution Packager encountered warnings. Check the output.”
  61.     }
  62. }
  63. #After Files
  64. [string[]]$afterFiles = [System.IO.Directory]::GetFileSystemEntries($solutionFilesFolder, “*”, [IO.SearchOption]::AllDirectories)
  65. Write-Host “After Files: ” $afterFiles
  66. #Get the deltas
  67. $deletedFiles = $beforeFiles | where {$afterFiles -notcontains $_}
  68. $addedFiles = $afterFiles | where {$beforeFiles -notcontains $_}
  69. if ($deletedFiles.Length -gt 0)
  70. {
  71.     Write-Host “Deleted Files:” $deletedFiles
  72.     $_files = [System.String]::Join(“”” “””, $deletedFiles)
  73.     & “$tfCommand” undo /noprompt “$_files”
  74.     & “$tfCommand” delete /noprompt “$_files”
  75. }
  76. if ($addedFiles.Length -gt 0)
  77. {
  78.     Write-Host “Added Files:” $addedFiles
  79.     $_files = [System.String]::Join(“”” “””, $addedFiles)
  80.     & “$tfCommand” add /noprompt “$_files”
  81. }
  82. # Checkin
  83. if ($checkin)
  84. {
  85.     & “$tfCommand” checkin /noprompt /recursive /override:”PowerShell Extract” “$solutionFilesFolder” /bypass
  86. }
  87. # End of script

Below is also a sample script that invokes the script above with some predefined parameters to get you started.

Code Snippet
  1. & “C:\Program Files (x86)\Xrm CI Framework\CRM 2013\PowerShell Cmdlets\ExtractCustomizations.ps1” -solutionPackager “C:\workspace\CISample\Src\Sample2\Lib\CrmSdk\solutionpackager.exe” -solutionFilesFolder “C:\waelcrm2013\CISample\Src\Sample2\SolutionFiles” -mappingFile “C:\workspace\CISample\Src\Sample2\LocalSolutionPackagerMapping.xml” -solutionName “Sample” -connectionString “ServiceUri=; Domain=domain; Username=username; Password=******” -checkout

If you this approach and you are using TFS for managing your backlog, I would strongly recommend you associated your check-in of the CRM Customizations (Extracted using the above) and any source files developed in visual studio with your product backlog item. This way you can later see what were the changes that were done to meet that specific requirement or bug.

Having said all of the above, parallel development adds an overhead and additional complexity. So only go for that option if you really need to. If you decide to ahead anyway, avoid making changes to the same customizations and components by different team  members/teams and remember communication and a good process in place is always important. Bare in mind you can still use the solution packager for keeping a backup or your customizations and viewing deltas overtime which can be helpful in troubleshooting as well.

In the next post I will showing you how to automate the build of these extracted solution files and produce solutions that you can then deploy to other environments or ship to your customers.