Skip to main content

Command Palette

Search for a command to run...

Dynamics 365 Customer Service ALM

Updated
9 min readView as Markdown

Building a Dynamics 365 Customer Service ALM Project with C# Plug-ins, Power Automate and Azure DevOps

I recently built a hands-on Dynamics 365 Customer Service project to practise the complete development and Application Lifecycle Management process — from Dataverse customisation and C# plug-in development to Power Automate, external API integration, unit testing and automated deployment using Azure DevOps.

Instead of focusing only on configuration, my goal was to understand how the different parts of a real Dynamics 365 solution work together across development and deployment environments.

The project is available on GitHub:

GitHub: https://github.com/sangeetha2402-ravichandran/Dynamics365-CustomerService-ALM

The main areas I wanted to cover were:

  • Dynamics 365 Customer Service customisation

  • Microsoft Dataverse

  • C# plug-in development

  • Plug-in execution pipeline

  • Pre Images

  • Custom Workflow Activities

  • Power Automate

  • Email-to-Case automation

  • External REST API integration

  • Retry and error handling

  • Environment Variables

  • Connection References

  • Managed and unmanaged solutions

  • Unit testing

  • Service Principal authentication

  • Azure DevOps CI/CD

  • DEV → TEST deployment

  • Patch, Update and Upgrade strategies

High-Level Architecture

The project follows this general flow:

Dynamics 365 Customer Service
        |
        +-- Microsoft Dataverse
        |
        +-- C# Plug-ins
        |      |
        |      +-- Case creation logic
        |      +-- Priority handling
        |      +-- Follow-up task creation
        |      +-- External API integration
        |
        +-- Power Automate
        |      |
        |      +-- Case escalation
        |      +-- Incoming Email -> Case
        |      +-- Approval process
        |      +-- External API integration
        |
        +-- Environment Variables
        +-- Connection References
        |
        v
Azure DevOps Pipeline
        |
        +-- Build
        +-- Unit Test
        +-- Export Managed Solution
        +-- Manual Approval
        +-- Deploy to TEST

C# Plug-in Development

I created multiple Dataverse plug-ins to understand different stages of the plug-in execution pipeline.

Case Creation Plug-in

The first plug-in runs when a Case is created.

I used it mainly to practise:

  • IPlugin

  • IPluginExecutionContext

  • Target Entity

  • PreOperation

  • Synchronous execution

  • Updating fields before Dataverse creates the record

Using PreOperation means I can modify the Target entity before Dataverse saves it, without making another update request afterwards.

Detecting Priority Changes Using a Pre Image

Another scenario was detecting when the Case priority changes from Normal to High.

For this, I registered a Pre Image containing the previous Case values.

The basic logic is:

Previous Priority = Normal
          |
          v
New Priority = High
          |
          v
Execute business logic

This helped me understand an important Dataverse concept: during an Update operation, the Target does not necessarily contain every attribute on the record.

The Pre Image allows the plug-in to compare the previous value with the new value.

Creating a Follow-up Task

I also created a PostOperation plug-in which creates a Task when a Case becomes High priority.

The generated task contains information such as:

Subject: Follow up high priority case
Regarding: Current Case
Priority: High
Due Date: +1 day

This scenario helped me practise:

  • IOrganizationService

  • Creating Dataverse records

  • Entity references

  • Regarding lookups

  • Owner assignment

  • PostOperation execution

External REST API Integration

Another plug-in calls an external REST API when a Case is created.

I used HttpClient, processed the JSON response and updated the Case with information returned from the API.

For this type of integration, I also looked at:

  • Asynchronous plug-in execution

  • API timeout handling

  • External service failures

  • JSON response processing

For enterprise implementations, I would carefully consider whether the external integration should run directly from a plug-in or asynchronously through services such as Azure Functions or messaging, depending on reliability, latency and business requirements.

Power Automate — High Priority Case Escalation

I created a Power Automate flow for Case escalation.

The flow works approximately like this:

Case Priority becomes High
        |
        v
Create Follow-up Task
        |
        v
Wait
        |
        v
Read latest Case
        |
        v
Still High + Active + Not Escalated?
        |
       Yes
        |
        +--> Send escalation notification
        |
        +--> Mark Case as escalated

One thing I wanted to avoid was assuming that the Case was still High priority after waiting.

Therefore, the flow retrieves the latest Case information before performing the escalation.

That makes the automation safer when the Case has already been resolved or its priority has changed.

Email to Existing Case or New Case

I also created an email-driven Case flow.

When an email arrives, the process:

Incoming Email
      |
      v
Find Contact using sender email
      |
      +---- Contact Found
      |          |
      |          v
      |     Find Active Case
      |          |
      |          +---- Case Found -> Add Note
      |          |
      |          +---- No Case -> Create Case
      |
      +---- Contact Not Found -> Send Notification

This scenario gave me practical experience with:

  • Outlook triggers

  • Dataverse List rows

  • OData filters

  • first()

  • length()

  • Contact lookups

  • Case lookups

  • Notes / annotations

  • Email-driven Case processing

Approval and Error Handling

For High priority Cases, I also created an approval process.

I used Scopes to follow a structure similar to:

TRY
 |
 +-- Start and wait for approval
 |
 +-- Process Approve / Reject

CATCH
 |
 +-- Handle failure or timeout
 |
 +-- Send failure notification

FINALLY
 |
 +-- Complete final processing

Power Automate's Configure run after functionality was used to control the Catch and Finally behaviour.

This helped me understand how workflows can be designed with failure scenarios in mind instead of handling only the successful path.

Environment Variables

I did not want environment-specific URLs hard-coded directly inside the solution.

For example:

DEV  -> DEV API URL
TEST -> TEST API URL
PROD -> PROD API URL

An Environment Variable allows the same solution component to move between environments while receiving the correct configuration value for each environment.

This is especially useful when implementing proper Power Platform ALM.

Connection References

I used solution-aware Connection References for components such as:

  • Dataverse

  • Office 365 Outlook

  • Approvals

Conceptually:

Power Automate Flow
        |
        v
Connection Reference
        |
        v
Environment-specific Connection

This prevents a solution-aware flow from being tightly coupled to the developer's original connection when it is transported to another environment.

Unit Testing Dataverse Plug-ins

I created a separate MSTest project for plug-in testing.

For the High Priority Case plug-in, I mocked components such as:

  • IPluginExecutionContext

  • ITracingService

  • IServiceProvider

using Moq.

One test scenario was:

Old Priority = Normal
New Priority = High

The expected plug-in behaviour is then verified without needing to manually perform the operation inside Dynamics 365 every time.

Adding unit tests into the repository also means they can be executed automatically by the CI pipeline.

Azure DevOps CI/CD

The next part of the project was automating ALM using an Azure DevOps YAML pipeline.

I organised the pipeline into several stages.

Stage 1 — Continuous Integration

NuGet Restore
      |
      v
Build Plug-in + Test Project
      |
      v
Run Unit Tests
      |
      v
Publish Build Artifact

If the build or tests fail, deployment should not continue.

Stage 2 — Export Solution from DEV

The pipeline connects to the DEV Power Platform environment and:

Publish Customisations
        |
        v
Export Managed Solution
        |
        v
Publish Solution ZIP as Artifact

The managed solution becomes the deployment artifact used for the TEST environment.

Manual Approval Before TEST

I added a manual approval stage between solution export and TEST deployment.

Build + Export
      |
      v
Manual Approval
      |
      +---- Approve -> Deploy
      |
      +---- Reject -> Stop

Although this is a learning project, I wanted the pipeline to represent the type of controlled release process used in enterprise environments.

Deploying to TEST

After approval, the managed solution is imported into the TEST environment using a Power Platform service connection.

I also created a deployment settings file for environment-specific configuration such as:

  • Environment Variable values

  • Connection Reference mappings

This separates environment configuration from the solution itself.

Patch vs Update vs Upgrade

Another important area I practised was Power Platform solution versioning.

Patch

A Patch is useful for a small targeted change.

Base Solution
     +
   Patch

Update

An Update deploys a newer version of an existing managed solution while retaining components that may not be present in the newer package.

Upgrade

An Upgrade can fully replace the previous solution layer and remove components that are no longer required.

A typical upgrade sequence is:

Import as Holding Solution
          |
          v
Apply Solution Upgrade

Understanding these differences is important when maintaining Power Platform applications over multiple releases.

A Useful ALM Issue I Faced

One of the most useful lessons came from a deployment failure.

The Azure DevOps Service Principal could authenticate successfully with Dataverse, but the pipeline failed with a connection authorization error.

The situation was similar to:

Pipeline Service Principal
          |
          v
Connection Reference
          |
          v
User-owned Connection
          |
          X
Service Principal cannot use connection

The deployment identity needed permission to use the target environment connection.

After sharing the required connection and providing the appropriate permission, the deployment was able to proceed.

This was a useful reminder that authentication to Dataverse and authorization to use a Power Platform connection are different concerns.

It also reinforced why enterprise environments should carefully manage service and integration accounts instead of depending on individual developer-owned connections.

Security

Because the repository is public, I made sure the project does not intentionally contain credentials such as:

Client Secrets
Passwords
Private Certificates
PFX files
Production Credentials
Personal Access Tokens

Secrets and environment-specific credentials should be stored securely and injected into deployment processes rather than committed to source control.

What I Learned

Building this project helped connect several topics that are often learned separately:

Dynamics development

  • C# plug-ins

  • Pipeline stages

  • Pre Images

  • Dataverse operations

  • Custom Workflow Activities

Power Platform

  • Power Automate

  • Environment Variables

  • Connection References

  • Solution-aware components

Integration

  • REST APIs

  • JSON

  • Retry handling

  • Failure handling

Engineering practices

  • Unit testing

  • Source control

  • CI/CD

  • Deployment approvals

ALM

  • Managed solutions

  • DEV → TEST deployments

  • Deployment settings

  • Patch

  • Update

  • Upgrade

  • Service Principals

The biggest benefit for me was seeing how these pieces work together as one lifecycle rather than treating Dynamics 365 configuration, development and deployment as separate topics.

Source Code

The complete project, including C# plug-ins, tests, Power Platform solution files, deployment settings and Azure DevOps YAML pipeline, is available here:

👉 https://github.com/sangeetha2402-ravichandran/Dynamics365-CustomerService-ALM