Wednesday, July 01, 2009

ADSI Guru

So the past few days were spent struggling to write a binary attribute into my Active Directory instance. Java isn't too clever when it comes to binary objects yet I seemed to be capable of generating a perfect binary object which I could write into ANY other LDAP compliant repository. Of course, Active Directory is merely LDAPpy - as I christened it yesterday.

Most of my efforts were probably in vain as the IBM Tivoli Identity Manager Active Directory Adapter does not support binary objects. Generating such an object an assigning it to a person record within ITIM would have been futile as would trying to generate the object on the fly during workflow.

The fact still remains that I absolutely must get this binary attribute into Active Directory as part of the provisioning process. And to that end, I wrote my first complete ADSI script today. I've spent years working on unix boxes and working with "real" LDAPs. To be scripting in VBScript and attempting to update AD was rather alien. I learned a thing or too on the way. VBScript desperately needs to know the precise size of your arrays, for example. Java, as we know, is fairly tolerant to lazy coding. VBScript desperately needs object types to be precisely as it expects whereas Java is quite tolerant when it comes to determining the difference between 1, "1" and "one"!

Design
I decided that I could write an ADSI script that would commit these binary objects to my accounts after they had been created with the AD Adapter. But I don't merely want to call this process as part of workflow. I've decided to take it a step further and create a separate service and adapter which will perform this function. ITIM, calling ITDI to write these attributes (which are based on attributes assigned to person objects as strings anyway) and ITDI calling a VBScript to commit the write.

Args
My ADSI script take command line arguments, of course. Things like the bind DN & password; the target AD instance; the target user; the raw data to be converted into binary. I'm pleased with the args processing. Not quite the way I would do it in shell scripting, but easy enough:

Dim args
Dim sBindUID

Set args = WScript.Arguments.Named
sBindUID = args.Item("bindUID")


I can now call the script as such:

cscript myscript.vbs /bindUID:Administrator

Binding
Next, I bound to the AD instance using scripting methods I found by Googling:

Dim oDS
Dim oAuth
Dim oConn
Set oDS = GetObject("LDAP:")
Set oAuth = oDS.OpenDSObject(sServer, sBindDN, sPassword, &H0200)
Set oConn = CreateObject("ADODB.Connection")
oConn.Provider = "ADsDSOObject"
oConn.Open "Active Directory Provider", sBindDN, sPassword


And then attempted to find the target for my update:

sSearchObject = "<" & sServer & ">;(" & sTarget & ");name,ADsPath;subtree"
set oRS = oConn.Execute(sSearchObject)
Set oUser = GetObject(oRS.Fields(1).Value)
oUser.GetInfo


Updating
Then came the tricky bit. I had a multi-valued attribute which required each attribute to be converted into a binary stream. I shan't bore you with the binary conversion as it is convoluted in the extreme. However, the multi-valued issue required use of the PutEx method:

oUser.PutEx ADS_PROPERTY_UPDATE, "mybinaryattribute", aEntityGUIDs

And, of course, my aEntityGUIDs object need to be an array of a size equal to the number of values in the array. Time for some Redim. Redim, of course, is not something I've ever had to do in Java! A goodly two hours were spent pondering my failure to commit my values to AD before it dawned on me that the size of the array may have an impact.

I tarted up the code to add a logging mechanism. ERROR, FATAL, WARN, INFO and DEBUG messages are written to a log file by calling a little function that includes this code:

Stuff = dateStamp & ": " & loggedString
Set myFSO = CreateObject("Scripting.FileSystemObject")
Set WriteStuff = myFSO.OpenTextFile("myvbs.log", 8, True)
WriteStuff.WriteLine(Stuff)
WriteStuff.Close

I'm not 100% sure but I'm fairly convinced that others would be fit to declare themselves VBScript/ADSI gurus. I shan't do likewise but I now have a better understanding of the pitfulls of VBScripting AD access.

NOTES
I haven't shown all the Dim statements for the objects defined in the code above - I'm sure you can work that out for yourself.

No comments: