Get Started with Dynamics 365 Plugin Development: A Practical Guide
Write the plugin, understand every line, register it, and confirm it actually runs.
A plugin is how you run custom C# code the moment something happens in Dynamics 365 — a record created, updated, or deleted.
Plugins run on the server, not inside your own application, so the setup and the debugging look a little different from a normal C# project.
This guide covers everything you need: writing the plugin, understanding what each piece of code does, registering it in D365, and testing it.
Scenario
Trigger: a new Account record is created.
Logic: if the description field is left blank, the plugin fills it in with a note like "Description auto-populated by plugin on {date}".
This is a simple example, but it uses the same pattern you'll use in almost every plugin: read the record, check a value, update it.
Prerequisites
- Visual Studio 2022 (Community edition is fine). During setup, install the ".NET desktop development" workload — that's what gives you the Class Library (.NET Framework) project template.
- .NET Framework 4.6.2 as the project's target framework. D365 plugins must use .NET Framework, not .NET Core or .NET 5+, or the plugin won't load.
- NuGet package:
Microsoft.CrmSdk.CoreAssemblies— adds the SDK classes used in the code below. - Plugin Registration Tool, available as a NuGet package (
Microsoft.CrmSdk.XrmTooling.PluginRegistrationTool) or as a tool inside XrmToolBox. You'll use this to upload your plugin to D365. - A D365 environment with a user account that has System Administrator (or System Customizer) access, so you can register plugins and view logs.
- A strong name key (.snk), created in Visual Studio. D365 requires plugin assemblies to be signed.
Step 1: Create the project
- Open Visual Studio → Create a new project.
- Search for Class Library (.NET Framework) (not the plain "Class Library" template, which uses .NET Core).
- Name it
AccountPlugins, confirm the target framework is .NET Framework 4.6.2, and create it. - Right-click the project → Manage NuGet Packages → Browse → search
Microsoft.CrmSdk.CoreAssemblies→ Install. - Right-click the project → Properties → Signing tab → check Sign the assembly → New... → create a
.snkfile (password optional). This step is required before you can register the plugin later.
Step 2: Write the plugin class
Every plugin implements IPlugin, an interface with one method: Execute. D365 calls this method whenever your registered event happens.
using System; using Microsoft.Xrm.Sdk; namespace AccountPlugins { public class SetDefaultDescription : IPlugin { public void Execute(IServiceProvider serviceProvider) { ITracingService tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService)); IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext)); IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory)); IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId); tracingService.Trace("Plugin started for message: " + context.MessageName); if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity account) { if (!account.Contains("description")) { account["description"] = $"Description auto-populated by plugin on {DateTime.Now}"; service.Update(account); tracingService.Trace("Description field updated."); } else { tracingService.Trace("Description already had a value, nothing to do."); } } } } }
What each line means
IServiceProvider serviceProvider
D365 passes this into your plugin automatically. Think of it as a toolbox; everything else you need comes out of it using GetService(typeof(...)).
ITracingService
Lets you write log messages. Since plugins run on the server, you can't debug them like a normal app, so these logs are how you check what happened.
IPluginExecutionContext
Tells you what triggered the plugin, like the message name (Create, Update, etc.) and the record itself. context.InputParameters["Target"] is the actual record being created or updated.
IOrganizationServiceFactory
Creates the service you'll use to talk to D365.
IOrganizationService
The object you use to Create, Retrieve, Update, or Delete records. In this plugin, service.Update(account) saves the change.
Step 3: Build the assembly
Build the project (use the Release configuration). Check that AccountPlugins.dll was created under bin\Release with no errors. If the build fails because of signing, go back and confirm you completed the signing step in Step 1.
Step 4: Register the plugin
Open the Plugin Registration Tool and connect to your D365 environment:
- CREATE NEW CONNECTION → enter your environment URL → sign in.
- Register menu → Register New Assembly → select
AccountPlugins.dll→ set Isolation Mode to Sandbox → click Register Selected Plugins. - Right-click the
SetDefaultDescriptionclass in the tree → Register New Step. - Fill in the step (see the mock panel below), then click Register New Step to save.
Click "Register New Step" above to see how the tool confirms the step was saved.
Step 5: Verify it works
Create a new Account record without filling in the Description field, then save it. Reopen the record and check the Description field — it should now show the auto-generated note.
Open the Plugin Registration Tool, right-click your step, and use Profile to see the trace log and find out where it stopped.
Every plugin follows the same shape: pull the target record out of InputParameters, check a value, and use IOrganizationService to act on it. Once this pattern is comfortable, moving from a simple field default to more complex business logic is mostly a matter of what you check and what you do next — not how the plugin is wired together.