Loading...
Exalents
Back to ALM Services

Case Study · Power Platform ALM

A Production-Grade ALM Pipeline for a Multi-Solution Dataverse Platform

How we designed and built a fully automated, secretless CI/CD system for four interconnected Dataverse solutions spanning preview, integration, test, and production environments, using GitHub Actions, OIDC federated auth, and a dual deployment model.

4
Dataverse Solutions
9
Deployment Environments
30+
PowerShell Scripts
13
GitHub Actions Workflows
10
Agent ALM Skills
225+
Dataverse Tables

The Challenge

Enterprise Power Platform projects frequently outgrow the tooling assumptions baked into the platform's own guidance. Managing a single solution in a single environment is well-documented. Managing four interdependent solutions across three vertical industry lines, each with its own preview, preview-test, dev, test, and production environments, is an entirely different engineering problem.

The client needed to deliver a laboratory management platform spanning core laboratory science, DOT compliance workflows, veterinary diagnostics, and analytics. The constraints were demanding:

Multi-solution dependency tree

Two vertical solutions extend the core, so every deployment must respect transitive solution dependencies and ordering.

Code-first and low-code in one pipeline

C# Dataverse plugins and TypeScript/React PCF controls must be compiled, tested, and packaged together with declarative solution metadata in a single build.

Nine target environments

Each vertical has its own preview-test, test, and production environment. A secrets-based approach to service identities would not scale safely across 9+ environments.

Feature isolation during development

Developers needed to work independently in feature solutions without contaminating the integration milestone or blocking release trains.

Platform Overview

The platform is a Microsoft Dataverse monorepo combining four distinct technical layers: solution metadata (XML), server-side plugins (C#/.NET), client-side UI controls (TypeScript + React + Fluent UI), and ALM/deployment automation (PowerShell + GitHub Actions).

SolutionVerticalTablesRole
slp_LabSciCore~122Shared laboratory domain model: requests, samples, tests, results, equipment, specifications, metrology
dot_DOTSciDOT Compliance~60Plants, pay items, facility templates, materials; depends on LabSci
vet_VetSciVeterinary~43Patient, species, breed, biochemicals, instrument raw results; depends on LabSci
ins_InsightSciAnalytics4Reporting and analytics metadata; standalone
Microsoft DataversePower PlatformC# / .NETTypeScript / ReactPCF ControlsGitHub ActionsPowerShellOIDC Authpac CLI

The ALM Architecture

The central design decision was to separate development-time iteration from release-time promotion into two deliberately distinct deployment mechanisms. This mirrors the Microsoft Learn Power Platform guidance, but extends it considerably in automation depth.

Dual deployment model: The inner loop uses pac solution import for fast, unmanaged feature iteration. The outer loop uses pac package deploy for versioned, managed release bundles. Mixing the two is an explicit architectural anti-pattern caught by code review and CI enforcement.

Inner Loop - Daily Development

1

Preview env (unmanaged)

Developer iterates in feature solution

2

Feature branch (Git)

Sync workflow exports and unpacks to branch

3

Preview-test env (managed)

Build + compile + pac solution import

4

Dev env (integration)

Transport components to main solution

Outer Loop - Release & Promotion

1

develop → main (PR)

Triggers release package build

2

Build all solutions

Compile, version (YYYY.MM.DD.N), package

3

GitHub Release

8 ZIP artifacts + per-env settings files

4

Target environments

Manual dispatch + approval gate deployment

Key Technical Decisions

OIDC Federated Auth — No Secrets

All CI/CD authentication uses GitHub OIDC federated credentials rather than client secrets. Each environment has its own app registration. Tokens are short-lived and scoped to the run — no long-lived secrets anywhere.

Settings Pipeline: Templates → Merged Files

Deployment settings are generated in three stages: sync produces templates, human-maintained JSON files hold per-environment values, and build time merges all three into final settings files for each deployment target.

14-Stage Hook System

An extensible pipeline hook system defines 14 lifecycle stages. Hooks are PowerShell scripts discovered by naming convention and invoked with context parameters. Post-deploy config imports and custom logic plug in without modifying core scripts.

Plugin Registration Without PRT

Replaced the GUI-only Plugin Registration Tool with a scriptable PowerShell workflow that pushes binaries, registers message processing steps, images, and custom APIs — all repeatable and auditable.

PR Validation with Change Detection

CI identifies which plugins and PCF controls actually changed in a PR. Only touched projects compile and test. A second script computes transitive solution dependencies to validate downstream impact.

Feature Transport Pre-Flight Checks

Before transporting a feature solution from preview to dev, validation verifies that every environment variable and connection reference has values configured for all target environments.

Code-First Extensions

The platform treats declarative Dataverse capabilities as the default but extends them with two code-first layers wherever platform limits are reached.

Plugin Suite (C#)

Eleven plugin projects for the core platform plus one for veterinary, sharing a common base library. Domains include calculation engine, label generation, metrology, specification enforcement, order management, and custom API implementations. Each targets .NET Framework 4.6.2 with xUnit tests.

PCF Control Suite (TypeScript / React)

Thirteen or more PCF controls using TypeScript, React 16, and Fluent UI 8. A shared utility library uses workspace references for cross-package reuse. Controls are compiled via pcf-scripts and tested with Jest.

“Replacing the Plugin Registration Tool with a scriptable CLI workflow was one of the highest-leverage changes in the inner loop. What was a manual, GUI-only step became a repeatable, auditable automation.”

Agents in the Loop

AI agents don't just generate code.They run the pipeline.

Beyond the automation pipeline itself, this implementation ships a set of agent-directed workflow skills built for VS Code Copilot. Each skill encodes the full operational knowledge for one ALM task: checklists, parameter gathering, decision trees, environment URLs, and validation steps. A developer can initiate any part of the ALM lifecycle with a natural language prompt rather than reading documentation and manually invoking scripts.

Skills are plain Markdown files checked into the repository. When invoked, the AI agent follows the skill procedure, asks only for missing inputs, selects the right script, constructs the correct command, and surfaces results. Developers stop context-switching between docs, terminals, and browser tabs.

How skills are invoked: A developer types a natural language phrase in VS Code Copilot Chat (for example, “start feature AB12345 add customer validation”) and the matching skill activates automatically. The agent gathers any missing parameters and runs the appropriate workflow or script.

10 ALM Lifecycle Skills

start-feature

"start feature AB#### …"

Creates the feature branch, initializes a Dataverse feature solution, and sets it as the preferred solution so all GUI changes are auto-tracked.

sync-solution

"sync solution from dev"

Exports a solution from a Dataverse environment, unpacks metadata, and commits to source control. Handles both inner-loop and outer-loop contexts.

build-solution

"build and deploy to preview-test"

Pre-builds changed plugins and PCF controls, then runs the solution build to produce managed and unmanaged ZIPs. Optionally deploys in a single step.

deploy-solution

"deploy to preview-test"

Inner-loop managed deployment using pac solution import with generated settings. Covers both unmanaged and managed imports.

transport-solution

"transport AB12345 to dev"

Runs pre-transport validation to catch missing environment variable values, then moves feature components from preview to the dev integration environment.

create-release

"create a release"

Guides the developer through merging develop → main, monitors the automated release build, and initiates deployment to the first target environment.

deploy-package

"deploy LabSci to labsci-test"

Outer-loop release deployment using pac package deploy. Validates the package-to-environment match, downloads the correct artifact, and triggers dispatch.

scaffold-plugin

"scaffold a plugin for solution"

Creates a new SDK-style .csproj targeting net462, derives from PluginBase, and wires the project into the solution and Visual Studio solution file.

scaffold-pcf-control

"scaffold a PCF control for vet_VetSci"

Runs pac pcf init with correct namespace, sets up TypeScript/React manifest and Jest config, and wires the control into the .cdsproj solution reference.

register-plugin

"register the CalcEngine plugin in preview"

Pushes the compiled plugin binary and registers message processing steps, images, and custom APIs — replacing the GUI Plugin Registration Tool entirely.

A Complete Inner Loop Session

The following illustrates a typical developer session from feature kickoff to integration, conducted entirely through agent prompts:

VS Code Copilot Chat - inner loop session

// Day 1 - start the feature

👤 start feature AB12345 add result approval workflow

🤖 Created branch feat/AB12345_AddResultApprovalWorkflow from develop. Feature solution AB12345_ResultApproval created in labsci-preview and set as your preferred solution.

// Day 2 - after adding tables and flows in Dataverse studio

👤 sync slp_LabSci from preview

🤖 Exported slp_LabSci from labsci-preview, unpacked to feat/AB12345_AddResultApprovalWorkflow. 12 files changed. Settings template refreshed.

// Plugin implementation complete

👤 scaffold a plugin ResultApproval for slp_LabSci

🤖 Created src/plugins/slp_LabSci/slp_SynapticSci.Plugins.LabSci.ResultApproval. Wired into slp_LabSci.sln and slp_LabSci.cdsproj.

👤 register the ResultApproval plugin in preview

🤖 Built assembly. Registered in labsci-preview: 1 package, 2 steps (Post-Create slp_result, Post-Update slp_result). Ready to test.

// Validation passed - promote to integration

👤 build slp_LabSci and deploy to preview-test

🤖 Pre-built plugins and controls. Built managed + unmanaged ZIPs. Deployed to labsci-preview-test via pac solution import. Deployment settings applied.

👤 transport AB12345 to dev

🤖 Pre-transport validation passed (all environment variables and connection references have values). Copied 14 components to slp_LabSci in labsci-dev. Opened PR feat/AB12345_AddResultApprovalWorkflow → develop for code-first changes.

Each step executes the appropriate script with correct parameters, validates preconditions, and surfaces errors immediately, with no manual documentation lookup required. The same skills run identically in GitHub Actions workflows, so the developer experience and the CI pipeline share the same operational knowledge base.

Outcomes

Fully repeatable builds

Every solution build is driven from source control. No manual export steps from dev environments in the release path.

Zero long-lived secrets for Dataverse

OIDC federated credentials issue short-lived tokens at workflow runtime. No client secrets stored for any of the nine target environments.

Developer autonomy without merge conflicts

Feature solutions in preview give each developer isolated scope. Transport workflows carry only relevant components into integration.

Single command deployments

One workflow dispatch deploys any package group to any environment. Approval gates enforce the test → production promotion policy.

Consistent multi-environment settings

The merge pipeline guarantees every deployment carries correct connection references and environment variable values.

Locally runnable scripts

Every workflow is a thin wrapper over PowerShell runnable on a developer machine. Only OIDC wrappers require CI context.

Technology Stack

LayerTechnologyPurpose
Solution metadataDataverse XML + AlbanianXrm CDSProj SDKModel-driven apps, tables, forms, views, flows, custom APIs
PluginsC# / .NET Framework 4.6.2Server-side business logic, calculation engine
UI controlsTypeScript + React + Fluent UI + PCFCustom field and dataset controls
CI/CDGitHub Actions (13 workflows)PR validation, sync, transport, release, deploy
AutomationPowerShell 7 (30+ scripts)Shared logic for local and CI workflows
CLIpac (Power Platform CLI)Solution import/export, package deploy, PCF push
AuthGitHub OIDC + Azure federated credentialsSecretless CI authentication
DeploymentPower Platform Package DeployerOuter-loop bundled solution deployment
TestingxUnit (plugins) + Jest (controls)Automated unit testing in PR validation

Ready to build something like this?

Whether you're managing four solutions or forty, we can design an ALM system that matches your platform, not the other way around.