In many cases there is a need to add loops to a workflow.

Such loops may be required if you need to execute repetitive tasks, or to perform tasks on multiple objects.

This article shows both:

  • the sample task will disable users who have been inactive for a specified number of days
  • the sample task is repeated daily until explicitly stopped

The workflow sample outlined in this article has the purpose to deactivate users who have not logged in for a specified number of days.

For this purpose, the example uses two loops:

  • The outer loop is repeated every day. It scans for users who have not logged in for longer than the specified time
  • The inner loop goes through the result list and perform an Entity activity (disable the user) for each matching user.

Adding a time-controlled loop is rather simple.

  • behind your custom actions, append a dummy approval activity with a timeout of your choice
  • on timeout, go back to the start of your custom actions

Adding data-dependent loops is only slightly more complicated, since you need to add some custom ECMAscript to evaluate the data and control the loop.

Here’s the basic logic for a workflow that, once started, will automatically run every day, identify the inactive users, and disable each user in the list:

  1. Start Form

    Start your workflow and enter the inactivity interval. The sample uses a list with the options “30 days”, “60 days”, “90 days”, “180 days”, and “360 days”

    The form will also show what users will be affected during the first run.

    Internally, a Query is used, and you will probably need to adapt this query to your own needs:

    The sample query is named “CoolSolution_inactiveUsers” and perform a search on the container “OU=users,O=utopia” with a filter “( && ( loginDisabled != true )( loginTime < [param] ))”

  2. Calculate the Criterion Date

    If we want to identify the users who have not logged in for 90 days, we need to calculate the criterion date with something like “criterionDate = ([current date] – 90 days)”, before finding all users whose las login date is earlier that this criterion.

    We store the calculated criterionDate in flowdata (flowdata.start/criterionDate).

    This is the code to calculate the target date:

    function getCriterionDate(  )
    	var result 		= "00000000000000Z";
    		var daysOfInactivity		= flowdata.get('start/request_form/daysOfInactivity');
    		var someTimeAgo 		= new Date();
    		someTimeAgo.setDate( someTimeAgo.getDate() - daysOfInactivity );
    		result 			= "" + 
    			(	// make date 8-digit
    				10000	* 	someTimeAgo.getUTCFullYear() 
    			+ 	100		* 	(someTimeAgo.getMonth() +1)	
    			+ 				someTimeAgo.getUTCDate() 
    			(	// make time 8-digit
    				10000	* 	someTimeAgo.getUTCHours() 
    			+ 	100		* 	someTimeAgo.getUTCMinutes() 
    			+				someTimeAgo.getUTCSeconds() 
    			+ "Z";
    	catch ( ex ) {}
    	return( result );

  3. Query for Inactive Users

    Here, we run the query, passing the calculated criterionDate as parameter. The query will return an unknown number of matching users, which we store in flowdata (flowdata.start/inactiveUsers/DNs).

    This is the code to run the query and store the list:

    function getInactiveUsers() 
    	var result 					= new java.util.Vector();
    		var daysOfInactivity		= flowdata.get('start/request_form/daysOfInactivity');
    		var criterionDate  		= flowdata.get('start/criterionDate');
    		result  = IDVault.globalQuery( "CoolSolution_inactiveUsers", {"someTimeAgo":  criterionDate });
    	catch ( ex ) {}
    	return( result );

  4. Inactive Users Found

    If no inactive users were found, we skip the next actions, jump to step i), and wait for the next polling loop on the next day.

    If inactive users were found, we continue with step e)

    ( flowdata.get('start/inactiveUsers/DNs').length > 0 )

  5. Get Next User

    So, we have found a list of one or more inactive users.

    This step will simply get the first user from this list and store the DN in flowdata (flowdata.start/inactiveUsers/nextDN).


  6. User Found?

    This conditional activity checks if we have identified the next user in the list.

    The condition will return “true” as long there are more users to process; in this case, we’ll continue with step g).

    It will return “false”, after all users have been processed; in this case we’ll goto step i) to wait for the next polling loop on the next day.

    ( flowdata.get('start/inactiveUsers/nextDN').length > 0 )

  7. Disable this User

    This entity activity will set the selected user’s (flowdata.start/inactiveUsers/nextDN) attribute “Login Disabled” to true.
  8. Remove this User from List

    We have now just disabled the first user in the result list. In this step, we recalculate the list and remove the user that we have just processed.

    We continue with step e) to get the next user in the reduced list.

    This code removes the first list entry:

    function removeFirstElement() 
    	var result 				= new java.util.Vector();
    		var objectsRemaining	= flowdata.getObject('start/inactiveUsers/DNs')
    		// start loop with second element
    		for ( var i=1; i < objectsRemaining.size(); i++) 
    				result.add( objectsRemaining.get(i).getFirstChild().getNodeValue() );
    			catch (e) {}
    	catch (e) {}
    	return result;
    } ; 

  9. Await Next Scan

    We have now processed all users in the list, coming from step d) or from step f), we will use this dummy approval activity to wait for its timeout, i.e., for the next polling interval.

    If the selected addressee does nothing, the workflow will timeout and continue with step b)

    If an admin opens the approval form and presses “Deny Request”, the whole workflow ends with step j)

  10. We’re done

The sample workflow for this article has been attached and can be imported into designer.

It consists of

If you are not working with Novell’s demo environment “Utopia”, you will probably have to modify the trustees of the workflow and the search base for the query.

Developed and tested on Identity Manager version 3.7.0

1 vote, average: 4.00 out of 51 vote, average: 4.00 out of 51 vote, average: 4.00 out of 51 vote, average: 4.00 out of 51 vote, average: 4.00 out of 5 (1 votes, average: 4.00 out of 5)
You need to be a registered member to rate this post.

Disclaimer: As with everything else at NetIQ Cool Solutions, this content is definitely not supported by NetIQ, so Customer Support will not be able to help you if it has any adverse effect on your environment.  It just worked for at least one person, and perhaps it will be useful for you too.  Be sure to test in a non-production environment.

Leave a Reply

Leave a Comment

  • jlazer says:


    I am confusing with the below statement, if you are quering loginDisabled == true, then finally you are setting loginDisabled == true through entity.

    “The sample query is named “CoolSolution_inactiveUsers” and perform a search on the container “OU=users,O=utopia” with a filter “( && ( loginDisabled == true )( loginTime < [param] ))" pls correct it. may be its loginDisabled not equal to True in the query.

Jun 23, 2010
12:01 pm
Active Directory Authentication Automation Cloud Computing Cloud Security Configuration Customizing Data Breach DirXML Drivers End User Management Identity Manager Importing-Exporting / ICE/ LDIF Intelligent Workload Management IT Security Knowledge Depot LDAP Monitoring Open Enterprise Server Passwords Reporting Secure Access Supported Troubleshooting Workflow