Custom Workflow Example: Enumerate Resources Activity

The Enumerate Resources Activity (Microsoft.ResourceManagement.Workflow.Activities.EnumerateResourcesActivity) is used by Custom Workflows in FIM 2010 R2 to search for and retrieve objects in the FIM Portal/Service database using an XPath Filter. You will need to use this in place of the Read Resource Activity in situations where you do not already know the Resource ID of the object you wish to retrieve, or when you wish to retrieve multiple objects.

In some cases, you may simply want to use the activity to determine whether an object already exists (such as in a workflow that Generates a Unique Value or which generates automated role-based groups), but in most cases if you are searching for objects in the FIM Portal, we may want to do something with the objects returned. Now, unlike the Read Resources Activity, the Enumerate Resources Activity does not return a property with the results of its search – instead, we are able to nest activities inside the Enumerate Activity which execute for each object returned by the search filter (conceptually, you can think of this as a foreach on the results record).

Now, there are two complications with how to use the results returned. The first of these is that there is a bug in the Activity Designer for the Enumerate Resources Activity which means that you can’t nest an activity inside the Enumerate activity using the designer, and so have to do it manually. The second, is that you can only nest a single activity inside of the Enumerate Activity, and so you will need to use a Sequence Activity if you want to run multiple activities. The solution to both of these will be covered below.

In this example, we’re going to assume that a Department object (a custom object type created to hold organisational information) has been modified, and we now need to search for all users that have a string value for their Department attribute which matches the DisplayName of the Department object, and update the Manager attribute on each user. Note that if the user’s Department field was actually a reference value (as in Method 4 of our Populating RCDC Dropdowns example), you might search directly for a reference to the Department object (this will be covered below).

Step 1: Add a new Activity to your FIM Custom Activity Library

Step 2: Give that activity an appropriate name, in this case EnumerateResourcesExample.cs


Step 3: Add the relevant activities into the workflow In this example, we have added the Current Request Activity, a Read Resource Activity, an Enumerate Resources Activity, a Sequence Activity, an Update Resource Activity and two code activities

Note: The last Code Activity and the Update Resource Activity are nested in the Sequence Activity, which will in turn need to be nested manually inside the Enumerate Resource Activity by editing the designer directly. Refer to Step 7.

Step 4: Give the activities meaningful names.

Step 5: Double click each of the code blocks to promote the bindable properties for the code blocks.


Step 6: Right click on the Update Resource Activity and select Promote the bindable properties on the update activity.

Note: Unlike my Update Resource Activity Example, we are unable to assign the properties of the Update Resources Activity object directly and so must bind some Dependency Properties to the Activity. You’ll see how we use those later. Once you’ve created the dependency properties, you’ll see them generated in your activity source code:

public static DependencyProperty UpdateDepartmentUser_ActorId1Property = DependencyProperty.Register("UpdateDepartmentUser_ActorId1", typeof(System.Guid), typeof(FIMSpecialist.DemoLibrary.Activities.EnumerateResourcesActivity));
 
[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
[BrowsableAttribute(true)]
[CategoryAttribute("Parameters")]
public Guid UpdateDepartmentUser_ActorId1
{
	get
	{
		return ((System.Guid)(base.GetValue(FIMSpecialist.DemoLibrary.Activities.EnumerateResourcesActivity.UpdateDepartmentUser_ActorId1Property)));
	}
	set
	{
		base.SetValue(FIMSpecialist.DemoLibrary.Activities.EnumerateResourcesActivity.UpdateDepartmentUser_ActorId1Property, value);
	}
}
 
public static DependencyProperty UpdateDepartmentUser_ApplyAuthorizationPolicy1Property = DependencyProperty.Register("UpdateDepartmentUser_ApplyAuthorizationPolicy1", typeof(System.Boolean), typeof(FIMSpecialist.DemoLibrary.Activities.EnumerateResourcesActivity));
 
[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
[BrowsableAttribute(true)]
[CategoryAttribute("Parameters")]
public Boolean UpdateDepartmentUser_ApplyAuthorizationPolicy1
{
	get
	{
		return ((bool)(base.GetValue(FIMSpecialist.DemoLibrary.Activities.EnumerateResourcesActivity.UpdateDepartmentUser_ApplyAuthorizationPolicy1Property)));
	}
	set
	{
		base.SetValue(FIMSpecialist.DemoLibrary.Activities.EnumerateResourcesActivity.UpdateDepartmentUser_ApplyAuthorizationPolicy1Property, value);
	}
}
 
public static DependencyProperty UpdateDepartmentUser_ResourceId1Property = DependencyProperty.Register("UpdateDepartmentUser_ResourceId1", typeof(System.Guid), typeof(FIMSpecialist.DemoLibrary.Activities.EnumerateResourcesActivity));
 
[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
[BrowsableAttribute(true)]
[CategoryAttribute("Parameters")]
public Guid UpdateDepartmentUser_ResourceId1
{
	get
	{
		return ((System.Guid)(base.GetValue(FIMSpecialist.DemoLibrary.Activities.EnumerateResourcesActivity.UpdateDepartmentUser_ResourceId1Property)));
	}
	set
	{
		base.SetValue(FIMSpecialist.DemoLibrary.Activities.EnumerateResourcesActivity.UpdateDepartmentUser_ResourceId1Property, value);
	}
}
 
public static DependencyProperty UpdateDepartmentUser_UpdateParameters1Property = DependencyProperty.Register("UpdateDepartmentUser_UpdateParameters1", typeof(Microsoft.ResourceManagement.WebServices.WSResourceManagement.UpdateRequestParameter[]), typeof(FIMSpecialist.DemoLibrary.Activities.EnumerateResourcesActivity));
 
[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
[BrowsableAttribute(true)]
[CategoryAttribute("Parameters")]
public Microsoft.ResourceManagement.WebServices.WSResourceManagement.UpdateRequestParameter[] UpdateDepartmentUser_UpdateParameters1
{
	get
	{
		return ((Microsoft.ResourceManagement.WebServices.WSResourceManagement.UpdateRequestParameter[])(base.GetValue(FIMSpecialist.DemoLibrary.Activities.EnumerateResourcesActivity.UpdateDepartmentUser_UpdateParameters1Property)));
	}
	set
	{
		base.SetValue(FIMSpecialist.DemoLibrary.Activities.EnumerateResourcesActivity.UpdateDepartmentUser_UpdateParameters1Property, value);
	}
}

Step 7: Add the Microsoft.ResourceManagement references to your activity, as well as System.Collections.Generic so we can use the List Class for the UpdateRequestParameter object.

Step 8: Initialise the Read Resource Activity to read the target department (in this case, we’re assuming that the MPR which triggered the workflow occurred after a change to a Department object).

Step 9: Initialise the Enumerate Resources Activity. This is where we define the XPath Filter to search the FIM Portal database.

Note: If your user object had a Department reference field, you could configure your XPath Filter to search for all users with a reference, like this:

Step 10: For each object returned by the Enumerate Resources Activity, configure the Update Resource Activity to update the Manager attribute to the manager of their department.

Note: If a Code Activity was nested directly in the EnumerateResourcesActivity, we could pass the sender to the GetCurrentIterationItem method. However, because the sender object is the Sequence Activity, we actually need to call the Parent object of that to get the correct resource returned. If we were to just pass the sender, in this example, we would get a null value returned.

Step 11: Add another class to the project to create a basic UI for this activity, with the following code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.ResourceManagement.Workflow.Activities;
using Microsoft.IdentityManagement.WebUI.Controls;
using FIMSpecialist.DemoLibrary.Activities;
 
namespace FIMSpecialist.DemoLibrary.WebUIs
{
    public class EnumerateResourcesActivityExampleUI : ActivitySettingsPart
    {
        public override System.Workflow.ComponentModel.Activity GenerateActivityOnWorkflow(SequentialWorkflow workflow)
        {
            if (!this.ValidateInputs())
            {
                return null;
            }
 
            EnumerateResourcesActivityExample enumerateExample = new EnumerateResourcesActivityExample();
            return enumerateExample;
        }
 
        public override void LoadActivitySettings(System.Workflow.ComponentModel.Activity activity)
        {
            EnumerateResourcesActivityExample enumerateExample = activity as EnumerateResourcesActivityExample;
 
            if (null != enumerateExample)
            {
            }
        }
 
        public override ActivitySettingsPartData PersistSettings()
        {
            ActivitySettingsPartData data = new ActivitySettingsPartData();
            return data;
        }
 
        public override void RestoreSettings(ActivitySettingsPartData data)
        {
            if (null != data)
            {
            }
        }
 
        public override void SwitchMode(ActivitySettingsPartMode mode)
        {
            bool readOnly = mode == ActivitySettingsPartMode.View;
        }
 
        public override string Title
        {
            get { return "Enumerate Resources Activity Example"; }
        }
 
        public override bool ValidateInputs()
        {
            return true;
        }
    }
}

And that’s it. From here, configure your Workflow in the FIM Portal, and setup an MPR to execute the workflow when a Department object’s manager field is modified.

Final code for EnumerateResourcesActivityExample.cs is as follows:

using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Collections;
using System.Linq;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Design;
using System.Workflow.ComponentModel.Compiler;
using System.Workflow.ComponentModel.Serialization;
using System.Workflow.Runtime;
using System.Workflow.Activities;
using System.Workflow.Activities.Rules;
using Microsoft.ResourceManagement.WebServices.WSResourceManagement;
using Microsoft.ResourceManagement.Workflow.Activities;
using Microsoft.ResourceManagement.WebServices;
using System.Collections.Generic;
 
namespace FIMSpecialist.DemoLibrary.Activities
{
    public partial class EnumerateResourcesActivityExample : SequenceActivity
    {
        // This is the guid for the FIM Service built-in admin account.
        // We will use it to execute each of our workflow activities.
        const string FIMAdminGuid = "7fb2b853-24f0-4498-9534-4e10589723c4";
 
        public EnumerateResourcesActivityExample()
        {
            InitializeComponent();
        }
 
        private void InitialiseReadDepartment_ExecuteCode(object sender, EventArgs e)
        {
            // Set the Actor ID for the Read Activity to the FIM Admin GUID
            ReadDepartment.ActorId = new Guid(FIMAdminGuid);
 
            // Set the Resource to retrieve as the currently requested object.
            // Note, you could also set this to the target ID of the containing workflow
            ReadDepartment.ResourceId = CurrentRequest.CurrentRequest.Target.GetGuid();
 
            // Get the Manager attribute from the Department reference.
            ReadDepartment.SelectionAttributes = new string[] { "DisplayName", "Manager" };
 
        }
 
        private void InitialiseSearchUsers_ExecuteCode(object sender, EventArgs e)
        {
            // Get the object that was read using the Read Activity Resource into a ResourceType object
            // In a real-world workflow, you should check that ReadDepartment.Resource isn't null
            ResourceType department = ReadDepartment.Resource;
 
            // Get the DisplayName for the group
            string displayName = (string)department["DisplayName"];
 
            // Initialise the search for users whose department match the current department
            SearchDepartmentUsers.ActorId = new Guid(FIMAdminGuid);
            SearchDepartmentUsers.XPathFilter = String.Format("/Person[Department='{0}'", displayName);
 
            // SearchDepartmentUsers.XPathFilter = String.Format("/Person[DepartmentRef='{0}'", department.ObjectID.GetGuid());
 
        }
 
        private void InitialiseUpdateUser_ExecuteCode(object sender, EventArgs e)
        {
            // Get the current object being processed by the Enumerate Resources Activity
            ResourceType currentUser = EnumerateResourcesActivity.GetCurrentIterationItem(((CodeActivity)sender).Parent) as ResourceType;
 
            // Put the Department object into a Resource Type object
            ResourceType department = ReadDepartment.Resource;
 
            // Get the manager reference from the object (again, you should check that it exists)
            // The manager reference in this case is stored as a UniqueIdentifier type.
            UniqueIdentifier manager = (UniqueIdentifier)department["Manager"];
 
            UpdateDepartmentUser_ActorId1 = new Guid(FIMAdminGuid);
            UpdateDepartmentUser_ResourceId1 = currentUser.ObjectID.GetGuid();
 
            // Create a list of UpdateRequestParameter objects
            List<UpdateRequestParameter> updateRequestParameters = new List<UpdateRequestParameter>();
 
            // Add the Manager for this department to the Update Parameters list
            updateRequestParameters.Add(new UpdateRequestParameter("Manager", UpdateMode.Insert, manager));
 
            // Convert the update parameters list into an array of UpdateRequestParameter objects and assign it
            // to the UpdateParameters property of the Update Resource Activity
            UpdateDepartmentUser_UpdateParameters1 = updateRequestParameters.ToArray<UpdateRequestParameter>();
 
        }
 
        public static DependencyProperty UpdateDepartmentUser_ActorId1Property = DependencyProperty.Register("UpdateDepartmentUser_ActorId1", typeof(System.Guid), typeof(FIMSpecialist.DemoLibrary.Activities.EnumerateResourcesActivityExample));
 
        [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
        [BrowsableAttribute(true)]
        [CategoryAttribute("Parameters")]
        public Guid UpdateDepartmentUser_ActorId1
        {
            get
            {
                return ((System.Guid)(base.GetValue(FIMSpecialist.DemoLibrary.Activities.EnumerateResourcesActivityExample.UpdateDepartmentUser_ActorId1Property)));
            }
            set
            {
                base.SetValue(FIMSpecialist.DemoLibrary.Activities.EnumerateResourcesActivityExample.UpdateDepartmentUser_ActorId1Property, value);
            }
        }
 
        public static DependencyProperty UpdateDepartmentUser_ApplyAuthorizationPolicy1Property = DependencyProperty.Register("UpdateDepartmentUser_ApplyAuthorizationPolicy1", typeof(System.Boolean), typeof(FIMSpecialist.DemoLibrary.Activities.EnumerateResourcesActivityExample));
 
        [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
        [BrowsableAttribute(true)]
        [CategoryAttribute("Parameters")]
        public Boolean UpdateDepartmentUser_ApplyAuthorizationPolicy1
        {
            get
            {
                return ((bool)(base.GetValue(FIMSpecialist.DemoLibrary.Activities.EnumerateResourcesActivityExample.UpdateDepartmentUser_ApplyAuthorizationPolicy1Property)));
            }
            set
            {
                base.SetValue(FIMSpecialist.DemoLibrary.Activities.EnumerateResourcesActivityExample.UpdateDepartmentUser_ApplyAuthorizationPolicy1Property, value);
            }
        }
 
        public static DependencyProperty UpdateDepartmentUser_ResourceId1Property = DependencyProperty.Register("UpdateDepartmentUser_ResourceId1", typeof(System.Guid), typeof(FIMSpecialist.DemoLibrary.Activities.EnumerateResourcesActivityExample));
 
        [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
        [BrowsableAttribute(true)]
        [CategoryAttribute("Parameters")]
        public Guid UpdateDepartmentUser_ResourceId1
        {
            get
            {
                return ((System.Guid)(base.GetValue(FIMSpecialist.DemoLibrary.Activities.EnumerateResourcesActivityExample.UpdateDepartmentUser_ResourceId1Property)));
            }
            set
            {
                base.SetValue(FIMSpecialist.DemoLibrary.Activities.EnumerateResourcesActivityExample.UpdateDepartmentUser_ResourceId1Property, value);
            }
        }
 
        public static DependencyProperty UpdateDepartmentUser_UpdateParameters1Property = DependencyProperty.Register("UpdateDepartmentUser_UpdateParameters1", typeof(Microsoft.ResourceManagement.WebServices.WSResourceManagement.UpdateRequestParameter[]), typeof(FIMSpecialist.DemoLibrary.Activities.EnumerateResourcesActivityExample));
 
        [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
        [BrowsableAttribute(true)]
        [CategoryAttribute("Parameters")]
        public Microsoft.ResourceManagement.WebServices.WSResourceManagement.UpdateRequestParameter[] UpdateDepartmentUser_UpdateParameters1
        {
            get
            {
                return ((Microsoft.ResourceManagement.WebServices.WSResourceManagement.UpdateRequestParameter[])(base.GetValue(FIMSpecialist.DemoLibrary.Activities.EnumerateResourcesActivityExample.UpdateDepartmentUser_UpdateParameters1Property)));
            }
            set
            {
                base.SetValue(FIMSpecialist.DemoLibrary.Activities.EnumerateResourcesActivityExample.UpdateDepartmentUser_UpdateParameters1Property, value);
            }
        }
    }
}
4 comments on “Custom Workflow Example: Enumerate Resources Activity
  1. Nigel Jones says:

    Hi Ross I caught your article in search of a solution I need

    I am walking through http://msdn.microsoft.com/en-us/library/windows/desktop/ff859524(v=vs.100).aspx = Walkthrough: Create a Logging Custom Activity and Deploy it to the FIM Portal

    After completing step 3 = Add the following code to RequestLoggingActivity.cs after the class constructor

    I get DependencyProperty with red underline.. which is stopping me binding to existing member .. I get the same DependencyProperty with red underline if I use the snippet to create WF dependency property

    I am running VS2008 and have added all the R2 references…

    I have installed ALL FIM components with FIM Sync, FIM Portal, pwd reg, pwd reset portal all working on the same Hyper-V Server 2008 R2 under windows 8

    Stalling at such an early point in exploring workflows (:

    Can you help please?

    TIA

    Nigel

  2. Ross says:

    Hi Nigel,

    I didn’t think much of the Microsoft logging example, to be honest. In some of my more basic workflows, I don’t even use dependency properties.

    Without seeing your code, I would guess that you either haven’t got the right ‘using’ directive, or you’ve put them in an incorrect place in your code. Probably the first one.

    If you’re trying to get started with workflows, consider starting with my basic Read Resource Activity Example and go from there. It’s about as basic as you can get.

  3. Nigel Jones says:

    Thanks Ross.. yep it was a newbie mistake.. moving forward now with the PD I had deferred for so many years when working for XXXXX

    Hope you are well

    Nigel

  4. Ross says:

    There’s nothing like getting your hands dirty with FIM! Can be a bit tricky at first, but once you get the hang of it, you’ll love it.

Leave a Reply

Your email address will not be published. Required fields are marked *

*