Active Directory Driver, Catch Move Subtree's Block, and Reset

0 Likes
over 12 years ago
Active Directory Driver, catch Move subtree's block, and reset:

Novell Identity Manager gets a lot of its unique benefits from the underlying technologies that it uses. One of those is eDirectory which is a truly excellent directory service.



Usually I would be listing benefits of eDirectory at this point (excellent replication, partitioning, filtered replicas, very minor dependencies on a single master server, and what not). I recently ran into an issue with the Active Directory driver where eDirectory causes us a problem in synchronizing events.



The issue has to do with subtree moves. That is, moving a container object in Active Directory, and syncing that move event into eDirectory. This has to do with how eDirectory does moves of container objects. It turns out you have to make the Organizational Unit (OU) a partition boundary, Once the OU is a partition boundary then you can move it in eDirectory.



I personally do not think it is ever a good idea to automate this operation, even if I could figure out how to do it via Identity Manager. (I think that we might be able to modify the attributes that define an OU as a partition boundary, and that might kick off the partitioning process, as I seem to recall you could use LDAP to modify them and cause the partitioning event to happen). The reasoning is simple. Subtree moves, like deletes, and renames generate obituaries and expect everyone in the tree (or at least the replica ring) to be healthy, happy, and talking to each other.



In real life environments, this is as likely as all your family members meeting that requirement! (Who does not have some cousin who is not talking to some other family member? Come on, be honest! People are people so too are eDirectory servers!) That is not to say every instance of an eDirectory tree has problems, but it is so foolish to blindly do such an operation without confirming all is well that I could never recommend it.



For the same reason I am critical of the default behavior of iManager and Designer when it comes to making or deploying a new Driver Set object. The default is to make it a partition. Now I understand the reasoning behind the partitioning requirement. You really should break this small subtree off since configuration information for Identity Manager is all stored there, and it makes replicating it to the servers that need it much easier.



However, blindly doing a partition operation seems like you are just asking for trouble! I always uncheck this option, and only do it manually at a later time. It is possible that the iManager snapins do a health check in the background to validate that it will complete, but I have seen no evidence of that happening.



We ran into this issue on a project and we modeled a solution we would like to try to handle the subtree move event, but ran out of time to implement it in the first release. Our thinking was, make the end result look like a move of a subtree, but do not do a move event in eDirectory.



We define a subtree move as the move of a structural object that has children underneath it. If you try to map that directly to an eDirectory event, you will get an error and it will not synchronize the change.



We consider structural objects to be the following Active Directory object classes:


container

domain

organization

organizationalUnit



Now I have not yet tested if you can make an object of class domain in Active Directory, like you can in eDirectory, without it being a domain boundary. Active Directory is not like eDirectory, in the sense that a eDirectory tree is really contiguous, but the Active Directory tree is really just a series of connected Domains, and when you cross a Domain boundary, it is a different operation than when moving across a partition boundary in eDirectory. But I included the domain object class in anyway, just in case.



When moving objects, the core reason to move them as opposed to deleting and recreating them is to avoid loosing information that is unique to the object. The common example is a password. We could not retrieve the password from a user in Active Directory to then put on the newly created user. So we would want to move that type of object. Organizational Units and other structural objects, usually do not have any unique information stored on them. As I type this though, I see a flaw in this reasoning. Group Policy in Active Directory is stored on the Organizational Unit, and I have yet to discover if we can see that information in eDirectory and whether we can copy it.



Well that is why it is a plan for the future and not done yet. If anyone else has tried this, please let me know so I can save myself some learning curve!



In the interim we needed a way to deal with this sort of event, since in Active Directory, using the Active Directory Users and Computer Microsoft Management Console (ADUC MMC, which is easily started as dsa.msc from a Windows server) it is far to easy to click and drag a structural object by accident from one location to another. Much the days of trying to find files in the file system that users had accidentally clicked, dragged, and dropped into another folder.



Our approach for the interim was pretty straightforward. Catch the move of a structural object, veto that event, and then move the object back to where it was originally. This has the nice side affect of fixing those accidental drag and drop move events.



Jumping ahead, it is pretty darn funny to watch this happen in the ADUC MMC interface. You select a structural object with nothing or many things underneath it, then right click, select Move. You pick the container to move it too, and all looks good. The display updates, and there the organizational unit is in its new location. Count to ten, hit F5 or select Refresh, and it gone, and back to where it was.



Would be fun to watch someone who has no idea what is happening try to do this. Would probably be maddening!



There are a couple of tricky bits to doing this, as I will detail in the sample rule I have for this. For brevity, I removed my comments in the rule that I would normally include, you can add whatever you think is appropriate when you try using it. I highly recommend putting as much detail into the comment field as possible, with a focus on what is being done, but also more importantly why it is being done. For example as I will detail in a few lines, the whole issue with where the old value for where the structural unit USED to be is stored is critical and non-obvious unless you have thought about it. Save the next poor soul who has to work on this the trouble and write down what the story with that is, so they can get up to speed that much faster.



I would place this in the Publisher Event Transform, in either a new policy object, or in an existing one you have been using.





<rule>
<description>[Acme] Block Container Moves and reset them in AD</description>
<conditions>
<or>
<if-class-name mode="nocase" op="equal">Organization</if-class-name>
<if-class-name mode="nocase" op="equal">Organizational Unit</if-class-name>
<if-class-name mode="nocase" op="equal">domain</if-class-name>
<if-class-name mode="nocase" op="equal">container</if-class-name>
</or>
<or>
<if-association op="associated"/>
</or>
<or>
<if-operation op="equal">move</if-operation>
</or>
</conditions>
<actions>
<do-set-local-variable name="ORIG-DN" scope="policy">
<arg-string>
<token-parse-dn dest-dn-format="ldap" length="-2" src-dn-format="ldap">
<token-dest-attr name="DirXML-ADContext"/>
</token-parse-dn>
</arg-string>
</do-set-local-variable>
<do-move-src-object>
<arg-dn>
<token-local-variable name="ORIG-DN"/>
</arg-dn>
</do-move-src-object>
<do-veto/>
</actions>
</rule>




Now lets walk through the rule and explain what is going on. There is value to the notion of leaving the explanation of the rule as an exercise to the reader, but in reality it is probably better for me to spend the time explaining it.




<conditions>
<or>
<if-class-name mode="nocase" op="equal">Organization</if-class-name>
<if-class-name mode="nocase" op="equal">Organizational Unit</if-class-name>
<if-class-name mode="nocase" op="equal">domain</if-class-name>
<if-class-name mode="nocase" op="equal">container</if-class-name>
</or>
<or>
<if-association op="associated"/>
</or>
<or>
<if-operation op="equal">move</if-operation>
</or>
</conditions>



First off we test for the event happening to one of our chosen structural classes. Note that I have it testing the object class first. Events on these object classes are significantly less frequent than anything else, so it is probably a smidgen more efficient to test for that first, before checking the operation type. The reason this can matter is that this rule will be applied to every operation, and moving users is probably more common than any other operations that might happen to the structural classes. Though I could be wrong on this one, and it may in the end make almost no difference at all. But it is worth thinking about when you are defining the conditions for a rule.



Next check and make sure the object is associated. If not, this is going to become a synthetic add, and that would need to be handled totally differently. Besides, I suspect a synthetic add of an entire subtree all at once would not work very well at all.



Finally only do this on move events.




<do-set-local-variable name="ORIG-DN" scope="policy">
<arg-string>
<token-parse-dn dest-dn-format="ldap" length="-2" src-dn-format="ldap">
<token-dest-attr name="DirXML-ADContext"/>
</token-parse-dn>
</arg-string>
</do-set-local-variable>



This step is critical, and it expects an understanding of how renames and moves come across from the Active Directory driver shim, differently than from say, an eDirectory driver.



In eDirectory, you would get a move event that looks something like (I did not have an event handy, so I sort of made this one up, so your mileage may vary, and move events may not be exactly identical to this one):



<nds dtdversion="2.2">
<source>
<product version="3.6.0.4294">DirXML</product>
<contact>Novell, Inc.</contact>
</source>
<input>
<move class-name="Organizational Unit" event-id="6ea92##0" src-dn="\ACME-TREE\ACME\US\North\Michigan\Snowy">
<association>f282355c1b927f4b9b7f768623e26921</association>
<parent>\ACME-TREE\ACME\US\South\Georgia\Peachy</parent>
</move>
</input>
</nds>



The src-dn in the <move> node is the objects current location, after the move, and the DN specified in the <parent> node would be the location it came from.



Then you could just select the value of the parent node with XPATH and use that to reverse the move, easy peasy.



It would look something like this:



<do-move-src-object>
<arg-dn>
<token-xpath expression="parent/@src-dn"/>
</arg-dn>
</do-move-src-object>



But the src-dn from the <parent> node, that the XPATH of parent/@src-dn returns will be the current DN in AD, as we will see below.



However in Active Directory, you get a different looking event:



<nds dtdversion="2.2">
<source>
<product version="3.6.0.4294">DirXML</product>
<contact>Novell, Inc.</contact>
</source>
<input>
<move class-name="organizationalUnit" event-id="ADLink##1201fb6ea92##0" src-dn="OU=Snowy,OU=Michigan,OU=North,OU=US,DC=acme,DC=corp">
<association>f282355c1b927f4b9b7f768623e26921</association>
<parent src-dn="OU=Michigan,OU=North,OU=US,DC=acme,DC=corp">
<association>635cbaf92a11e6489e520072afad38ea</association>
</parent>
</move>
<rename class-name="organizationalUnit" event-id="ADLink##1201fb6ea92##1" src-dn="OU=Snowy,OU=Michigan,OU=North,OU=US,DC=acme,DC=corp">
<association>f282355c1b927f4b9b7f768623e26921</association>
<new-name>Snowy</new-name>
</rename>
</input>
</nds>



You get both a move and a rename event. By itself that would be easy to handle, just ignore the rename and only handle the move event.



But if you look at the event carefully, you will see that the <parent> node does not have a value as such, rather it has an XML attribute src-dn (we reference that in XPATH as -dn) that contains the DN of the objects new location, minus one node, aka it's parent node.



The <association> node is there so that we can identify in the eDirectory tree, the object that is being referenced.



This is not spectacularly helpful, as we get told where the object is now (the <move> events -dn value) and then we get told again in the <parent> node where it is now. Thanks for nothing there AD driver shim! I do not know what causes events to look like this coming from Active Directory, but no doubt it is something related to how Active Directory processes moves. If you happen to have an insight into the reason behind this, I would be very interested in knowing what it is!



Thus the local variable ORIG-DN that I set. Go look in the destination (eDirectory) for the DirXML-ADContext attribute of the associated object. (Here again is why the test for Associated in the condition blocks, only associated objects should have this attribute, therefore I do not have to test and see if I get it back successfully or not, I already tested that it is associated in the beginning).



Note that we ParseDN the result, using length of -2 to chop off the name of the object (You can see this article: Examples of using the ParseDN Token in Identity Manager
for some examples of how to use ParseDN). Also, we specify ldap as the destination format, since we want it in a format the Active Directory driver can handle which is LDAP format.



Now we know where the object last WAS. From the current operation document we know where it is going (@src-dn or parent/@src-dn plus the name of the object) or at least where it is now, post move or rename.



Then in that case, lets just move it right back to where it should be!



<do-move-src-object>
<arg-dn>
<token-local-variable name="ORIG-DN"/>
</arg-dn>
</do-move-src-object>



We use a Move action, and specify the destination as the ORIG-DN, which we already chopped down to the parent container using ParseDN as explained a moment ago.



Viola! When it happens it is pretty darn funny to watch!



Finally, we should block this operation from proceeding into the Identity Vault since it is not being allowed, thus a do-veto action.



<do-veto/>


What a nice simple rule to help keep things sane in your tree. On a side not, you could also use this to block User move events too, just by changing the condition blocks to look for Users as well. That might be useful if Active Directory admins are not supposed to move users.



What I have not tested, is what to do when a move is across a Domain boundary in Active Directory. There is a Microsoft tool to do it, and if you had a couple of drivers running, one per domain, this is a case you would have to handle. Actually, I still have to handle it, but the second domain driver is not yet up and running, so I have no idea what it looks like yet, to decide how to handle it.



Labels:

How To-Best Practice
Comment List
Anonymous
  • You ask a very interesting question. The answer is, it depends. ALOT.

    It turns out the shipping Active Directory driver configurations have a series of issues with them. Novell's sort of official policy on the default configuration is that they are like the Pirates Code... They are basically recommendations and starting points.

    I recently worked through the entire AD driver, finding EVERY place that the DirXML-ADContext and DirXML-ADALiasName get set, in order to redo it to use a multivalued Path syntax attribute, so that if you have two or more Active Directory drivers, they can properly interact and not stomp all over. I started from a base of the IDM 4 package version of the AD driver, which I hear WILL be supported (which in NTS's defense is because Packages ought to actually be supportable, where as configurations are almost impossible to support in a scalable fashion). I have a long article series in queue on this exact topic, but it is buried behind 20 someodd other articles, so it may take a while to make it onto the site. (They are only publishing 1-2 a week of mine, and I have 28 in queue right now).

    So I am now somewhat familiar with this issue. If I could show you the articles you could see it your self. :)

    In short, I think the answer on a create, for an OU, or on a merge of an OU, it ought to be in reaction to the event in the Input Transform. There is a rule itp-SubscriberUserAdd that should do it.

    However, only later (V6 and higher?) AD Driver configs include this rule.

    To summarize all the spots it MIGHT be, at least in the IDM4 Package version:

    DirXML-ADContext:

    Input Transform:
    NOVLADDCFG-itp-SubscriberUserAdd 1 rule

    Publisher Channel:
    Event Transformation:
    NOVLADDCFG-pub-etp-HandleMovesAndRenames 3 rules

    Create Policy Set:
    NOVLADDCFG-pub-cp 1 rule

    Command Transform:
    NOVLADDCFG-pub-ctp 2 rules

    Subscriber Channel:
    Command Transform:
    NOVLADENTEX-sub-ctp-EntitlementsImpl 2 rules
    NOVLADDCFG-sub-ctp-UserNameMap 1 rule.


    DirXML-ADAliasName:

    Subscriber Channel:
    Create Policy Set:
    NOVLADDCFG-sub-cp-Users, 1 rule
    NOVLADDCFG-sub-cp-Groups 1 rule

    Command Transform:
    NOVLADENTEX-sub-ctp-EntitlementsImpl 2 or more rules)

    Publisher Channel:
    Command Transform:
    NOVLADDCFG-pub-ctp-UserNameMap 2 rules
    NOVLADDCFG-pub-ctp 2 rules

    Have fun!
  • I tested this by first associating an OU, then moving it in AD. I found that the OU object in eDirectory did not have the DirXML-ADContext attribute, so the driver was unable to move it back to its original location in AD. Do you know what process causes the DirXML-ADContext attribute to be added?
  • So this gets published on Aprils Fools. The article is actually serious, but if you put it in place, and your AD admins like to do subtree moves, it would be perfectly appropriate for the day!

    It is really funny to watch as the change happens, you see it, a few seconds later, it reverts back to where it was.

    Certain to drive an admin up the wall!
Related Discussions
Recommended