Skip to content
Scott Durow edited this page Mar 11, 2022 · 22 revisions

Simple, No fuss, Dynamics 365 Deployment Task Runner

Build Status

Releases

See https://github.com/scottdurow/SparkleXrm/releases

Why?

I've used the Dynamics Developer Toolkit since it was first released by MCS for CRM4! I love the functionality it brings however the latest version is still in beta, it isn't supported on VS2017 and there isn't a date when it's likely to be either (yes, you can hack it to make it work but that's not the point J).

Rather than using an add-in Visual Studio project type, I've been attracted by the VS Code style simple project approach and so I decided to create a 'no-frills' alternative that uses a simple json config file (and that can be used in VS2017).

What?

  1. Deploy Plugins & Workflow Activities - Uses reflection to read plugin registration information directly from the assembly. This has the advantage that the plugin configuration is in the same file as the code. You can use the 'instrument' task to pull down the plugin configuration from Dynamics and add the metadata to your classes if you already have an existing project.
  2. Deploy Web Resources – deploy webresources from file locations defined in the spkl.json configuration. You can use the 'get-webresources' task to create the spkl.json if you already have webresources deployed. Generate Early Bound Types – Uses the spkl.json to define the entities to generate each time the task is run to make the process repeatable.
  3. Profile management – An optional profile can be supplied to select a different set of configuration from spkl.json. E.g. debug and release build profiles.

Video Tutorials

How?

Let's assume you have a project in the following structure:

Solution
    |-Webresources
    |    |-html
    |    |    |-HtmlPage.htm
    |    |-js
    |    |    |-Somefile.js
    |-Plugins
    |    |-MyPlugin.cs
    |-Workflows
    |    |-MyWorkflowActivity.cs

On both the Plugin and Workflows project, Run the following from the Nuget Console:

Install-Package spkl

This will add the spkl to the packages folder and the metadata CrmPluginConfigurationAttribute.cs that is used to mark up your classes so that spkl can deploy them. Some simple batch files are also included that you can use to get started.

If you already have plugins deployed, you can run the following command line in the context of the Plugins folder:

spkl instrument

This will prompt you for a Dynamics Connection, and then search for any deployed plugins and their matching .cs file. If the MyPlugin.cs plugin is already deployed it might end up with the following Attribute metadata:

[CrmPluginRegistration("Create","account",
    StageEnum.PreValidation,ExecutionModeEnum.Synchronous,
    "name,address1_line1", "Create Step",1,IsolationModeEnum.Sandbox,
    Description ="Description",
    UnSecureConfiguration = "Some config")]

You can also register plugins on custom actions - e.g.:

[CrmPluginRegistration(
        "dev1_TestAction",
        "account",
        StageEnum.PreOperation,
        ExecutionModeEnum.Synchronous,
        null,
        "dev1_TestAction",
        1000,
        IsolationModeEnum.Sandbox
        )]
public class TestActionPlugin : IPlugin

A spkl.json file will be created in the project directly similar to:

{
  "plugins": [
    {
      "solution": "Test",
      "assemblypath": "bin\\Debug"
    }
  ]
}

If you now build your plugins, you can then run the following to deploy

spkl plugins

You can run instrument for the workflow project using the same technique which will result in code similar to the following being added to your workflow activity classes:

[CrmPluginRegistration( "WorkflowActivity", "FriendlyName","Description", "Group Name",IsolationModeEnum.Sandbox)] …and then run the following to deploy:

spkl workflow

To get any currently deployed webresources matched to your project files you can run the following from the Webresource project folder:

spkl get-webresources /s:new

Where new is the solution prefix you've used

This will create a spkl.json similar to the following:

{
  "webresources": [
    {
      "root": "",
      "files": [
        {
          "uniquename": "new_/js/somefile.js",
          "file": "js\\somefile.js",
          "description": ""
        },
        {
          "uniquename": "new_/html/HtmlPage.htm",
          "file": "html\\HtmlPage.htm",
          "description": ""
        }
      ]
    }
  ]
}

You can then deploy using:

spkl webresources

If you have webresources already deployed but not inside your local project, you can download them and add to the spkl.json by using:

spkl download-webresources [/o]

Where the optional /o parameter controls if any existing files should be overwritten.

Mapping the solution pack to build artefacts

The solution packager map section is used to define where the webresources are located.

E.g.

"solutions": [
    {
      "profile": "2016_3",
      /*
      The unique name of the solution to extract, unpack, pack and import
      */
      "solution_uniquename": "Packager",
      /*
      The relative folder path to store the extracted solution metadata xml files
      */
      "packagepath": "2016_3",
      "solutionpath": "RibbonWorkbench2016_{0}_{1}_{2}_{3}_managed.zip",
      "packagetype": "managed",
      /*
      Map code artefacts to the solution package folder
      */
      "map": [
        {
          "map": "file",
          "from": "PluginAssemblies\\**\\RWB2016Plugins.dll",
          "to": "..\\..\\Plugins\\bin\\Release\\RWB2016.Plugins.dll"
        },
        {
          "map": "path",
          "from": "WebResources\\**\\*.*",
          "to": "..\\WebResources\\**"
        }
      ]
    }
  ]

Custom base classes

If you have plugin or workflow base classes different to the ones that spkl detects, you can change the regex that it uses by adding a spkl.json file similar to:

{
  "plugins": [
    {
      "solution": "Test",
      "assemblypath": "bin\\Debug",
      "classRegex": "((public( sealed)? class (?'class'[\\w]*)[\\W]*?)((?'plugin':[\\W]*?((IPlugin)|(PluginBase)|(Plugin)))|(?'wf':[\\W]*?CodeActivity)))"
    }
  ]
}

Early Bound Types

Wherever you can, it's best practice to use early bound types in your C# code. You can easily get spkl to generate these classes in a way that can be repeatedly run and refreshed when your schema changes.

Your spkl.json would look like:

{
  "earlyboundtypes": [
    {
      "entities": "account,contact,quote",
      "actions": "dev1_simpleaction",
      "generateOptionsetEnums": true,
      "generateStateEnums": true,
      "generateGlobalOptionsets": true,
      "filename": "EarlyBoundTypes.cs",
      "classNamespace": "TestPlugin",
      "serviceContextName": "XrmSvc"
    }
  ]
}
  • entities - Comma seperate list of entity logical names. I've not provided support for -all- entities because this results in unnecessarily large plugins!
  • actions - Comma separated list of actions request/responses to generate - leave empty or omit for none
  • generateOptionsetEnums - Set to 'true' to generate Enums for optionsets
  • generateStateEnums - Set to 'true' to generate Enums for States and Statuses
  • generateGlobalOptionsets - Set to 'true' to generate Enums for Global optionsets
  • filename - The path (relative to this file) to output
  • classNamespace - The namespace to put the classes under
  • serviceContextName - The name of the Service context to create - leave blank or omit for none

You would then refresh the classes file by calling

spkl earlybound

Solution Packager

The solution packager allows you to manage your Dynamics metadata inside a Visual Studio project by extracting the solution into separate xml files. When you need to combine multiple updates from code comments, you can then use the packager to re-combine and import into Dynamics.

To configure the solution packager task you can add the following to your spkl.json

 /*
  The solutions section defines a solution that can be extracted to individual xml files to make
  versioning of Dynamics metadata (entities, attributes etc) easier
  */
  "solutions": [
    {
      "profile": "default,debug",
      /*
      The unique name of the solution to extract, unpack, pack and import
      */
      "solution_uniquename": "spkltestsolution",
      /*
      The relative folder path to store the extracted solution metadata xml files
      */
      "packagepath": "package",
      /*
      Set to 'true' to increment the minor version number before importing from the xml files
      */
      "increment_on_import": false
    }
  ]

There are two .bat files provided that will call:

spkl unpack

This will extract the solution specifed in the spkl.json into the packagepath as multiple xml files

spkl import

This will re-pack the xml files and import into Dynamics - optionally increasing the version number of the solution to account for the new build.

Profiles

For Debug/Release builds you can define multiple profiles that can be triggered using the /p: parameter.

{
  "plugins": [
    {
      "profile": "default,debug",
      "assemblypath": "bin\\Debug"
    },
    {
      "profile": "release",
      "solution": "Test",
      "assemblypath": " bin\\Release"
    }
  ]
}

The default profile will be used if no /p: parameter is supplied. You can specify a profile using:

spkl plugins /p:release

Referencing a specific assembly rather than searching the folder If you have multiple plugins in a single deployment folder and you just want to deploy one, you can explicitly provide the path rather than using the folder search. E.g.

{
  "plugins": [
    {
      "assemblypath": "bin\\Debug\MyPlugin.dll"

Adding to a solution

If you'd like to automatically add the items deployed to a solution after deployment you can use:

{
  "webresources": [
    {
      "root": "",
      "solution": "Test",

Combining spkl.json

Perhaps you want to have a single spkl.json rather than multiple ones per project. You can simply add them all together:

{
  "webresources": […],
  "plugins": […]
}

Multiple project deployments

Since the spkl.json configuration files are searched from the current folder, you can deploy multiple plugins/webresources using a single spkl call from a root folder.

Passing a connectionstring with client secret to spkl

You can pass a connection string to the batch files, or to spkl.exe directly using the following syntax:

unpack "AuthType=ClientSecret;url=https://<YourOrg>.crm4.dynamics.com;ClientId=<AppID>;ClientSecret=<ClientSecret>"

or if you were calling spkl.exe directly you could use the following:

spkl.exe whoami path\to\project "AuthType=ClientSecret;url=https://<YourOrg>.crm4.dynamics.com;ClientId=<AppID>;ClientSecret=<ClientSecret>"

whoami will simply test the connection string, but you could equally pass unpack/plugins/workflows as the task name.

Stored Connections

Spkl stores the connections that you use for easy use next time. You can find these at the following location

C:\Users\<Your User>\AppData\Roaming\spkl\connections.json For legacy connections, they are stored at:

C:\Users\<Your User>\AppData\Roaming\CrmServer\Credentials.xml