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:
- CRM user specified in configuration file need to be system administrator (update assembly)
- You should provide an Assembliesfolder 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 privilèges
Process
- Scan assembly folder given in parameter
- Check if assembly need to be updated (regarding configuration : name and configuration)
- Update assembly
- 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;
Comments
Anonymous
July 17, 2015
Your "Download full script" button is pointing to the wrong script.Anonymous
July 17, 2015
Oups .. Thanks Nicolas, that should be ok now!Anonymous
October 22, 2015
Great article as usual !!!Anonymous
May 19, 2016
Nice script that saves me having to do the laborious plugin deployment dance, thanks.I had to remove the ToLower() on the configuration as my configuration path is 'Debug' not 'debug'.