Deploy plugins / workflows assemblies with Powershell

 

Context

When you work on a new plugin, you need to deploy it multiple time to get the expected behavior regarding customer need.
But, using Registration Tool multiple time per day represent an optimisable workload considering manual operations and also the risk of human error.
So I have made a little script that scan the binaries folder in output of Visual Studio compilation and update the existing CRM assemblies.

Prerequisites

The script couldn’t run properly without the following criterias:

  1. CRM user specified in configuration file need to be system administrator (update assembly)
  2. You should provide an Assembliesfolder that contains
    1. CRM SDK assemblies :
      1. Microsoft.Xrm.Sdk.dll
      2. Microsoft.Xrm.Client.dll
      3. Microsoft.Crm.Sdk.Proxy.dll
  3. Script must be run with elevated privilèges

 

Process

  1. Scan assembly folder given in parameter
  2. Check if assembly need to be updated (regarding configuration : name and configuration)
  3. Update assembly
  4. Deploy debug symbols (pdb) to configured folder

Configuration

The script use a configuration.xml file that provide the following parameters :

  1: <Configuration>
  2:     <CrmConnectionString>Url=https://crm/dev</CrmConnectionString>
  3:     <Assemblies>
  4:         <Path>c:\MCS\Assemblies</Path>
  5:         <Configuration>debug</Configuration>
  6:         <Names>Plugins.dll;Workflows.dll;</Names>
  7:         <SymbolPath>\\crmsrv\C$\Program Files\Microsoft Dynamics CRM\Server\bin\assembly</SymbolPath>
  8:     </Assemblies>
  9: </Configuration>
  • CrmConnectionString : Connection to CRM organization (More info :https://msdn.microsoft.com/en-us/library/gg695810.aspx)
  • Assemblies
    • Path : Folder where assemblies are outputed
    • Configuration : Specify if assemblies are build in “debug” or “release”
    • Names : Names of the assemblies to update ‘'(separated with semi-colon ‘;’)
    • SymbolPath : Folder where to copy pdb files for debug purposes

Script

  1: clear;
  2:  
  3: function Add-Crm-Sdk
  4: {
  5:     # Load SDK assemblies
  6:     Add-Type -Path "$PSScriptRoot\Assemblies\Microsoft.Xrm.Sdk.dll";
  7:     Add-Type -Path "$PSScriptRoot\Assemblies\Microsoft.Xrm.Client.dll";
  8:     Add-Type -Path "$PSScriptRoot\Assemblies\Microsoft.Crm.Sdk.Proxy.dll";
  9: }
  10:  
  11: function Get-Configuration()
  12: {
  13:     $configFilePath = "$PSScriptRoot\Configuration.xml";
  14:     $content = Get-Content $configFilePath;
  15:     return [xml]$content;
  16: }
  17:  
  18:  
  19: function Get-Assembly
  20: {
  21:      PARAM
  22:     (
  23:         [parameter(Mandatory=$true)]$name,
  24:         [parameter(Mandatory=$true)]$orgService
  25:     )
  26:  
  27:     $query = New-Object -TypeName Microsoft.Xrm.Sdk.Query.QueryExpression -ArgumentList "pluginassembly";
  28:     $query.Criteria.AddCondition("name", [Microsoft.Xrm.Sdk.Query.ConditionOperator]::Equal, $name);
  29:     $query.ColumnSet =  New-Object -TypeName Microsoft.Xrm.Sdk.Query.ColumnSet -ArgumentList $true;
  30:     $results = $orgService.RetrieveMultiple($query);
  31:     $records = $results.Entities;
  32:  
  33:     if($records.Count -eq 1)
  34:     {
  35:         return $records[0];
  36:     }
  37:     return $null;
  38: }
  39:  
  40: function Get-Base64
  41: {
  42:     PARAM
  43:     (
  44:         [parameter(Mandatory=$true)]$path
  45:     )
  46:     
  47:     $content = [System.IO.File]::ReadAllBytes($path);
  48:     $content64 = [System.Convert]::ToBase64String($content);
  49:     return $content64;
  50: }
  51:  
  52: Add-Crm-Sdk;
  53: $config = Get-Configuration;
  54:  
  55: # =======================================================
  56: # Crm Connection
  57: # =======================================================
  58: $crmConnectionString = $config.Configuration.CrmConnectionString;
  59: $crmConnection = [Microsoft.Xrm.Client.CrmConnection]::Parse($crmConnectionString);
  60: $service = New-Object -TypeName Microsoft.Xrm.Client.Services.OrganizationService -ArgumentList $crmConnection;
  61:  
  62: $assembliesPath = $config.Configuration.Assemblies.Path;
  63: $assemblyConfiguration = $config.Configuration.Assemblies.Configuration.ToLower();
  64: $assembliesToDeploy = $config.Configuration.Assemblies.Names.Split(";", [StringSplitOptions]::RemoveEmptyEntries);
  65:  
  66:  
  67: # =======================================================
  68: # Process assemblies
  69: # =======================================================
  70: $d = Get-Date;
  71: Write-Host "$d - Deploy Assemblies ($assemblyConfiguration) start" -ForegroundColor Cyan;
  72:  
  73: $assemblies = Get-ChildItem $assembliesPath -recurse -include *.dll;
  74: $assemblies | ForEach-Object {        
  75:     $assemblyPath = $_.FullName.ToString();
  76:     $assemblyName = $_.Name.ToString();
  77:         
  78:     if($assemblyPath.Contains("bin") -and $assemblyPath.ToLower().Contains($assemblyConfiguration))
  79:     {
  80:         foreach($assemblyToDeploy in $assembliesToDeploy)
  81:         {
  82:             if($assemblyName -eq $assemblyToDeploy)
  83:             {        
  84:                 Write-Host " - Deploying assembly $assemblyPath ...";
  85:                 
  86:                 $assemblyFile = [System.Reflection.Assembly]::LoadFile($assemblyPath);
  87:                 $assemblyProperties = $assemblyFile.GetName().FullName.Split(",= ".ToCharArray(), [StringSplitOptions]::RemoveEmptyEntries);
  88:                 $assemblyShortName = $assemblyProperties[0];
  89:                 $assemblyFile = $null;
  90:  
  91:                 $assemblyContent = Get-Base64 $assemblyPath;
  92:  
  93:                 Write-Host " > Searching assembly $assemblyShortName ..." -NoNewline;
  94:                 $crmAssembly = Get-Assembly -name $assemblyShortName -orgService $service;
  95:                 if ($crmAssembly -eq $null)
  96:                 {
  97:                     Write-Host "not found!" -ForegroundColor Red; 
  98:                     continue;
  99:                 }
  100:                 else
  101:                 {    
  102:                     Write-Host "found!" -ForegroundColor Green; 
  103:                 }
  104:  
  105:                 $crmAssembly["version"] = $assemblyProperties[2];
  106:                 $crmAssembly["culture"] = $assemblyProperties[4];
  107:                 $crmAssembly["publickeytoken"] = $assemblyProperties[6];
  108:                 $crmAssembly["content"] = $assemblyContent;
  109:  
  110:                 Write-Host " > Updating assembly $assemblyShortName ..." -NoNewline;
  111:                 try
  112:                 {
  113:                     $service.Update($crmAssembly);
  114:                 }
  115:                 catch [Exception]
  116:                 {
  117:                     Write-Host "failed! [Error : $_.Exception]" -ForegroundColor Red;
  118:                     break;
  119:                 }
  120:                 $assemblyFile = $null;
  121:                 
  122:                 Write-Host "done!" -ForegroundColor Green; 
  123:                 break;
  124:             }
  125:         }
  126:     }
  127: }
  128:  
  129: # =======================================================
  130: # Deploy PDB
  131: # =======================================================
  132:  
  133: Write-Host "";
  134:  
  135: $debugFolder = $config.Configuration.Assemblies.SymbolPath;
  136:  
  137: if(![string]::IsNullOrEmpty($debugFolder))
  138: {
  139:     $debugFiles = Get-ChildItem $assembliesPath -recurse -include *.pdb;
  140:     $debugFiles | ForEach-Object {  
  141:         $debugFilePath = $_.FullName.ToString();
  142:         $debugFileName = $_.Name.ToString();
  143:         if($debugFilePath.Contains("bin") -and $debugFilePath.Contains("$assemblyConfiguration"))
  144:         {
  145:             Write-Host " - Deploying pdb $debugFilePath to $debugFolder ..." -NoNewLine;
  146:             Copy-Item $debugFilePath -Destination $debugFolder -Force;
  147:             Write-Host -ForegroundColor Green "Done!";
  148:         }
  149:     }
  150: }
  151: $d = Get-Date;
  152: Write-Host "$d - Deploy Assemblies ($assemblyConfiguration) stop" -ForegroundColor Cyan;
  153:  
  154: $d = Get-Date;
  155: Write-Host "$d - Deploy assemblies stop" -ForegroundColor Cyan;

Download full script