Synchronize a WebResources folder to CRM with Powershell

 

If you need to synchronize all webresource from a local folder tree to a CRM solution with a simple Powershell script, I have the solution! Sourire

Process

The Powershell script realizes the following operations:

  1. Load configuration file
  2. Connect to CRM
  3. Load files from specified folder which have been modified in give timeframe
  4. Process each file
    1. Check if file match to an existing Webresource
    2. If not
      1. Create the webresource according to the file extension
      2. Add webresource to given solution
    3. Else
      1. Check if webresource content has changed
      2. If content is different, update webresouce
  5. Publish modifications if changes have been detected

 

Prerequisites

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

  1. CRM user specified in configuration file need to be system administrator (create / update webresources, publish)
  2. You should provide an Assemblies folder 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 privileges

 

Configuration

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

    1: <Configuration>
    2:     <CrmConnectionString>Url=https://crm/dev</CrmConnectionString>
    3:     <DeltaHours>15</DeltaHours>
    4:     <SolutionName>Solution1</SolutionName>
    5:     <SolutionPrefix>new_</SolutionPrefix>
    6:     <WebResourceFolderPath>c:\MCS\WebResouces</WebResourceFolderPath>
    7: </Configuration>
  • CrmConnectionString : Connection to CRM organization (More info : https://msdn.microsoft.com/en-us/library/gg695810.aspx)
  • DeltaHours : Number of hours before the execution time that determine if the file is considered as changed (prevent to process a lot of file if it is not necessary)
  • SolutionName : Name of the CRM solution where webresources should be added
  • SolutionPrefix : Prefix used by webresource name in solution
  • WebResourceFolderPath : Path to the folder that contains webresource files.

 

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: function Get-WebResource
   19: {
   20:      PARAM
   21:     (
   22:         [parameter(Mandatory=$true)]$name
   23:     )
   24:  
   25:     $query = New-Object -TypeName Microsoft.Xrm.Sdk.Query.QueryExpression -ArgumentList "webresource";
   26:     $query.Criteria.AddCondition("name", [Microsoft.Xrm.Sdk.Query.ConditionOperator]::Equal, $name);
   27:     $query.ColumnSet.AddColumn("content");
   28:     $results = $service.RetrieveMultiple($query);
   29:     $records = $results.Entities;
   30:  
   31:     if($records.Count -eq 1)
   32:     {
   33:         return $records[0];
   34:     }
   35:     return $null;
   36: }
   37:  
   38: Add-Crm-Sdk;
   39: $config = Get-Configuration;
   40: $publishXmlRequest = "<importexportxml><webresources>";
   41:  
   42: # =======================================================
   43: # Crm Connection
   44: # =======================================================
   45: $crmConnectionString = $config.Configuration.CrmConnectionString;
   46: $crmConnection = [Microsoft.Xrm.Client.CrmConnection]::Parse($crmConnectionString);
   47: $service = New-Object -TypeName Microsoft.Xrm.Client.Services.OrganizationService -ArgumentList $crmConnection;
   48:  
   49: $d = Get-Date;
   50: Write-Host "$d - Deploy WebResources start" -ForegroundColor Cyan;
   51:  
   52: # =======================================================
   53: # Load last modified webresources and process files
   54: # =======================================================
   55: $deltaHours = [int]$config.Configuration.DeltaHours;
   56: $delta = $d.AddHours($deltaHours*-1);
   57:  
   58: $webResources = Get-ChildItem $config.Configuration.WebResourceFolderPath -recurse -include *.js, *.html, *.css, *.png, *.gif | where-object {$_.mode -notmatch "d"} | where-object {$_.lastwritetime -gt $delta} 
   59: $current = 0;
   60: $total = $webResources.Count;
   61: foreach($wr in $webResources)
   62: {    
   63:     $current++;
   64:     $percent = ($current/$total)*100;
   65:         
   66:     # =======================================================
   67:     # Handle prefix in file name
   68:     # =======================================================
   69:     $webResourcePath = $wr.FullName.ToString();
   70:     $webResourceName = $wr.Name.ToString();
   71:     $extension = $_.Extension;
   72:     if($webResourceName.StartsWith($config.Configuration.SolutionPrefix) -eq $false)
   73:     {
   74:         $position = $webResourcePath.LastIndexOf($config.Configuration.SolutionPrefix);
   75:         if($position -gt 0)
   76:         {
   77:             $webResourceName = $webResourcePath.Substring($position);
   78:             $webResourceName = $webResourceName.Replace('\', '/');
   79:         }
   80:     }
   81:  
   82:     Write-Host " - WebResource '$webResourceName' (Path : $webResourcePath) " -NoNewline;
   83:     Write-Progress -Activity "WebResource deployment" -Status "[$current/$total] WebResource '$webResourceName' (Path : $webResourcePath)" -PercentComplete $percent;
   84:              
   85:     # =======================================================
   86:     # Check webresource existence
   87:     # If not exists, create it
   88:     # Else update it
   89:     # =======================================================
   90:     $webResourceContentB64 = Get-Base64 $webResourcePath;    
   91:     $webresource = Get-WebResource $webResourceName;
   92:     if($webresource -eq $null)
   93:     {
   94:         #region Webresource creation
   95:         Write-Host " not found!" -ForegroundColor Yellow -NoNewline;
   96:         if(!$webResourceName.StartsWith($config.Configuration.SolutionPrefix))
   97:         {
   98:             Write-Host "ignored!" -ForegroundColor Gray;
   99:             continue;
  100:         }
  101:         Write-Host "=> Create it ..." -NoNewline;
  102:                            
  103:         # =======================================================
  104:         # Create webresource
  105:         # =======================================================
  106:         $wr =  New-Object -TypeName Microsoft.Xrm.Sdk.Entity -ArgumentList "webresource"
  107:         $wr["name"] = $webResourceName;
  108:         $wr["displayname"] = $webResourceName;
  109:         $wr["content"] = Get-Base64 $webResourcePath;
  110:  
  111:         switch ($extension.ToLower())
  112:         {
  113:             ".htm"  { $wr["webresourcetype"] = New-Object -TypeName Microsoft.Xrm.Sdk.OptionSetValue -ArgumentList 1; }
  114:             ".html" { $wr["webresourcetype"] = New-Object -TypeName Microsoft.Xrm.Sdk.OptionSetValue -ArgumentList 1; }
  115:             ".css"  { $wr["webresourcetype"] = New-Object -TypeName Microsoft.Xrm.Sdk.OptionSetValue -ArgumentList 2; }
  116:             ".js"   { $wr["webresourcetype"] = New-Object -TypeName Microsoft.Xrm.Sdk.OptionSetValue -ArgumentList 3; }
  117:             ".xml"  { $wr["webresourcetype"] = New-Object -TypeName Microsoft.Xrm.Sdk.OptionSetValue -ArgumentList 4; }
  118:             ".png"  { $wr["webresourcetype"] = New-Object -TypeName Microsoft.Xrm.Sdk.OptionSetValue -ArgumentList 5; }
  119:             ".jpg"  { $wr["webresourcetype"] = New-Object -TypeName Microsoft.Xrm.Sdk.OptionSetValue -ArgumentList 6; }
  120:             ".jpeg" { $wr["webresourcetype"] = New-Object -TypeName Microsoft.Xrm.Sdk.OptionSetValue -ArgumentList 6; }
  121:             ".gif"  { $wr["webresourcetype"] = New-Object -TypeName Microsoft.Xrm.Sdk.OptionSetValue -ArgumentList 7; }
  122:             ".xap"  { $wr["webresourcetype"] = New-Object -TypeName Microsoft.Xrm.Sdk.OptionSetValue -ArgumentList 8; }
  123:             ".xsl"  { $wr["webresourcetype"] = New-Object -TypeName Microsoft.Xrm.Sdk.OptionSetValue -ArgumentList 9; }
  124:             ".ico"  { $wr["webresourcetype"] = New-Object -TypeName Microsoft.Xrm.Sdk.OptionSetValue -ArgumentList 10;}
  125:             default { Write-Host "Unkown webresource extension : $extension" -ForegroundColor Red; }
  126:         }
  127:         $wr["webresourcetype"] = [Microsoft.Xrm.Sdk.OptionSetValue]$wr["webresourcetype"]; 
  128:  
  129:         try
  130:         {
  131:             $id = $service.Create($wr);
  132:             $publishXmlRequest += [string]::Concat("<webresource>", $id, "</webresource>"); 
  133:             Write-Host "Done!" -ForegroundColor Green -NoNewline;            
  134:             $publish = $true;
  135:         }
  136:         catch [Exception]
  137:         {                
  138:             Write-Host "Failed! [Error : $_.Exception]" -ForegroundColor Red;
  139:             continue;
  140:         }
  141:         
  142:         
  143:         # =======================================================
  144:         # Add webresource to CRM Solution
  145:         # =======================================================
  146:         $solutionName = $config.Configuration.SolutionName;
  147:         Write-Host " => Add to solution '$solutionName'..." -NoNewline;
  148:  
  149:         $request = New-Object -TypeName Microsoft.Crm.Sdk.Messages.AddSolutionComponentRequest;
  150:         $request.AddRequiredComponents = $false;
  151:         $request.ComponentType = 61;
  152:         $request.ComponentId = $id;
  153:         $request.SolutionUniqueName = $solutionName;
  154:         try
  155:         {
  156:             $response = $service.Execute($request);
  157:             
  158:         }
  159:         catch [Exception]
  160:         {                
  161:             Write-Host "Failed! [Error : $_.Exception]" -ForegroundColor Red;
  162:             continue;
  163:         }            
  164:         Write-Host "Done!" -ForegroundColor Green;
  165:         
  166:         #endregion Webresource creation
  167:     }
  168:     else
  169:     {
  170:         #region Webresource update
  171:  
  172:         # =======================================================
  173:         # Update webresource if content is different
  174:         # =======================================================
  175:         $crmWebResourceContent = $webresource.Attributes["content"].ToString();
  176:         if($crmWebResourceContent -ne $webResourceContentB64)
  177:         {
  178:             $webresource["content"] = $webResourceContentB64;
  179:             try
  180:             {
  181:                 $service.Update($webresource);
  182:                 $publishXmlRequest += [string]::Concat("<webresource>", $webresource.Id, "</webresource>");
  183:                 $publish = $true;
  184:                 Write-Host "updated!" -ForegroundColor Green;
  185:             }
  186:             catch [Exception]
  187:             {                
  188:                 Write-Host "update failed! [Error : $_.Exception]" -ForegroundColor Red;
  189:                 continue;
  190:             }
  191:         }
  192:         else
  193:         {
  194:             Write-Host "ignored!" -ForegroundColor Gray;
  195:         }
  196:         #endregion Webresource update
  197:     }    
  198: }
  199: write-progress one one -completed;
  200:  
  201: # =======================================================
  202: # Publish modifications if necessary
  203: # =======================================================
  204:  if($publish)
  205: {    
  206:     $d = Get-Date;
  207:     Write-Host "$d - Publish start" -ForegroundColor Cyan;
  208:     $publishXmlRequest += "</webresources></importexportxml>";
  209:  
  210:     $publishRequest = New-Object -TypeName Microsoft.Crm.Sdk.Messages.PublishXmlRequest;
  211:     $publishRequest.ParameterXml = $publishXmlRequest;
  212:     $publishResponse = $service.Execute($publishRequest);
  213:  
  214:     $d = Get-Date;
  215:     Write-Host "$d - Publish stop" -ForegroundColor Cyan;
  216: }
  217:  
  218: $d = Get-Date;
  219: Write-Host "$d - Deploy WebResources stop" -ForegroundColor Cyan;

Download the full version