XrmSync 1.0.0-preview.12
See the version list below for details.
dotnet tool install --global XrmSync --version 1.0.0-preview.12
dotnet new tool-manifest
dotnet tool install --local XrmSync --version 1.0.0-preview.12
#tool dotnet:?package=XrmSync&version=1.0.0-preview.12&prerelease
nuke :add-package XrmSync --version 1.0.0-preview.12
XrmSync
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
- Clone the repository:
git clone https://github.com/delegateas/XrmSync.git
cd XrmSync
- Build and pack the project:
dotnet build
dotnet pack XrmSync/XrmSync.csproj
- 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.jswwwroot/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:
- Take all the provided CLI options
- Create or update the target configuration file
- Merge with existing content if the file already exists
- 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
AssemblyPathandSolutionNameare provided - Plugin analysis runs only if
AssemblyPathis provided - Webresource sync runs only if
FolderPathandSolutionNameare 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:
- CLI arguments (highest priority) - Override everything
- Configuration file values - Used when CLI arguments are not provided
- 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 loggingDebug: Detailed information for debuggingInformation: General information (default)Warning: Warning messages onlyError: Error messages onlyCritical: 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
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
Dataverse Comparison: It connects to your Dataverse environment and compares:
- Existing plugin assemblies and their metadata
- Current plugin registrations
- Custom API configurations
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
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
Dataverse Comparison: It connects to your Dataverse environment and compares:
- Existing webresources in the solution
- Content hashes to detect changes
- Webresource names and types
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
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests if applicable
- 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 | Versions 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. |
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 |