One small plugin, built end to end, using every chapter in this course: when a Case is created, automatically create a follow-up Task due 2 business days later.
- Message: Create on the Case (incident) table
- Stage: Post-operation, running asynchronously
- Needs a post-image to read the system-generated Case ID
- Uses unsecure configuration for a configurable "days to add" value
Step 1 — Choose the message and stage
The trigger is Create on Case. Creating a follow-up Task isn't time-critical to the user's save, so it belongs in post-operation, running async — the case saves instantly, and the Task appears moments later.
Step 2 — Decide on an image
A new Case doesn't have its ID yet when the plugin starts — the platform assigns it during the write. A post-image containing the Case's ID and Title is exactly what's needed to link the new Task back to it.
Step 3 — Write the plugin
public class CreateCaseFollowUpTask : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
var context = (IPluginExecutionContext)serviceProvider
.GetService(typeof(IPluginExecutionContext));
var serviceFactory = (IOrganizationServiceFactory)serviceProvider
.GetService(typeof(IOrganizationServiceFactory));
var service = serviceFactory.CreateOrganizationService(context.UserId);
var tracingService = (ITracingService)serviceProvider
.GetService(typeof(ITracingService));
try
{
if (!context.PostEntityImages.Contains("PostImage")) return;
var caseRecord = context.PostEntityImages["PostImage"];
int daysToAdd = 2;
if (context.PostEntityImages.Contains("daystoaddconfig"))
{
int.TryParse(context.PostEntityImages["daystoaddconfig"].ToString(), out daysToAdd);
}
var task = new Entity("task");
task["subject"] = "Follow up: " + caseRecord.GetAttributeValue("title");
task["regardingobjectid"] = new EntityReference("incident", caseRecord.Id);
task["scheduledend"] = DateTime.UtcNow.AddDays(daysToAdd);
service.Create(task);
tracingService.Trace("Follow-up task created for Case {0}", caseRecord.Id);
}
catch (Exception ex)
{
tracingService.Trace("CreateCaseFollowUpTask failed: {0}", ex.Message);
throw new InvalidPluginExecutionException("Could not create the follow-up task for this case.");
}
}
}
Step 4 — Add configuration (optional, but a nice touch)
Instead of hardcoding "2 days," the unsecure configuration field can hold that number as plain text, so an admin can adjust it later without touching code or redeploying. Nothing here is sensitive, so unsecure configuration is the right choice — no need for secure configuration on this one.
Step 5 — Sign and register
Sign the assembly, then register it in the Plugin Registration Tool: message Create, primary entity incident (Case), stage post-operation, execution mode asynchronous, plus a registered post-image containing at least title. Full steps are in the Signing & Registering a Plugin chapter.
Step 6 — Test it
Execute method — it's a chain of small decisions: which message and stage, whether you need an image, what belongs in configuration, and how you'll verify it actually worked. Get comfortable with that chain, and the code itself is the easy part.
You've now covered the full plugin development lifecycle. For a deeper single-page reference, see the existing Plugins & Server-Side Extensibility tutorial, and for calling Dataverse from outside a plugin entirely, Web API & Dataverse Integration is the natural next read.