Background

Not everybody has the same approach as we do (we make software solutions) while working with the MS CRM platform. Therefore you cannot always expect to have a perfect scenario where the blueprint of the solution is saved to the source control and from a specific tag, it’s deployed to both the TEST and PROD environments as a managed package. When we take over other consultancies soltions, we usually see that they just deploy the Default solution to both TEST and PROD as an unmananged package. There are a lot of reasons why this is not a best practice, but lets try to keep this blogpost positive.

Another issue that we sometimes meet is that a customer wants to upgrade to a newer version of MS CRM and they ask us if we can visualize how many changes there are from their solution compared to a Vanilla from the version they want to upgrade to.

Note: Vanilla is the name we use to denominate a MS CRM solution that is not modified with any configurations or customizations. In other words, standard out-of-the-box.

Back in the days, MS CRM 4.0, we had the Customization Comparison Utility which could load two MS CRM customization files and provide a visualization in order to see what the differences between the source and target are, if any:

This tool helped us a lot in the way that we could always point out to the customer that they had once again made a change to PROD environment whithout notifying us and therefore the change would be overwritten by the DEV package when deployed.

It also gave us the possibility to export all the differences to an Excel Spreadsheet, where it was a bit easier to make diagrams and other visualizations for better customer understanding.

Diff of solutions

Because we missed this tool a lot we decided to implement the logic as a module in Daxif in order to provide this functionality.

It’s very easy to use, lets take the example that we have just created a solution, see previous How to Daxif, basic setup blogpost, and we have now retrieved both the managed and unmananged solution and stored them in our source control:

DG.Delegate.HowToDaxif.SolutionExportDev.fsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#load @"DG.Delegate.HowToDaxif.Config.fsx"

module cfg = DG.Delegate.HowToDaxif.Config

open DG.Daxif.Modules

cfg.ensureFolder cfg.solutions

Solution.export
  cfg.wsdlDev' cfg.solution cfg.solutions false 
    cfg.authType cfg.usrDev cfg.pwdDev cfg.domainDev 
      cfg.log

Solution.export
  cfg.wsdlDev' cfg.solution cfg.solutions true 
    cfg.authType cfg.usrDev cfg.pwdDev cfg.domainDev 
      cfg.log

DG.Delegate.HowToDaxif.SolutionExtract.fsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#load @"DG.Delegate.HowToDaxif.Config.fsx"

module cfg = DG.Delegate.HowToDaxif.Config

open DG.Daxif.Modules

cfg.ensureFolder cfg.solutions

let map   = cfg.rootFolder + @"\..\Blueprint\DG.Delegate.HowToDaxif.xml"
let cms   = cfg.rootFolder + @"\..\Blueprint\customizations"
let vsSol = cfg.rootFolder + @"\..\Blueprint\Blueprint.csproj"

let zip = cfg.solutions + cfg.solution + @".zip"

Solution.extract
  cfg.solution
    zip cms map vsSol
      cfg.log

DG.Delegate.HowToDaxif.SolutionPack.fsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#load @"DG.Delegate.HowToDaxif.Config.fsx"

module cfg = DG.Delegate.HowToDaxif.Config

open DG.Daxif.Modules

cfg.ensureFolder cfg.solutions

let map  = cfg.rootFolder + @"\..\Blueprint\DG.Delegate.HowToDaxif.xml"
let cms  = cfg.rootFolder + @"\..\Blueprint\customizations"

let zipu = cfg.solutions + cfg.solution + @"_.zip"
let zipm = cfg.solutions + cfg.solution + @"_managed_.zip"

Solution.pack
  cfg.solution zipu cms map false cfg.log

Solution.pack
  cfg.solution zipm cms map true cfg.log

DG.Delegate.HowToDaxif.SolutionExportDefault.fsx

1
2
3
4
5
6
7
8
9
10
11
12
#load @"DG.Delegate.HowToDaxif.Config.fsx"

module cfg = DG.Delegate.HowToDaxif.Config

open DG.Daxif.Modules

cfg.ensureFolder cfg.solutions

Solution.export
  cfg.wsdlDev' @"Default" cfg.solutions false 
    cfg.authType cfg.usrDev cfg.pwdDev cfg.domainDev 
      cfg.log

Note: We also export the Default solution in order to compare to a Vanilla On-premise solution.

You should have the following solutions (you need to get a Vanilla unmananged from a trial On-Premise):

Now we can begin to play with the diff of solution module by comparing:

1) Unmananged vs managed

2) Unmananged vs Unmananged (GIT)

3) Default (Online Demo) vs Vanilla (On-Premise 2016)

4) …

1) DG.Delegate.HowToDaxif.SolutionDiff.fsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#load @"DG.Delegate.HowToDaxif.Config.fsx"

module cfg = DG.Delegate.HowToDaxif.Config

open DG.Daxif.Modules

cfg.ensureFolder cfg.solutions

let zipSource = cfg.solutions + cfg.solution + @".zip"
let zipTarget = cfg.solutions + cfg.solution + @"_managed" + @".zip"

Diff.summary zipSource zipTarget cfg.log |> ignore

Diff.solution zipSource zipTarget cfg.log

Note: Only difference is the managed flag

2) DG.Delegate.HowToDaxif.SolutionDiff.fsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#load @"DG.Delegate.HowToDaxif.Config.fsx"

module cfg = DG.Delegate.HowToDaxif.Config

open DG.Daxif.Modules

cfg.ensureFolder cfg.solutions

let zipSource = cfg.solutions + cfg.solution + @".zip"
let zipTarget = cfg.solutions + cfg.solution + @"_" + @".zip"

Diff.summary zipSource zipTarget cfg.log |> ignore

Diff.solution zipSource zipTarget cfg.log

Note: The only difference is that the assembly was rebuilt before packaging, therefore, even though there are no code differences, the hash code of the binary changes due to compilation strategies (non-deterministic) and therefore not producing the same binary.

3) DG.Delegate.HowToDaxif.SolutionDiff.fsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#load @"DG.Delegate.HowToDaxif.Config.fsx"

module cfg = DG.Delegate.HowToDaxif.Config

open DG.Daxif.Modules

cfg.ensureFolder cfg.solutions

let zipSource = cfg.solutions + @"Default.zip"
let zipTarget = cfg.solutions + @"Vanilla.zip"

Diff.summary zipSource zipTarget cfg.log |> ignore

Diff.solution zipSource zipTarget cfg.log

Note: In order to get a better view of the summary .CSV file, just convert to an Excel Spreadsheet, enable Data filters and apply the following filter on Source (does not contain a dot, which exclude all files paths)

More info: