XrmSync 1.0.0-preview.12

This is a prerelease version of XrmSync.
There is a newer version of this package available.
See the version list below for details.
dotnet tool install --global XrmSync --version 1.0.0-preview.12
                    
This package contains a .NET tool you can call from the shell/command line.
dotnet new tool-manifest
                    
if you are setting up this repo
dotnet tool install --local XrmSync --version 1.0.0-preview.12
                    
This package contains a .NET tool you can call from the shell/command line.
#tool dotnet:?package=XrmSync&version=1.0.0-preview.12&prerelease
                    
nuke :add-package XrmSync --version 1.0.0-preview.12
                    

XrmSync

Build Status NuGet GitHub release .NET License: MIT

A command-line tool for synchronizing Dataverse plugins, custom APIs, and webresources with your local code.

Overview

XrmSync is a powerful tool that helps you manage and synchronize your Microsoft Dataverse plugins, custom APIs, and webresources. It analyzes your plugin assemblies and local webresource files, compares them with what's deployed in Dataverse, and performs the necessary create, update, or delete operations to keep them in sync.

Features

  • Plugin Assembly Analysis: Automatically analyzes .NET assemblies to discover plugin types, steps, and images
  • Intelligent Synchronization: Compares local definitions with Dataverse and performs only necessary changes
  • Custom API Support: Handles custom API definitions, request parameters, and response properties
  • Webresource Sync: Synchronizes HTML, CSS, JavaScript, images, and other webresources from local folders
  • Dry Run Mode: Preview changes without actually modifying your Dataverse environment
  • Solution-aware: Deploys plugins and webresources to specific Dataverse solutions
  • Flexible Connection: Supports connection string and URL-based Dataverse connections
  • Configuration Files: Support for JSON configuration files to streamline repetitive operations
  • Named Configurations: Manage multiple environments (dev, staging, prod) in a single config file
  • Comprehensive Logging: Configurable logging levels for debugging and monitoring

Installation

As a .NET Tool (globally)

dotnet tool install --global XrmSync

As a .NET Tool (locally)

dotnet new tool-manifest # if you don't have a manifest file yet
dotnet tool install --local XrmSync

From Source

  1. Clone the repository:
git clone https://github.com/delegateas/XrmSync.git
cd XrmSync
  1. Build and pack the project:
dotnet build
dotnet pack XrmSync/XrmSync.csproj
  1. Install as a global tool:
dotnet tool install --global --add-source ./XrmSync/nupkg XrmSync

Usage

Plugin Synchronization

xrmsync plugins --assembly "path/to/your/plugin.dll" --solution-name "YourSolutionName"

Webresource Synchronization

xrmsync webresources --folder "path/to/webresources" --solution-name "YourSolutionName"

Configuration File Usage

For repeated operations or complex configurations, you can read the configuration from the appsettings.json file:

# Run all configured commands (plugins, webresources, analysis)
xrmsync --config default

# Run a specific command with configuration
xrmsync plugins --config default

You can also override specific options when using a configuration file:

xrmsync plugins --dry-run --log-level Debug

Command Line Options

Plugins Command
Option Short Description Required
--assembly -a Path to the plugin assembly (*.dll) Yes*
--solution-name -n Name of the target Dataverse solution Yes*
--dry-run Perform a dry run without making changes No
--log-level -l Set the minimum log level (Trace, Debug, Information, Warning, Error, Critical) No
--ci-mode --ci Enable CI mode which prefixes all warnings and errors No
--config -c Name of the configuration to load from appsettings.json No
--save-config --sc Save current CLI options to appsettings.json No
--save-config-to If --save-config is specified, override the filename to save to No

*Required when not present in appsettings.json

Webresources Command
Option Short Description Required
--folder -w, --path Path to the root folder containing the webresources to sync Yes*
--solution-name -n Name of the target Dataverse solution Yes*
--dry-run Perform a dry run without making changes No
--log-level -l Set the minimum log level (Trace, Debug, Information, Warning, Error, Critical) No
--ci-mode --ci Enable CI mode which prefixes all warnings and errors No
--config -c Name of the configuration to load from appsettings.json No
--save-config --sc Save current CLI options to appsettings.json No
--save-config-to If --save-config is specified, override the filename to save to No

*Required when not present in appsettings.json

Supported Webresource Types:

  • HTML files (.html, .htm)
  • CSS stylesheets (.css)
  • JavaScript files (.js)
  • XML data files (.xml)
  • Image files (.png, .jpg, .gif, .ico, .svg)
  • RESX string resources (.resx)
  • XSL stylesheets (.xsl, .xslt)

The webresource name in Dataverse is determined by the file path relative to the specified folder root, prefixed with the solution name. For example:

  • wwwroot/js/script.js → [prefix]_[solution]/js/script.js
  • wwwroot/css/styles.css → [prefix]_[solution]/css/styles.css
Analyze Command
Option Short Description Required
--assembly -a Path to the plugin assembly (*.dll) Yes*
--prefix -p Publisher prefix for unique names No (Default: "new")
--pretty-print --pp Pretty print the JSON output No
--save-config --sc Save current CLI options to appsettings.json No
--save-config-to If --save-config is specified, override the filename to save to No

*Required when not present in appsettings.json

Config Commands

Config Validate Command

Option Short Description Required
--config -c Name of the configuration to validate No (Default: "default")

Config List Command

Option Short Description Required
No options Lists all configurations from appsettings.json N/A

Assembly Analysis

You can analyze an assembly without connecting to Dataverse:

xrmsync analyze --assembly "path/to/your/plugin.dll" --pretty-print

You can also save analysis configurations:

xrmsync analyze --assembly "path/to/your/plugin.dll" --prefix "contoso" --pretty-print --save-config

This outputs JSON information about the plugin types, steps, and images found in the assembly.

Configuration Validation

You can validate your configuration files to ensure they are correctly set up:

# Validate the default configuration
xrmsync config validate

# Validate a specific named configuration
xrmsync config validate --config dev

The config validate command shows:

  • Which configuration file is being used (appsettings.json, appsettings.Development.json, etc.)
  • Resolved configuration values for each section (Plugin Sync, Plugin Analysis, Webresource Sync, Logger, Execution)
  • Validation status with specific errors if any
  • Available commands based on the configuration

Example output:

Configuration: 'default' (from appsettings.json)

✓ Plugin Sync Configuration
  Assembly Path: C:\path\to\plugin.dll
  Solution Name: MySolution

✓ Webresource Sync Configuration
  Folder Path: C:\path\to\webresources
  Solution Name: MySolution

✓ Logger Configuration
  Log Level: Information
  CI Mode: false

✓ Execution Configuration
  Dry Run: false

Available Commands: plugins, webresources

Validation: PASSED

You can also list all available configurations:

# List all named configurations
xrmsync config list

Example output:

Available configurations (from appsettings.json):

  - default
    ✓ Configured: plugins, webresources

  - dev
    ✓ Configured: plugins

  - prod
    ✗ No valid configurations

Configuration File Format

XrmSync supports JSON configuration files that contain all the necessary settings for synchronization and analysis. This is particularly useful for CI/CD pipelines or when you have consistent settings across multiple runs.

The configuration uses a hierarchical structure under the XrmSync section, separating sync and analysis options under Plugin.Sync and Plugin.Analysis respectively.

Generating Configuration Files

You can automatically generate configuration files using the --save-config option with any command:

Save sync options to appsettings.json (default)
xrmsync plugins --assembly "MyPlugin.dll" --solution-name "MyCustomSolution" --save-config
Save analysis options to appsettings.json
xrmsync analyze --assembly "MyPlugin.dll" --prefix "contoso" --pretty-print --save-config
Save to a custom file
xrmsync plugins --assembly "MyPlugin.dll" --solution-name "MyCustomSolution" --save-config --save-config-to "my-project.json"

When using --save-config, XrmSync will:

  1. Take all the provided CLI options
  2. Create or update the target configuration file
  3. Merge with existing content if the file already exists
  4. Save the configuration in the proper JSON format
JSON Schema
{
  "XrmSync": {
    "default": {
      "Plugin": {
        "Sync": {
          "AssemblyPath": "path/to/your/plugin.dll",
          "SolutionName": "YourSolutionName"
        },
        "Analysis": {
          "AssemblyPath": "path/to/your/plugin.dll",
          "PublisherPrefix": "contoso",
          "PrettyPrint": true
        }
      },
      "Webresource": {
        "Sync": {
          "FolderPath": "path/to/webresources",
          "SolutionName": "YourSolutionName"
        }
      },
      "Logger": {
        "LogLevel": "Information",
        "CiMode": false
      },
      "Execution": {
        "DryRun": false
      }
    }
  }
}
Named Configurations

XrmSync supports multiple named configurations within a single appsettings.json file. This allows you to manage different environments (dev, staging, prod) or different projects in one configuration file.

Using named configurations:

# Use the 'default' configuration (or the only configuration if only one exists)
xrmsync --config default

# Use a specific named configuration
xrmsync --config dev

# If --config is not specified, 'default' is used, or the single config if only one exists
xrmsync

Example with multiple named configurations:

{
  "XrmSync": {
    "default": {
      "Plugin": {
        "Sync": {
          "AssemblyPath": "bin/Debug/net462/MyPlugin.dll",
          "SolutionName": "DevSolution"
        }
      },
      "Execution": {
        "DryRun": true
      }
    },
    "prod": {
      "Plugin": {
        "Sync": {
          "AssemblyPath": "bin/Release/net462/MyPlugin.dll",
          "SolutionName": "ProdSolution"
        }
      },
      "Execution": {
        "DryRun": false
      }
    }
  }
}
Executing All Configured Sub-Commands

When you call the root command with a configuration name, XrmSync will automatically execute all configured sub-commands for that configuration:

# This will run plugin sync, plugin analysis, and webresource sync
# for all that are configured in the 'default' configuration
xrmsync --config default

XrmSync will only execute sub-commands that have their required properties configured. For example:

  • Plugin sync runs only if AssemblyPath and SolutionName are provided
  • Plugin analysis runs only if AssemblyPath is provided
  • Webresource sync runs only if FolderPath and SolutionName are provided
Plugin Sync Properties
Property Type Description Default
AssemblyPath string Path to the plugin assembly (*.dll) Required
SolutionName string Name of the target Dataverse solution Required
Plugin Analysis Properties
Property Type Description Default
AssemblyPath string Path to the plugin assembly (*.dll) Required
PublisherPrefix string Publisher prefix for unique names "new"
PrettyPrint boolean Pretty print the JSON output false
Webresource Sync Properties
Property Type Description Default
FolderPath string Path to the root folder containing webresources Required
SolutionName string Name of the target Dataverse solution Required
Logger Properties
Property Type Description Default
LogLevel string Log level (Trace, Debug, Information, Warning, Error, Critical) "Information"
CiMode boolean Enable CI mode for easier parsing in CI systems false
Execution Properties
Property Type Description Default
DryRun boolean Perform a dry run without making changes false
Example Configuration Files

Basic sync configuration:

{
  "XrmSync": {
    "default": {
      "Plugin": {
        "Sync": {
          "AssemblyPath": "MyPlugin.dll",
          "SolutionName": "MyCustomSolution"
        }
      }
    }
  }
}

Full configuration with plugins, webresources, and analysis:

{
  "XrmSync": {
    "default": {
      "Plugin": {
        "Sync": {
          "AssemblyPath": "bin/Release/net462/MyPlugin.dll",
          "SolutionName": "MyCustomSolution"
        },
        "Analysis": {
          "AssemblyPath": "bin/Release/net462/MyPlugin.dll",
          "PublisherPrefix": "contoso",
          "PrettyPrint": true
        }
      },
      "Webresource": {
        "Sync": {
          "FolderPath": "wwwroot",
          "SolutionName": "MyCustomSolution"
        }
      },
      "Logger": {
        "LogLevel": "Debug"
      },
      "Execution": {
        "DryRun": true
      }
    }
  }
}

Webresource-only configuration:

{
  "XrmSync": {
    "default": {
      "Webresource": {
        "Sync": {
          "FolderPath": "src/webresources",
          "SolutionName": "MyCustomSolution"
        }
      },
      "Execution": {
        "DryRun": false
      }
    }
  }
}

Analysis-only configuration:

{
  "XrmSync": {
    "default": {
      "Plugin": {
        "Analysis": {
          "AssemblyPath": "../../../bin/Debug/net462/ILMerged.SamplePlugins.dll",
          "PublisherPrefix": "contoso",
          "PrettyPrint": false
        }
      }
    }
  }
}

Examples

Plugin synchronization:
xrmsync plugins --assembly "MyPlugin.dll" --solution-name "MyCustomSolution"
Webresource synchronization:
xrmsync webresources --folder "wwwroot" --solution-name "MyCustomSolution"
Dry run for webresources:
xrmsync webresources --folder "wwwroot" --solution-name "MyCustomSolution" --dry-run
Using a configuration file to run all configured commands:
# Runs all configured sub-commands (plugin sync, analysis, webresource sync)
# from the 'default' configuration
xrmsync --config default

# Or simply (uses 'default' if it exists, or the only config if there's just one)
xrmsync
Using a specific named configuration:
xrmsync --config prod
Running a specific sub-command with configuration:
# Uses the configuration but only runs the plugins command
xrmsync plugins --config dev
Configuration file with CLI overrides:
xrmsync plugins --dry-run --log-level Debug
Dry run with debug logging:
xrmsync plugins --assembly "MyPlugin.dll" --solution-name "MyCustomSolution" --dry-run --log-level Debug

Configuration

Dataverse Connection

XrmSync utilizes the Dataverse Connection NuGet package to manage connections to your Dataverse environment. See the Dataverse Connection documentation for more details on how to configure connections.

Option Priority

When using configuration files with CLI options, the following priority order applies:

  1. CLI arguments (highest priority) - Override everything
  2. Configuration file values - Used when CLI arguments are not provided
  3. Default values (lowest priority) - Used when neither CLI nor config file specify a value

Logging

Configure logging levels using the --log-level option:

  • Trace: Most detailed logging
  • Debug: Detailed information for debugging
  • Information: General information (default)
  • Warning: Warning messages only
  • Error: Error messages only
  • Critical: Critical errors only

Azure DevOps Pipeline support

It is possible to run XrmSync in an Azure DevOps pipeline.

Below is an example YAML snippet to get you started:

parameters:
  - name: environment
    type: string
    default: Development

steps:
- task: NuGetToolInstaller@1
  displayName: 'NuGet tool installer'

- task: NuGetCommand@2
  displayName: 'NuGet restore'
  inputs:
    command: 'restore'
    restoreSolution: $(SolutionFile)

- task: DotNetCoreCLI@2
  displayName: 'Install dotnet tools'
  inputs:
    command: 'custom'
    custom: 'tool'
    arguments: 'restore'
    workingDirectory: '$(BasePath)/Tools'

- task: PowerPlatformToolInstaller@2
  displayName: 'Power Platform Tool Installer'

- task: PowerPlatformSetConnectionVariables@2
  displayName: 'Set Power Platform Connection Variables'
  name: connectionVariables
  inputs:
    authenticationType: 'PowerPlatformSPN'
    PowerPlatformSPN: ${{parameters.environment}}

- task: VSBuild@1
  displayName: 'Build solution'
  inputs:
    solution: $(SolutionFile)
    platform: '$(BuildPlatform)'
    configuration: '$(BuildConfiguration)'

- task: DotNetCoreCLI@2
  displayName: 'Validate Plugins'
  env:
    AZURE_CLIENT_ID: '$(connectionVariables.BuildTools.ApplicationId)'
    AZURE_CLIENT_SECRET: '$(connectionVariables.BuildTools.ClientSecret)'
    AZURE_TENANT_ID: '$(connectionVariables.BuildTools.TenantId)'
    DATAVERSE_URL: '$(BuildTools.EnvironmentUrl)'
  inputs:
    command: 'custom'
    custom: 'tool'
    arguments: 'run xrmsync plugins --dry-run --ci'
    workingDirectory: '$(BasePath)/Tools'

Project Structure

The solution consists of several projects:

  • XrmSync: Main command-line application
  • SyncService: Core synchronization logic and services
  • Dataverse: Dataverse connection and data access layer
  • AssemblyAnalyzer: Assembly analysis and reflection utilities
  • Model: Shared data models and entities

How It Works

Plugin Synchronization

  1. Assembly Analysis: The tool analyzes your plugin assembly to discover:

    • Plugin types and their attributes
    • Plugin steps (event handlers)
    • Plugin images (entity snapshots)
    • Custom API definitions
  2. Dataverse Comparison: It connects to your Dataverse environment and compares:

    • Existing plugin assemblies and their metadata
    • Current plugin registrations
    • Custom API configurations
  3. Synchronization: Based on the comparison, it performs:

    • Creates new plugin types and steps
    • Updates existing configurations
    • Removes obsolete registrations
    • Manages custom API definitions

Webresource Synchronization

  1. Local File Discovery: The tool scans your local folder to discover:

    • All files matching supported webresource types
    • File paths relative to the specified root folder
    • File content and MIME types
  2. Dataverse Comparison: It connects to your Dataverse environment and compares:

    • Existing webresources in the solution
    • Content hashes to detect changes
    • Webresource names and types
  3. Synchronization: Based on the comparison, it performs:

    • Creates new webresources
    • Updates modified webresources with new content
    • Removes webresources that no longer exist locally
    • Associates webresources with the specified solution

Development

Prerequisites

  • .NET 8 SDK
  • Visual Studio 2022 or VS Code
  • Access to a Dataverse environment for testing

Building

dotnet build

Testing

dotnet test
./scripts/Test-Samples.ps1 -SkipBuild

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests if applicable
  5. Submit a pull request

License

This project is licensed under the MIT License. See the LICENSE file for details.

Support

For issues, questions, or contributions, please visit the GitHub repository.


Copyright (c) 2025 Context& A/S

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

Version Downloads Last Updated
1.0.1 45 10/9/2026
1.0.0-preview.27 86 9/30/2026
1.0.0-preview.26 65 9/30/2026
1.0.0-preview.25 210 8/17/2026
1.0.0-preview.24 105 8/6/2026
1.0.0-preview.23 98 8/6/2026
1.0.0-preview.22 82 8/5/2026
1.0.0-preview.21 118 6/29/2026
1.0.0-preview.20 95 6/26/2026
1.0.0-preview.19 1,764 4/24/2026
1.0.0-preview.18 109 4/22/2026
1.0.0-preview.17 97 4/20/2026
1.0.0-preview.16 120 3/2/2026
1.0.0-preview.15 99 2/27/2026
1.0.0-preview.14 1,640 11/18/2025
1.0.0-preview.13 377 11/5/2025
1.0.0-preview.12 209 11/5/2025
1.0.0-preview.11 214 11/5/2025
1.0.0-preview.10 370 10/8/2025
1.0.0-preview.9 186 10/3/2025
Loading failed