Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
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