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!
Process
The Powershell script realizes the following operations:
- Load configuration file
- Connect to CRM
- Load files from specified folder which have been modified in give timeframe
- Process each file
- Check if file match to an existing Webresource
- If not
- Create the webresource according to the file extension
- Add webresource to given solution
- Else
- Check if webresource content has changed
- If content is different, update webresouce
- Publish modifications if changes have been detected
Prerequisites
The script couldn’t run properly without the following criterias:
- CRM user specified in configuration file need to be system administrator (create / update webresources, publish)
- You should provide an Assemblies folder that contains
- CRM SDK assemblies :
- Microsoft.Xrm.Sdk.dll
- Microsoft.Xrm.Client.dll
- Microsoft.Crm.Sdk.Proxy.dll
- CRM SDK assemblies :
- 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;
Comments
Anonymous
July 19, 2015
The comment has been removedAnonymous
July 21, 2015
Hi Chris, oups, I forgot this function in the script : function Get-Base64 { PARAM ( [parameter(Mandatory=$true)]$path ) $content = [System.IO.File]::ReadAllBytes($path); $content64 = [System.Convert]::ToBase64String($content); return $content64; } Sorry- Anonymous
May 05, 2016
Added the function and created a github repo for the fixed solution located here: https://github.com/BmanthaBernard/CRM2016PowershellDevOpsMad props to Aymeric. This saves me a lot of time while building angular apps for inside of CRM.
- Anonymous