Intro
In a previous post I described how I implemented a windows service for scheduling Forefront Identity Manager.Since then, me and my colleagues used it in every FIM project. For one project I was asked if it was possible to trigger the synchronization "on demand". A specific trigger for a synchronization cycle for example, was the creation of a user in the FIM portal. After some brainstorming and Googling, we came up with a solution.
Trigger
We asked ourselves following question: "Is it possible to send a signal to our existing Windows service to start a synchronization cycle?". All the functionality for scheduling was already there, so it seemed reasonable to investigate and explore this option. As it turns out, it is possible to send a signal to a Windows service and the implementation turned out to be very simple (and simple is good, right?).In addition to the scheduling on predefined moments defined in the job configuration file, which is implemented through the Quartz framework, we started an extra thread:
while (true)
{
if (scheduler.GetCurrentlyExecutingJobs().Count == 0
&& !paused)
{
scheduler.PauseAll();
if (DateTime.Compare(StartSignal, LastEndTime) > 0)
{
running = true;
StartSignal = DateTime.Now;
LastEndTime = StartSignal;
SchedulerConfig schedulerConfig =
new SchedulerConfig(runConfigurationFile);
if (schedulerConfig != null)
{
schedulerConfig.RunOnDemand();
}
else
{
logger.Error("Scheduler configuration not found.");
throw new JobExecutionException
("Scheduler configuration not found.");
}
running = false;
}
scheduler.ResumeAll();
}
// 5 second delay
Thread.Sleep(5000);
}
StartSignal
First thing it does is check if one of the time-triggered schedules is not running and the service is not paused. Then it checks to see if an on-demand trigger was received by checking the StartSignal timestamp. So as you can see, the StartSignal timestamp is the one controlling the action. If the service receives a signal to start a synchronization schedule, it simply sets the StartSignal parameter:protected override void OnCustomCommand(int command)
{
if (command == ONDEMAND)
{
StartSignal = DateTime.Now;
}
}
If you want to know more about developing custom activities, this article is a good starting point.
The first thing it does next if a signal was received, is pause the time-triggered mechanism. If the synchronization cycle finishes the time-triggered scheduling is resumed. The beautiful thing about this way of working is that the two separate mechanisms work alongside each other. The time-triggered schedule is not fired if an on-demand schedule is running and vice versa. If a signal was sent during a period of time the service was paused, the on-demand schedule will fire as soon as the service is resumed. The StartSignal timestamp will take care of that.
StartSync
So, how do you send a signal to this service, you ask? This is also fairly straightforward. I implemented the FIM portal scenario I described above by implementing a custom C# workflow with a single code activity:using System.ServiceProcess;
private const int OnDemand = 234;
private void startSync(){
ServiceController is4uScheduler =
new ServiceController("IS4UFIMScheduler");
is4uScheduler.ExecuteCommand(OnDemand);
}
If you want to know more about developing custom activities, this article is a good starting point.
The integer value is arbitrary. You only need to make sure you send the same value as is defined in the service source code. The ServiceController takes the system name of the Windows service.
Powershell
The same is possible in Powershell:[System.Reflection.Assembly]::Load("System.ServiceProcess,
Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a")
$is4uScheduler = New-Object System.ServiceProcess.ServiceController
$is4uScheduler.Name = "IS4UFIMScheduler"
$is4uScheduler.ExecuteCommand(234)
Another extension I implemented (inspired by Dave Nesbitt's question on my previous post) was the delay step. This kind of step allows you to insert a window of time between two management agent runs. This in addition to the default delay, which is inserted between every step. So now there are four kind of steps possible in the run configuration file: LinearSequence, ParallelSequence, ManagementAgent and Delay. I saw the same idea being implemented in powershell here.
A very usefull function I didn't mention in my previous post, but was already there, is the cleanup of the run history (which can become very big in a fast-synchronizing FIM deployment). This function can be enabled by setting the option "ClearRunHistory" to true and setting the number of days in the "KeepHistory" option. If you enable this option, you need to make sure the service account running the service is a member of the FIM Sync Admins security group. If you do not use this option, membership of the FIM Sync Operators group is sufficient.
To end I would like to give you pointers to some other existing schedulers for FIM:
FIM 2010: How to Automate Sync Engine Run Profile Execution