Scheduled Tasks in Crm 2013

Imagine you want to periodically update all your crm opportunities status depending the date they were created or something similar, you will need to implement scheduled tasks, which does not exist out of the box.

This article describes the technical implementation of scheduled Tasks in Dynamics Crm 2013. This solution also works with Dynamics Crm 2011 and does not involve recursive workflows and the associated limitations.

The components of the solution are:

A custom entity ‘Scheduled Task settings’

This entity allow to create multiple tasks settings with different frequencies and any useful parameters you may need. A field of this entity is the date of next execution.

A workflow that triggers on creation of this entity or when date of next execution changes

This workflow wait until the date of next execution then changes the status of the record to ‘Processing’.

A plugin registered on the update of a scheduled task setting

The plugin does nothing if the status has not changing to ‘Processing’.

When the status is changing to ‘Processing’, the plugin do the following:

  • Update the date of next execution depending on the frequency specified on the record. Note that this will trigger another workflow for the next execution.
  • Do the tasks parameterized on the record.
  • Update the status of the record to ‘Waiting’
  • Optionally update a message on the record to report the result of the tasks execution.

Pretty simple isn’t it?

Here are some screens to help you implement this:

Scheduled Task Setting Form:



Workflow :


For Record fields change, select ‘Next Execution Date Time’
Red part is detailed here:


Plugin Registration :


Plugin Implementation:

        protected void ExecutePostScheduledTaskSettingUpdate(LocalPluginContext localContext)
            if (localContext == null)
                throw new ArgumentNullException("localContext");

            IPluginExecutionContext context = localContext.PluginExecutionContext;

            new_scheduledtasksettings currentScheduledTaskSettings = ((Entity)localContext.PluginExecutionContext.InputParameters["Target"]).ToEntity<new_scheduledtasksettings>();

            // If Status not changing to 'Processing' -> exit
            if (false == currentScheduledTaskSettings.Attributes.Contains("statuscode") ||
                currentScheduledTaskSettings.statuscode == null ||
                currentScheduledTaskSettings.statuscode.Value != 100000000)     // Processing

            new_scheduledtasksettings preProcessusRecurrent = localContext.PluginExecutionContext.PreEntityImages["PreImage"].ToEntity<new_scheduledtasksettings>();

                // Get current values
                OptionSetValue frequency = currentScheduledTaskSettings.new_frequency != null
                            ? currentScheduledTaskSettings.new_frequency :
                            (preProcessusRecurrent.new_frequency != null ? preProcessusRecurrent.new_frequency : null);

                DateTime nextExecution = currentScheduledTaskSettings.new_nextexecutiondatetime.HasValue
                            ? currentScheduledTaskSettings.new_nextexecutiondatetime.Value
                            : (preProcessusRecurrent.new_nextexecutiondatetime.HasValue ? preProcessusRecurrent.new_nextexecutiondatetime.Value : default(DateTime));

                bool IsAgeUpdate = currentScheduledTaskSettings.new_ageupdate.HasValue
                            ? currentScheduledTaskSettings.new_ageupdate.Value
                            : (preProcessusRecurrent.new_ageupdate.HasValue ? preProcessusRecurrent.new_ageupdate.Value : false);

                bool IsOpportunityUpdate = currentScheduledTaskSettings.new_opportunitystatusupdate.HasValue
                            ? currentScheduledTaskSettings.new_opportunitystatusupdate.Value
                            : (preProcessusRecurrent.new_opportunitystatusupdate.HasValue ? preProcessusRecurrent.new_opportunitystatusupdate.Value : false);

                if (false == IsAgeUpdate &&
                    false == IsOpportunityUpdate
                    throw new InvalidPluginExecutionException("No task to process.");

                switch (frequency.Value)
                    case 100000000:
                        // Daily
                        nextExecution = nextExecution.AddDays(1);
                    case 100000001:
                        // Weekly                   
                        nextExecution = nextExecution.AddDays(7);
                    case 100000002:
                        // Monthly
                        nextExecution = nextExecution.AddMonths(1);
                        throw new InvalidPluginExecutionException("Invalid Frequency.");

                string batchLog = string.Empty;

                if (IsAgeUpdate)
                    // Age Update Process
                    batchLog = AgeUpdate(localContext);

                if (IsOpportunityUpdate)
                    // Opportunity Status Update Process
                    batchLog += Environment.NewLine + OpportunityStatusUpdate(localContext);

                // Status set back to 'Waiting'
                new_scheduledtasksettings settingToUpdate = new new_scheduledtasksettings();
                settingToUpdate.new_scheduledtasksettingsId = currentScheduledTaskSettings.new_scheduledtasksettingsId;
                settingToUpdate.new_nextexecutiondatetime = nextExecution;
                settingToUpdate.statuscode = new OptionSetValue(1);
                settingToUpdate.new_reportlog = batchLog;
            catch (Exception er)
                new_scheduledtasksettings settingToUpdate = new new_scheduledtasksettings();
                settingToUpdate.new_scheduledtasksettingsId = currentScheduledTaskSettings.new_scheduledtasksettingsId;
                settingToUpdate.new_reportlog = er.Message;
        private string AgeUpdate(LocalPluginContext localContext)
            // Do your process here
            return "log informations from Age Update";
        private string OpportunityStatusUpdate(LocalPluginContext localContext)
            // Do your process here
            return "log informations from Opportunity Status Update";