Q&A: Transitioning from ActiveSync Forms to Workflows

Bill S writes:

I was interested to see that you prefer rules to forms for A/S operations.

I have been using forms primarily, but now I am building my first A/S rule. I’m having a hard time finding an example, though. Can you point me to any examples of these kinds of activeSync rules?

Specifically: I know how to check out and check in users, but not sure how to update user attributes with activeSync. I imagine I could just iterate through the attributes, but I’m sure there’s a better way… maybe?

Process Rules

The first thing to point out is that the active sync rule is a rule that resolves to the name of a process/workflow.  The rule itself doesn’t actually perform the actions necessary to provision or update a user.

For example, you could have an active sync rule that would examine an employee number and execute the Staff active sync workflow or the Contractor active sync workflow based on a prefix in the employee ID.  Again, the process rule just resolves to the name of a workflow to be executed by active sync. This makes it highly dynamic and you could potentially have many different active sync workflows for a given resource.  In the IDM admin interface, you have the option of either selecting a rule or directly selecting a workflow.  (Directly selecting a workflow is essentially the same thing as having a rule that always returns the same workflow name.)

Below is a sample rule that demonstrates this option.

<Rule id='#ID#Rule:Rule1' name='Rule1' primaryObjectClass='Rule'>
  <block>
    <cond>
      <eq>
        <substr>
          <ref>activeSync.accountId</ref>
          <i>0</i>
          <i>2</i>
        </substr>
        <s>ST</s>
      </eq>
      <s>My Staff Active Sync Workflow</s>
      <s>My Contractor Active Sync Workflow</s>
    </cond>
  </block>
  <MemberObjectGroups>
    <ObjectRef type='ObjectGroup' id='#ID#Top' name='Top'/>
  </MemberObjectGroups>
</Rule>

So if you configured the resources synchronization policy to use “Rule 1″ as the process rule, then IDM would execute that rule for each and every new active sync object.  This in turn would execute the resulting workflow.

Active Sync Workflows

Once an active sync workflow is identified, either rule or directly configuring which workflow to use IDM starts the workflow and provides you with a few preconfigured variables.  The most important one to note is the activeSync variable.  The activeSync variable in a workflow will be populated with the attributes from your resource. For example, if I have a database table that was configured for active sync, then on each poll, IDM will execute the active sync rule and associated workflow once per new or updated row in the database.  This is important to note because it means that you do not have to iterate over the new or updated rows yourself, IDM will do that for you.

So, for each execution of the workflow the activeSync variable will be populated with the data for a single row’s attributes, keeping with the database example.  Below is a quick and short example of an active sync workflow.  (Remember, when you use active sync workflows you must do correlation and confirmation yourself.  Everything is in your control.)

<TaskDefinition
 id='#ID#TaskDefinition:My Staff Active Sync Workflow'
 name='My Staff Active Sync Workflow'
 taskType='Workflow'
 executor='com.waveset.workflow.WorkflowExecutor'
 suspendable='true'
 syncControlAllowed='true'
 execMode='sync'
 execLimit='0'
 resultLimit='0'
 resultOption='delete'
 visibility='runschedule'
 progressInterval='0'>
   <Extension>
    <WFProcess name='My Staff Active Sync Workflow'
     maxSteps='0'>
      <Variable name="accountId">
        <ref>activeSync.accountId</ref>
      </Variable>
      <Variable name='user'/>

      <Activity id='0' name='start'>
        <Transition to='Get Event Type'/>
      </Activity>

      <Activity name='Get Event Type'>
       <Variable name="queryUser"/>
       <Action application='com.waveset.session.WorkflowServices'>
         <Argument name='op' value='queryObjectNames'/>
         <Argument name='type' value='User'/>
         <Argument name='attributes'>
          <map>
            <s>name</s>
            <ref>activeSync.accountId</ref>
          </map>
         </Argument>
         <Return from='queryResult' to='queryUser'/>
       </Action>
       <Transition to='Create User'>
         <isnull>
           <ref>queryUser</ref>
           <!-- queryUser is null if the query returned nothing
              therefore the action must ben a creation -->
         </isnull>
       </Transition>
       <Transition to='Update User'>
         <notnull>
           <ref>queryUser</ref>
           <!-- if the queried user is not null then something was
                found in the repository, this must be an
                update -->
          </notnull>
       </Transition>
      </Activity>

      <Activity name="Create User">
       <Variable name="error"/>
       <Action name="Set up user attribute">
         <expression>
           <!-- here is where you set up the initial user attributes -->
           <setvar name="user.accounts[Other Resource].phone">
             <ref>activeSync.phone</ref>
           </setvar>
           <setvar name="user.accounts[Resource 3].name">
             <ref>activeSync.accountId</ref>
           </setvar>
           <setvar name="user.accounts[Resource 3].telephone">
            <ref>activeSync.phone</ref>
           </setvar>
           <!-- other attributes can be set here, including the
                user.waveset, user.accounts[Lighthouse] and global
                name spaces -->
         </expression>
       </Action>
       <Action process='Provision'>
         <Argument name='op' value='provision'/>
         <Argument name='options' value='$(options)'/>
         <Argument name='user' value='$(user)'/>
         <!-- no error checking is done in this example but it
              is caught -->
         <Return from='WF_ACTION_ERROR' to='error'/>
       </Action>
       <Transition to="end"/>
      </Activity>

      <Activity name='Update User'>
       <Action application='com.waveset.session.WorkflowServices'>
         <Argument name='op' value='checkoutView'/>
         <Argument name='type' value='User'/>
         <Argument name='id' value='$(accountId)'/>
         <Argument name='subject' value='Configurator'/>
         <Argument name='Form' value='Empty Form'/>
         <Return from='view' to='user'/>
       </Action>
       <Action>
         <!-- Apply the changes to the user -->
         <expression>
           <setvar name="user.accounts[Other Resource].phone">
             <ref>activeSync.phone</ref>
           </setvar>
           <setvar name="user.accounts[Resource 3].name">
             <ref>activeSync.accountId</ref>
           </setvar>
           <setvar name="user.accounts[Resource 3].telephone">
             <ref>activeSync.phone</ref>
           </setvar>
           <!-- other attributes can be set here, including the
                user.waveset, user.accounts[Lighthouse] and
                global name spaces -->
         </expression>
       </Action>
       <!-- commit the updates to the repository -->
       <Action application='com.waveset.session.WorkflowServices'>
         <Argument name='op' value='checkinView'/>
         <Argument name='subject' value='Configurator'/>
         <Argument name='view' value='$(user)'/>
         <Return from='WF_ACTION_ERROR' to='action_error'/>
       </Action>
       <Transition to="end"/>
      </Activity>

      <Activity name='end'>
      </Activity>
     </WFProcess>
    </Extension>
    <MemberObjectGroups>
      <ObjectRef type='ObjectGroup' id='#ID#Top' name='Top'/>
    </MemberObjectGroups>
</TaskDefinition>

If you follow the above workflow you can see how a user is updated (or created) using an active sync workflow.  First I query the repository to check to see if a user already exists.  If so then it is an update, if not then it is a creation.  Creating a new user is relatively easy:  set up the attribute map and call the provision sub-process.  Updating a user is slightly more complex but not much.  First the user view must be checked out of the repository.  Then the attributes of the view must be updated with the data from the activeSync namespace.  Finally, the view must be checked back in.

As you can see, this workflow only operates on a single user at a time, which is exactly how active sync presents the data to you.  The workflow gets called by IDM for each and every data change.  IDM iterates through the data calling the workflow over and over until all the changes have been processed.  Then IDM resumes polling the resource for changes.

To specifically address Bill’s question: to update the user attributes the view is checked out, the view is updated inside an “expression” tag and then checked back in.  (See the Action tag between the checkoutView and checkinView action elements.)  Now this is a relatively simple active sync workflow.  Note that it does not deal with user deletions.  Deletes can be performed but they just require more handling in the workflow.  That’s another issue for another time.

This entry was posted in Sun Identity Manager and tagged , , . Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.

2 Comments

  1. Nish
    Posted December 2, 2009 at 9:26 am | Permalink

    Another way to get the same response using ASync Forms is as follows

    
    <Field name="viewOptions.Process">
        <Expansion>
            <switch>
                <ref>feedOp</ref>
                <case>
                    <s>create</s>
                    <s>WF-Sync-Create</s>
                </case>
                <case>
                    <s>update</s>
                    <s>WF-Update</s>
                </case>
                <case>
                    <s>error</s>
                    <s>WF-Sync-Error</s>
                </case>
                <case>
                    <s>delete</s>
                    <s>WF-Sync-Delete</s>
                </case>
            </switch>
        </Expansion>
    </Field>
    

    In above example; activesync feedop value is referenced to identify the type of action – create, update, error or delete and viewOptions.Process is set to different workflows to be called by a “switch-case” usage.

    • Mr. I
      Posted December 2, 2009 at 11:46 am | Permalink

      Thanks Nish! That’s a very good point.

      Ah, the poorly undocumented viewOptions map… Ok, I agree, it can be done with forms. You need to make sure feedop is being set correctly, which likely means you need a delete rule in your AS configuration and that’s assuming IDM can even tell the difference between a create and an update. It’s not always possible for it to make that determination so everything shows up as an update feedop.

      My personal preference is still to use the process rule/workflow. The delete rule, confirmation rules, correlation rules, input form and then still yet another set of workflows each of which should have proper error handling in them… all of which assumes you’ve also only got one type of object, a single user, to update. Maybe I’m just a sucker for simplicity.

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>



  • Will you upgrade to the next version of Sun IDM given the recent Oracle acquisition?

    View Results

    Loading ... Loading ...