XRT.PowerApps.CLI
1.6.3
dotnet tool install --global XRT.PowerApps.CLI --version 1.6.3
dotnet new tool-manifest
dotnet tool install --local XRT.PowerApps.CLI --version 1.6.3
#tool dotnet:?package=XRT.PowerApps.CLI&version=1.6.3
nuke :add-package XRT.PowerApps.CLI --version 1.6.3
PowerApps.CLI
Note: This project was developed with assistance from Claude Sonnet 4.5 (Anthropic AI).
A .NET command-line tool for extracting and exporting metadata schema from Microsoft Power Platform / Dynamics 365 environments.
Features
Schema Extraction
- ๐ Metadata Export - Extract entity, attribute, and relationship metadata from Dataverse environments
- ๐ฏ Solution Filtering - Filter by one or multiple solutions (comma-separated)
- ๐ Multiple Export Formats:
- JSON - Complete schema with full metadata
- XLSX - Excel workbook with filterable tables and interactive navigation
- โ Audit Information - Includes audit enablement status at entity and attribute levels
Constants Generation
- ๐จ C# Constants - Generate strongly-typed C# constants from Dataverse metadata
- ๐ Tables & Choices - Modern terminology (Tables instead of Entities, Choices instead of OptionSets)
- ๐๏ธ Flexible Output:
- Single file mode: Tables.cs and Choices.cs
- Multiple files mode: Tables/.cs and Choices/.cs
- ๐ฏ Smart Filtering:
- Solution-based filtering
- Entity exclusions
- Attribute exclusions
- Prefix-based filtering
- ๐ Rich Documentation - XML comments and metadata comments in generated code
Reference Data Comparison
- ๐ Environment Comparison - Compare reference data tables between source and target environments
- ๐ Difference Detection - Identifies new, modified, and deleted records
- ๐ฏ Bidirectional Analysis - Compares both ways to find orphaned records
Reference Data Migration
- ๐ Environment-to-Environment Migration - Migrate reference data from source to target Dataverse environment
- ๐ Diff Mode - Only pushes records that have changed (default), with
--forceto push all - ๐ Multi-pass Strategy - Handles self-referential lookups via a two-pass upsert approach
- โ๏ธ State Management - Optionally sync record active/inactive state
- ๐ N:N Relationships - Sync many-to-many relationship associations and disassociations
- ๐งช Dry Run Mode - Preview all changes without writing to the target environment
- ๐ Excel Reporting - Summary and error report of all migration actions
Process Management
- โ๏ธ Process State Control - Activate/deactivate workflows, cloud flows, business rules, actions, business process flows, and duplicate detection rules
- ๐ฏ Pattern-based Rules - Use wildcard patterns to define which processes should be inactive
- ๐ CI/CD Ready - Run post-deployment to ensure processes are in the correct state
- ๐งช Dry Run Mode - Preview changes without modifying any process states
- ๐ Excel Reporting - Summary and detailed Excel report of all actions taken
Data Patch
- ๐ฉน Targeted Record Updates - Apply field-level updates to specific Dataverse records by key lookup
- ๐ Config File or Inline JSON - Supply config from a file (
--config) or as a JSON blob (--config-json) for pipeline use - ๐ Pipeline-friendly - Designed for Key Vault integration: retrieve a JSON secret and pipe it directly to the command
- ๐ Skip-if-unchanged - Reads the current value first; skips the update if it is already correct
- ๐ Excel Reporting - Per-patch outcome report: Updated / Unchanged / Not Found / Error
Solution Layer Analysis
- ๐ Unmanaged Layer Detection - Identifies solution components where an unmanaged layer sits above the managed solution layer, preventing deployment changes from taking effect
- ๐ Excel Report - Summary and detail sheets listing affected components by type and name with full layer stack
- ๐ Post-deployment Use - Run after a solution import to confirm changes will take effect
Installation
Prerequisites
- .NET 8.0 SDK or later
- Access to a Power Platform / Dynamics 365 environment
Build from Source
git clone https://github.com/yourusername/PowerApps.CLI.git
cd PowerApps.CLI
dotnet build -c Release
Usage
Schema Export
Extract metadata schema from Dataverse environments.
Using Pre-built Executable (Recommended)
After building or downloading a release, run directly:
# Windows
.\powerapps-cli.exe schema-export --url "https://yourorg.crm.dynamics.com" --output "schema.xlsx"
# Linux/macOS
./powerapps-cli schema-export --url "https://yourorg.crm.dynamics.com" --output "schema.xlsx"
Using dotnet run (Development)
When developing or if you prefer to run from source:
dotnet run --project src/PowerApps.CLI -- schema-export --url "https://yourorg.crm.dynamics.com" --output "schema.xlsx"
With Service Principal Authentication
powerapps-cli schema-export \
--url "https://yourorg.crm.dynamics.com" \
--client-id "your-client-id" \
--client-secret "your-client-secret" \
--solution "YourSolution" \
--output "schema.xlsx" \
--format xlsx
Multiple Solutions
powerapps-cli schema-export \
--url "https://yourorg.crm.dynamics.com" \
--solution "Solution1,Solution2,Solution3" \
--output "multi-solution-schema.json" \
--format json
Constants Generation
Generate C# constants from Dataverse metadata.
Basic Usage
powerapps-cli constants-generate \
--url "https://yourorg.crm.dynamics.com" \
--solution "YourSolution" \
--namespace "MyCompany.Model" \
--output "./Generated"
Using Connection String
powerapps-cli constants-generate \
--connection-string "AuthType=ClientSecret;Url=https://yourorg.crm.dynamics.com;ClientId=...;ClientSecret=..." \
--solution "YourSolution" \
--namespace "MyCompany.Model" \
--output "./Generated"
Single File Mode
powerapps-cli constants-generate \
--url "https://yourorg.crm.dynamics.com" \
--solution "YourSolution" \
--namespace "MyCompany.Model" \
--output "./Generated" \
--single-file
With Filtering
powerapps-cli constants-generate \
--url "https://yourorg.crm.dynamics.com" \
--solution "YourSolution" \
--namespace "MyCompany.Model" \
--output "./Generated" \
--exclude-entities "systemuser,team" \
--exclude-attributes "createdon,modifiedon,createdby,modifiedby" \
--attribute-prefix "rob_"
Using Configuration File
powerapps-cli constants-generate \
--url "https://yourorg.crm.dynamics.com" \
--solution "YourSolution" \
--config "./constants-config.json"
Example configuration file:
{
"SingleFile": false,
"IncludeEntities": true,
"IncludeGlobalOptionSets": true,
"IncludeComments": true,
"IncludeRelationships": true,
"PascalCaseConversion": true,
"AttributePrefix": "rob_",
"ExcludeAttributes": ["createdon", "modifiedon", "createdby", "modifiedby"],
"ExcludeEntities": ["systemuser", "team"]
}
Reference Data Comparison
Compare reference data between source and target environments.
Basic Usage
powerapps-cli refdata-compare \
--config refdata-config.json \
--source-url "https://dev.crm.dynamics.com" \
--target-url "https://test.crm.dynamics.com" \
--client-id "$CLIENT_ID" \
--client-secret "$CLIENT_SECRET" \
--output dev-vs-test.xlsx
Using Connection Strings
powerapps-cli refdata-compare \
--config refdata-config.json \
--source-connection "$DEV_CONNECTION_STRING" \
--target-connection "$TEST_CONNECTION_STRING" \
--output dev-vs-test.xlsx
Example Config File
{
"excludeSystemFields": true,
"globalExcludeFields": ["custom_ignorefield"],
"tables": [
{
"logicalName": "rob_category",
"primaryIdField": "rob_categoryid",
"primaryNameField": "rob_name",
"filter": "<filter><condition attribute='statecode' operator='eq' value='0'/></filter>",
"excludeFields": []
},
{
"logicalName": "rob_priority",
"primaryIdField": "rob_priorityid",
"primaryNameField": "rob_priorityname",
"filter": "<filter><condition attribute='statecode' operator='eq' value='0'/></filter>",
"excludeFields": ["rob_temporaryfield"],
"includeFields": []
},
{
"logicalName": "rob_item",
"includeFields": ["rob_name", "rob_categoryid", "rob_priorityid"]
}
],
"relationships": [
{
"relationshipName": "rob_category_priority",
"displayName": "Category to Priority",
"entity1NameField": "rob_name",
"entity2NameField": "rob_priorityname"
}
]
}
Tip: The
relationshipsconfig is compatible withrefdata-migrateโ you can use the same JSON file for both tools. See Shared Config below.
Behaviour:
includeFieldsrestricts comparison to only the specified fields (acts as an allowlist per table)excludeFieldsremoves specific fields from comparison- Relationship details are resolved automatically via metadata lookup โ only
relationshipNameis required displayName,entity1NameField,entity2NameFieldare optional overrides for report display
Output: Excel workbook with:
- Summary sheet showing all tables and relationship difference counts
- Detail sheets for each table with differences (NEW/MODIFIED/DELETED records)
- Detail sheets for each N:N relationship with differences (NEW/DELETED associations)
- Field-level comparison using formatted values (human-readable lookups and option sets)
- GUIDs resolved to display names for relationship associations
Reference Data Migration
Migrate reference data from a source Dataverse environment to a target environment.
Basic Usage
powerapps-cli refdata-migrate \
--config refdata-migrate-config.json \
--source-url "https://dev.crm.dynamics.com" \
--target-url "https://test.crm.dynamics.com" \
--client-id "$CLIENT_ID" \
--client-secret "$CLIENT_SECRET" \
--output migration-report.xlsx
Dry Run (Preview Changes)
powerapps-cli refdata-migrate \
--config refdata-migrate-config.json \
--source-url "https://dev.crm.dynamics.com" \
--target-url "https://test.crm.dynamics.com" \
--client-id "$CLIENT_ID" \
--client-secret "$CLIENT_SECRET" \
--dry-run \
--output migration-preview.xlsx
Using Connection Strings
powerapps-cli refdata-migrate \
--config refdata-migrate-config.json \
--source-connection "$DEV_CONNECTION_STRING" \
--target-connection "$TEST_CONNECTION_STRING" \
--output migration-report.xlsx
Force Full Sync
powerapps-cli refdata-migrate \
--config refdata-migrate-config.json \
--source-connection "$DEV_CONNECTION_STRING" \
--target-connection "$TEST_CONNECTION_STRING" \
--force \
--output migration-report.xlsx
Example Config File
{
"batchSize": 1000,
"tables": [
{
"logicalName": "rob_category",
"manageState": true
},
{
"logicalName": "rob_priority",
"filter": "<filter><condition attribute='statecode' operator='eq' value='0'/></filter>",
"excludeFields": ["rob_legacycode"],
"manageState": false
},
{
"logicalName": "rob_item",
"includeFields": ["rob_name", "rob_categoryid", "rob_priorityid"]
}
],
"relationships": [
{
"relationshipName": "rob_category_priority"
}
]
}
Behaviour:
- By default, only records that differ from the target are migrated (diff mode)
- Use
--forceto push all records regardless of whether they have changed - Lookups are applied in a second pass to handle self-referential relationships
manageState: truewill sync the active/inactive state of recordsincludeFieldsrestricts migration to only the specified fields (system fields always excluded)excludeFieldsremoves specific fields from migration- Relationship details are resolved automatically via metadata lookup โ only
relationshipNameis required
Output: Excel report with:
- Summary sheet showing environment info, totals, and per-table results
- Errors sheet (if any failures occurred) with table, record ID, phase, and error message
Shared Config
refdata-compare and refdata-migrate share the same config schema. A single JSON file can drive both tools โ each tool reads what it needs and silently ignores the rest.
{
"batchSize": 1000,
"excludeSystemFields": true,
"tables": [
{
"logicalName": "rob_category",
"primaryIdField": "rob_categoryid",
"primaryNameField": "rob_name",
"filter": "<filter><condition attribute='statecode' operator='eq' value='0'/></filter>",
"excludeFields": [],
"includeFields": [],
"manageState": true
}
],
"relationships": [
{
"relationshipName": "rob_category_priority",
"displayName": "Category to Priority",
"entity1NameField": "rob_name",
"entity2NameField": "rob_priorityname"
}
]
}
| Property | Compare | Migrate |
|---|---|---|
tables[].logicalName |
โ | โ |
tables[].filter |
โ | โ |
tables[].excludeFields |
โ | โ |
tables[].includeFields |
โ (allowlist) | โ (allowlist) |
tables[].manageState |
ignored | โ |
tables[].primaryIdField / primaryNameField / displayName |
โ | ignored |
relationships[].relationshipName |
โ (metadata lookup) | โ (metadata lookup) |
relationships[].displayName |
โ (report tab name) | โ (report tab name) |
relationships[].entity1NameField / entity2NameField |
โ (display names) | ignored |
batchSize |
ignored | โ |
excludeSystemFields / globalExcludeFields |
โ | ignored |
Process Management
Manage Dataverse process states (workflows, cloud flows, business rules, actions) to ensure correct activation/deactivation post-deployment.
Basic Usage
powerapps-cli process-manage \
--config process-config.json \
--url "https://prod.crm.dynamics.com" \
--client-id "$CLIENT_ID" \
--client-secret "$CLIENT_SECRET" \
--output process-report.xlsx
Dry Run (Preview Changes)
powerapps-cli process-manage \
--config process-config.json \
--url "https://prod.crm.dynamics.com" \
--client-id "$CLIENT_ID" \
--client-secret "$CLIENT_SECRET" \
--dry-run \
--output process-preview.xlsx
Using Connection String
powerapps-cli process-manage \
--config process-config.json \
--connection-string "$PROD_CONNECTION_STRING" \
--output process-report.xlsx
Example Config File
{
"solutions": ["Solution1", "Solution2"],
"inactivePatterns": [
"ZZ*",
"Test - *",
"Specific Process Name"
],
"maxRetries": 3
}
Behavior:
- Processes matching
inactivePatternsare deactivated - All other processes are activated
- Retry logic handles parent-child dependencies
- Wildcards supported in patterns (* matches any characters)
Output: Excel report with:
- Summary showing total, activated, deactivated, unchanged, and failed processes
- Detailed list of all processes with name, type, expected state, actual state, and action taken
Use Case: Run in CI/CD pipelines after deployment to ensure processes are in the correct state.
Data Patch
Apply targeted field-level updates to specific Dataverse records, driven by a config file or inline JSON blob. Designed for post-deployment pipelines where environment-specific values need patching (e.g. Power Pages authentication settings).
From a config file
powerapps-cli data-patch \
--config patch.json \
--connection-string "$SIT_CONNECTION_STRING" \
--output data-patch-report.xlsx
From an inline JSON blob (e.g. from Key Vault in a pipeline)
# Azure DevOps / bash pipeline step
JSON=$(az keyvault secret show --name "data-patch-sit" --vault-name "myvault" --query "value" -o tsv)
powerapps-cli data-patch \
--config-json "$JSON" \
--connection-string "$SIT_CONNECTION_STRING" \
--output data-patch-report.xlsx
Example Config File
{
"patches": [
{
"entity": "mspp_sitesetting",
"keyField": "mspp_name",
"key": "Authentication/OpenIdConnect/AzureADB2C/Authority",
"valueField": "mspp_value",
"value": "https://yourtenant.b2clogin.com/yourtenant.onmicrosoft.com/B2C_1A_Policy"
},
{
"entity": "mspp_sitesetting",
"keyField": "mspp_name",
"key": "Authentication/OpenIdConnect/AzureADB2C/ClientId",
"valueField": "mspp_value",
"value": "00000000-0000-0000-0000-000000000000"
}
]
}
Behaviour:
- Looks up each record by
keyField/key; errors if not found or if multiple records match - Reads the current
valueFieldvalue and skips the update if it already matches valuesupports JSON strings, numbers, and booleans โ type is inferred automatically- Use
"type"for fields that need explicit conversion:"date","datetime","guid","optionset"(wraps inOptionSetValue),"lookup"(value must be{ "logicalName": "...", "id": "..." }) --configand--config-jsonare mutually exclusive
Output: Excel report with one row per patch entry showing Entity, Key, Field, Old Value, New Value, and Status (Updated / Unchanged / Not Found / Ambiguous Match / Error).
Pipeline pattern:
1. Install solution
2. Retrieve JSON blob from Key Vault
3. powerapps-cli data-patch --config-json $json --connection-string $conn
One Key Vault secret per target environment, each containing the full JSON for that environment.
Solution Layers
Check for unmanaged layers on a solution's components post-deployment. Unmanaged layers prevent managed solution changes from taking effect.
Basic Usage
powerapps-cli solution-layers \
--solution "YourSolutionUniqueName" \
--url "https://yourorg.crm.dynamics.com" \
--client-id "$CLIENT_ID" \
--client-secret "$CLIENT_SECRET"
With Connection String
powerapps-cli solution-layers \
--solution "YourSolutionUniqueName" \
--connection-string "$PROD_CONNECTION_STRING" \
--output solution-layers.xlsx
Behaviour:
- Queries
msdyn_componentlayerfor all components belonging to the specified solution - A component is flagged when the topmost layer is
Active(the unmanaged customisations bucket) - Reports all affected components with their type, name, and full layer stack
Output: Excel workbook with:
- Summary sheet showing solution name, environment, date, and count of affected components
- Unmanaged Layers sheet with Component Type, Component Name, Unmanaged Layer, and full layer stack (bottom to top)
- Clean result message if no unmanaged layers are found
Pipeline pattern:
1. Install managed solution
2. powerapps-cli solution-layers --solution $solutionName --connection-string $conn
3. Review report โ any rows = someone has unmanaged customisations blocking your changes
Command Reference
schema-export
Extracts metadata schema from PowerApps/Dataverse environments.
Options
| Option | Description | Required | Default |
|---|---|---|---|
-u, --url |
PowerApps environment URL | Yes* | - |
-s, --solution |
Solution unique name(s) (comma-separated) | No | All entities |
-o, --output |
Output file path | No | powerapp-schema.json |
-f, --format |
Output format: json or xlsx |
No | json |
-c, --connection-string |
Dataverse connection string | No | - |
--client-id |
Azure AD Application Client ID | No | - |
--client-secret |
Azure AD Application Client Secret | No | - |
-v, --verbose |
Enable verbose output | No | false |
--attribute-prefix |
Only include attributes with this prefix | No | - |
--exclude-attributes |
Comma-separated attribute names to exclude | No | - |
* Either --url or --connection-string must be provided.
constants-generate
Generates C# constants from Dataverse metadata.
Options
| Option | Description | Required | Default |
|---|---|---|---|
-u, --url |
PowerApps environment URL | Yes* | - |
-s, --solution |
Solution unique name(s) to filter by | No | All entities |
-o, --output |
Output directory path | No | ./Generated |
-n, --namespace |
Root namespace for generated code | Yes | - |
--single-file |
Generate single Tables.cs and Choices.cs files | No | false |
--config |
Path to JSON configuration file | No | - |
-c, --connection-string |
Dataverse connection string | No | - |
--client-id |
Azure AD Application Client ID | No | - |
--client-secret |
Azure AD Application Client Secret | No | - |
-v, --verbose |
Enable verbose output | No | false |
--include-entities |
Include entity constants (Tables) | No | true |
--include-optionsets |
Include option set constants (Choices) | No | true |
--exclude-entities |
Comma-separated entity logical names to exclude | No | - |
--exclude-attributes |
Comma-separated attribute logical names to exclude | No | - |
--attribute-prefix |
Only include attributes with this prefix | No | - |
--pascal-case |
Convert identifiers to PascalCase | No | true |
* Either --url or --connection-string must be provided.
refdata-compare
Compares reference data between source and target Dataverse environments.
Options
| Option | Description | Required | Default |
|---|---|---|---|
--config |
Path to JSON configuration file | Yes | - |
--source-url |
Source environment URL | Yes* | - |
--target-url |
Target environment URL | Yes* | - |
--source-connection |
Source environment connection string | No | - |
--target-connection |
Target environment connection string | No | - |
--client-id |
Azure AD Client ID (for both environments) | No | - |
--client-secret |
Azure AD Client Secret (for both environments) | No | - |
-o, --output |
Output Excel file path | No | refdata-comparison.xlsx |
-v, --verbose |
Enable verbose output | No | false |
* Either --source-url/--target-url or --source-connection/--target-connection must be provided.
refdata-migrate
Migrates reference data from a source to a target Dataverse environment.
Options
| Option | Description | Required | Default |
|---|---|---|---|
--config |
Path to JSON configuration file | Yes | - |
--source-url |
Source environment URL | Yes* | - |
--target-url |
Target environment URL | Yes* | - |
--source-connection |
Source environment connection string | No | - |
--target-connection |
Target environment connection string | No | - |
--client-id |
Azure AD Client ID (for both environments) | No | - |
--client-secret |
Azure AD Client Secret (for both environments) | No | - |
--dry-run |
Preview mode โ no changes made to target | No | false |
--force |
Push all records regardless of whether they have changed | No | false |
-o, --output |
Output Excel report file path | No | migration-report.xlsx |
-v, --verbose |
Enable verbose output | No | false |
* Either --source-url/--target-url or --source-connection/--target-connection must be provided.
process-manage
Manages Dataverse process states (workflows, cloud flows, business rules, actions).
Options
| Option | Description | Required | Default |
|---|---|---|---|
--config |
Path to JSON configuration file | Yes | - |
--url |
Environment URL | Yes* | - |
--connection-string |
Environment connection string | No | - |
--client-id |
Azure AD Application Client ID | No | - |
--client-secret |
Azure AD Application Client Secret | No | - |
--dry-run |
Preview changes without modifying states | No | false |
-o, --output |
Output Excel report file path | No | process-report.xlsx |
-v, --verbose |
Enable verbose output | No | false |
* Either --url or --connection-string must be provided.
solution-layers
Reports unmanaged layers on a solution's components post-deployment.
Options
| Option | Description | Required | Default |
|---|---|---|---|
-s, --solution |
Unique name of the solution to inspect | Yes | - |
--url, -u |
Environment URL | Yes* | - |
--connection-string |
Environment connection string | No | - |
--client-id |
Azure AD Application Client ID | No | - |
--client-secret |
Azure AD Application Client Secret | No | - |
-o, --output |
Output Excel report file path | No | solution-layers.xlsx |
-v, --verbose |
Enable verbose output | No | false |
* Either --url or --connection-string must be provided.
Output Formats
Schema Export
JSON
Complete schema export with all metadata including:
- Entity definitions with audit settings
- Attribute metadata with types, constraints, and audit settings
- Relationships (1:N and N:N)
- OptionSets with all options
- Solution provenance information
XLSX (Excel)
Interactive Excel workbook featuring:
- Summary Sheet:
- Environment and solution metadata
- Filterable table of all entities
- Clickable hyperlinks to entity detail sheets
- Entity Detail Sheets: One per entity with:
- Entity properties and audit settings
- Filterable table of attributes
- Attributes Sheet: Complete list of all attributes across all entities
- Relationships Sheet: All entity relationships
The XLSX export includes:
- โ Excel Tables with filter dropdowns on all data sheets
- ๐ Interactive Navigation - Click entity names to jump to detail sheets
- ๐ Statistics - Entity, attribute, and relationship counts
- ๐จ Professional Formatting - Color-coded headers and styled tables
- ๐ Audit Information - "Is Audit Enabled" columns for entities and attributes
Constants Generation
Multiple Files Mode (Default)
Generated structure:
Generated/
โโโ Tables/
โ โโโ Account.cs
โ โโโ Contact.cs
โ โโโ ... (one file per entity)
โโโ Choices/
โโโ AccountType.cs
โโโ StatusCode.cs
โโโ ... (one file per global option set)
Example generated file:
namespace MyCompany.Model.Tables
{
/// <summary>
/// Constants for the Account entity.
/// </summary>
public static class Account
{
/// <summary>
/// Logical name of the entity.
/// </summary>
public const string EntityLogicalName = "account";
/// <summary>
/// Primary ID attribute.
/// </summary>
public const string PrimaryIdAttribute = "accountid";
/// <summary>
/// name (String) - MaxLength: 160
/// </summary>
public const string Name = "name";
/// <summary>
/// accountcategorycode (Picklist) - Uses local option set
/// </summary>
public const string Category = "accountcategorycode";
/// <summary>
/// Category option set values.
/// </summary>
public static class CategoryOptions
{
/// <summary>
/// Preferred Customer
/// </summary>
public const int PreferredCustomer = 1;
/// <summary>
/// Standard
/// </summary>
public const int Standard = 2;
}
}
}
Single File Mode
Generates two files:
Tables.cs- All entity constants in one fileChoices.cs- All global option set constants in one file
Architecture
Commands/
โโโ SchemaCommand.cs # Schema export CLI command
โโโ ConstantsCommand.cs # Constants generation CLI command
โโโ RefDataCompareCommand.cs # Reference data comparison CLI command
โโโ RefDataMigrateCommand.cs # Reference data migration CLI command
โโโ ProcessManageCommand.cs # Process management CLI command
โโโ DataPatchCommand.cs # Data patch CLI command
โโโ SolutionLayersCommand.cs # Solution layer analysis CLI command
Services/
โโโ SchemaService.cs # Schema export orchestration
โโโ SchemaExtractor.cs # Metadata extraction with solution filtering
โโโ SchemaExporter.cs # Export to JSON/XLSX formats
โโโ ConstantsGenerator.cs # Constants generation orchestration
โโโ CodeTemplateGenerator.cs # C# code template generation
โโโ ConstantsFilter.cs # Entity/attribute filtering logic
โโโ IdentifierFormatter.cs # C# identifier formatting (PascalCase, sanitization)
โโโ MetadataMapper.cs # SDK to model mapping
โโโ IRecordComparer.cs # Record comparison interface
โโโ RecordComparer.cs # Record and association comparison logic
โโโ IComparisonReporter.cs # Comparison report interface
โโโ ComparisonReporter.cs # Comparison report Excel generation
โโโ IRefDataMigrator.cs # Reference data migrator interface
โโโ RefDataMigrator.cs # Reference data migration logic
โโโ IMigrationReporter.cs # Migration reporter interface
โโโ MigrationReporter.cs # Migration report Excel generation
โโโ IProcessManager.cs # Process management interface
โโโ ProcessManager.cs # Process state management logic
โโโ ProcessReporter.cs # Process report Excel generation
โโโ IDataPatchReporter.cs # Data patch reporter interface
โโโ DataPatchReporter.cs # Data patch report Excel generation
โโโ ISolutionLayerService.cs # Solution layer service interface
โโโ SolutionLayerService.cs # Solution layer querying and unmanaged layer detection
โโโ ISolutionLayerReporter.cs # Solution layer reporter interface
โโโ SolutionLayerReporter.cs # Solution layer report Excel generation
Infrastructure/
โโโ DataverseClient.cs # Dataverse connection management
โโโ FileWriter.cs # File I/O abstraction
โโโ ConsoleLogger.cs # Logging implementation
Models/
โโโ PowerAppsSchema.cs # Root schema model
โโโ EntitySchema.cs # Entity metadata
โโโ AttributeSchema.cs # Attribute metadata
โโโ RelationshipSchema.cs # Relationship metadata
โโโ OptionSetSchema.cs # OptionSet metadata
โโโ ConstantsConfig.cs # Constants generation configuration
โโโ ConstantsOutputConfig.cs # Constants output settings
โโโ RefDataCompareConfig.cs # Reference data comparison configuration
โโโ ComparisonResult.cs # Table comparison result models
โโโ RelationshipComparisonResult.cs # N:N relationship comparison models
โโโ RefDataMigrateConfig.cs # Reference data migration configuration
โโโ RefDataMigrateModels.cs # Migration result models
โโโ ProcessManageConfig.cs # Process management configuration
โโโ ProcessManageModels.cs # Process state models
โโโ DataPatchConfig.cs # Data patch configuration
โโโ DataPatchModels.cs # Data patch result models
โโโ SolutionLayerResult.cs # Solution layer analysis result models
Testing
The project includes unit tests covering schema extraction, constants generation, reference data comparison, and process management.
Run Tests
dotnet test
Run Tests with Coverage
# Using test-scripts helper
.\tests\scripts\run-coverage.ps1
# Or manually
dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=cobertura
reportgenerator -reports:"tests/PowerApps.CLI.Tests/TestResults/coverage.cobertura.xml" \
-targetdir:"TestResults/CoverageReport" -reporttypes:"Html;TextSummary"
Current test coverage:
- 330+ passing tests (100% pass rate)
- Line coverage: 60%+
- Branch coverage: 55%+
Test coverage includes:
- โ Schema extraction and export (JSON/XLSX)
- โ Constants generation (single/multiple file modes)
- โ Code template generation
- โ Identifier formatting and sanitization
- โ Entity/attribute filtering
- โ Metadata mapping
- โ Model validation
- โ Command orchestration (all 7 commands)
- โ Reference data comparison (table records, N:N relationships, name resolution)
- โ Reference data migration (all 4 passes, diff/force modes, column filtering, N:N sync)
- โ Process management (pattern matching, retry logic, state determination)
- โ Data patch (lookup, skip-if-unchanged, update, error handling)
- โ Solution layer analysis (unmanaged layer detection, component type mapping, clean result handling)
Development
Project Structure
src/PowerApps.CLI/- Main application codeCommands/- CLI command definitionsServices/- Business logic and orchestrationInfrastructure/- External integrations and utilitiesModels/- Data models and schemas
tests/PowerApps.CLI.Tests/- Unit teststests/scripts/- Local test scripts with sample usage (credentials not committed)
Dependencies
- Microsoft.PowerPlatform.Dataverse.Client - Dataverse SDK
- ClosedXML - Excel file generation
- System.CommandLine - CLI framework
- xUnit - Testing framework
- Moq - Mocking library
Contributing
Contributions are welcome! Please ensure:
- All tests pass
- New features include unit tests
- Code follows existing patterns and conventions
License
MIT License - see LICENSE file for details.
This project is provided as-is with no warranties. Feel free to use, modify, and distribute as needed.
| 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.