Replacing OpenLDAP MA with PS MA

By replacing the OpenLDAP XMA with the Søren Granfeldt’s PowerShell MA I gained 20-30% performance improvement, got delta import support, and at the same time reduced the amount of managed code by hundreds of lines.

One of my customers are using OpenDJ as a central LDAP directory for information about users and roles. In order to import this information into FIM we have been using the OpenLDAP XMA from codeplex. But, since this MA is built using the deprecated ECMA 1.0 framework and also have some issues causing me to have to re-write it to a customer specific solution, I decided to move away from it and start using a PowerShell MA instead.

I will try to point out the most critical parts of the PowerShell I used if you would like to try this yourself.
But you can also download a sample script to look at if you like.

This PS MA sends three parameters as input to the PowerShell script
param ($Username,
$Password,
$OperationType)

but also holds a RunStepCustomData parameter that can be useful when doing delta imports. If you look at the example downloads
listed at this PS MA homepage you will see some examples using this RunStepCustomData parameter. In this particular case I choose to store the delta cookie in a file instead. Allowing the FIM administrator some flexibility to manually change the cookie value if needed.

Let’s move on to the script then.

I use the System.DirectoryServices.Protocols to do the LDAP queries so I need to start by adding a reference to that assembly.
Add-Type -AssemblyName System.DirectoryServices.Protocols

I need to define the credentials I will use and the LDAP Server to connect to.
$Credentials = New-Object System.Net.NetworkCredential($username,$password)
$LDAPServer = "ldap.company.com"

With that we can now create the LDAP connection to the OpenDJ server.
$LDAPConnection = New-Object System.DirectoryServices.Protocols.LDAPConnection($LDAPServer,$Credentials,"Basic")

Now it’s all about defining the search filters and search the LDAP for the objects we are interested in.It could look something like this
$BaseOU = "ou=People,dc=company,dc=com"
$Filter = "(&(objectClass=EduPerson)(!EduUsedIdentity=*))"
$TimeOut = New-Object System.TimeSpan(1,0,0)
$Request = New-Object System.DirectoryServices.Protocols.SearchRequest($BaseOU, $Filter, "Subtree", $null)
$Response = $LDAPConnection.SendRequest($Request,$TimeOut)

Within the $Response object we now have the result from our search and can iterate through it and set the values to the object returned to the Management Agent.
ForEach($entry in $Response.Entries) {
$obj = @{}
$obj.Add("OpenDJDN", $entry.DistinguishedName)
$obj.Add("objectClass", "eduPerson")
If($entry.Attributes["cn"]){$obj.Add("cn",$entry.Attributes["cn"][0])}
If($entry.Attributes["uid"]){$obj.Add("uid",$entry.Attributes["uid"][0])}
$obj}

You now have a working import from OpenDJ. But we have not added the delta support just yet. Once you have verified that your full import is working you can start to extend your script to also support delta imports from OpenDJ.

OpenDJ works with a changelog that increases the changenumber for each entry. In order to read the changelog from the correct changenumber we need to store the last known changenumber. In my example I store this in what I call the cookie file.
$CookieFile = "D:\PS-Scripts\OpenDJ\Cookie.bin"
In this file I store the integer value of the last changenumber we have worked with.
To read the value I use the following line.
$val= [int](Get-Content –Path $CookieFile)
We can then search the changelog for entries with higher changeNumber than we have already seen.
It would look something like this.

$ChangeFilter = "(&(targetdn=*$($BaseOU))(!cn=changelog)(changeNumber>=$($changeNumber)))"
$ChangeTimeOut = New-Object System.TimeSpan(0,10,0)
$ChangeRequest = New-Object System.DirectoryServices.Protocols.SearchRequest("cn=changelog", $ChangeFilter, "Subtree", $null)
$ChangeResponse = $LDAPConnection.SendRequest($ChangeRequest,$ChangeTimeOut)

We now run into two problems that need to be resolved.
First: The changelog might give me the same object twice if multiple changes has been done to the object.
Second: The changelog object does not contain all the attibutes I need to return to the MA.

The first problem is solved by filtering the response from the search and only collect unique objects.
In my case I used the following line to do that.
$UniqueDN = $ChangeResponse.Entries | ForEach{$_.attributes["targetdn"][0]} | Get-Unique
We then need to iterate through these unique DNs to get the actual objects and get the attributes we need.
ForEach($DN in $UniqueDN){
$GetUserReq = New-Object System.DirectoryServices.Protocols.SearchRequest($DN, "(objectClass=EduPerson)", "Base", $null)
$GetUser = $LDAPConnection.SendRequest($GetUserReq)
If($GetUser.Entries.Count -eq 0){continue}
$entry = $GetUser.Entries[0]
If($entry.Attributes["EduUsedIdentity"]){continue}
$obj = @{}
$obj.Add("OpenDJDN", $entry.DistinguishedName)
$obj.Add("objectClass", $Class)
If($entry.Attributes["cn"]){$obj.Add("cn",$entry.Attributes["cn"][0])}
If($entry.Attributes["uid"]){$obj.Add("uid",$entry.Attributes["uid"][0])}
$obj}

One thing we need to also remember to do is to save the last changenumber back to our cookie file for the next run.
$LastChangeNumber = [int]$ChangeResponse.Entries[($ChangeResponse.Entries.Count -1)].Attributes["changeNumber"][0]
Set-Content -Value $LastChangeNumber –Path $CookieFile

You also need to remember to do that during your full import runs. So within the script where you process full imports you need to add a search to get the current latest entry in the changelog.

I have attached a demoscript for you to download to get you started in your own exploration of using PowerShell to work with OpenDJ. If you look at it you will likely find that it can be optimized in some ways. But I kept it this way to make it easy for non PowerShell geeks to be able to read it and understand it.

One thing you might have notice is that I have not added support to detect deletes. This is just a matter of adding some logic to read the changetype in the changelog, but I have not yet got the time to do this. At this customer deletes will be detected every night when full import is running anyhow.

ForEach vs ForEach-Object in PowerShell

In a current project where I use the PowerShell Management Agent from Søren Granfeldt to import information from a large LDAP catalog I discovered that there are some performance problems if you use PowerShell the incorrect way. One of these things is the use of ForEach vs ForEach-Object when enumerating a large collection of objects.

Searching the web I found this article from Anita, that helped me.

The results was stunning!

Look at this scenario where I search for objects in the LDAP catalog and the search returns +20 thousand objects.

I got $Response.Entries.Count is 21897

I then use the Measure-Command to compare the ForEach with the ForEach-Object way of iterating the objects.

First let’s see how the  generic ForEach-Object{} is doing.

(Measure-Command{$Response.Entries | ForEach-Object{$_.DistinguishedName} }).TotalMilliseconds
Resulted in: 1020 milliseconds

And then let’s see how ForEach(){} is doing when defining the type of object in the collection

Defining the entry object type like this
[System.DirectoryServices.Protocols.SearchResultEntry]$entry

and then measuring the result

(Measure-Command{ForEach($entry in $Response.Entries){$entry.DistinguishedName} }).TotalMilliseconds
Resulted in: 98 milliseconds

A performance factor of 10!.

And since a few of my collections in this project was actually returning more than 200 thousand objects you can imagine that I actually was able to see some effect.

FIM 2010 R2 SP1 – 4.1.3114.0 – is Released

Today Microsoft made the FIM 2010 R2 SP1 release available for general download. Read all about the news in SP1 in KB2772429.

I myself ran into a situation at a customer yesterday where the binding redirect for the new version where not applied by the SP1 installer. As described in the KB this happens if some modifications has been done to the configuration files.

Updating the three files

  • miisserver.exe.config
  • mmsscrpt.exe.config
  • dllhost.exe.config

so that the bindingRedirect section had the newVersion=”4.1.0.0″ solved the problem and made the ECMA 2 adapters in the solution start working again. I actually first made the mistake of using 4.1.3114.0 as newVersion, since this is the Product Version of the new Microsoft.MetadirectoryServicesEx.dll file. But this is not the “Build Number” that we are talking about. The Build Number you need to redirect to is the 4.1.0.0 that you can see in the GAC on the Microsoft.MetadirectoryServicesEx.dll.

Working with SQL aliases

In all FIM implementations you should use SQL aliases and not point to the actual SQL server instances or servers in your configuration. The problem is that SQL aliases have two versions, one for 64-bit (the default) and one for 32-bit.

One time you will hit the 32-bit is if you are working with Visual Studio (32-bit application) or 32-bit ODBC drivers.

To manage your aliases you need to use the correct version of cliconfg.exe. If you just start a command prompt and run cliconfg you will be able to manage the 64-bit aliases. The full path is C:\Windows\System32\cliconfg.exe.

But if you need to manage the 32-bit aliases you need to start the C:\Windows\SysWOW64\cliconfg.exe.

Another problem you might run into is having to copy your alias settings from one machine to another. For example from your test FIM to your production FIM. The easiest way I have found is to copy the two registry keys that holds this information between the machines.

  • HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSSQLServer\Client\ConnectTo
  • HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\MSSQLServer\Client\ConnectTo

Hopefully this post will make it easier for you to work with your SQL aliases.

Deprecated Features and Planning for the Future of FIM

The product team has now started to reveal some news about the next version of FIM. First out is a list of deprecated features to prevent us from using them in our current projects. Making a transition to the next version easier.

Read about deprecated features in FIM on TechNet.

Positive feedback on my FIM 2010 R2 Book

I am getting lots of positive feedback from readers all over the world. Feedback that really warms my heart. I just can’t help sharing one with you.

Kent, I’m super impressed with your book. Authoritative information in combination with a collegial writing style is a rare find. One of the ways you distinguish your writing from other tech books is the way you inject little tips to help keep the boat pointed straight up the river:

FIMR2BookReferenceExample1

Looking forward to reading every page…
-Bill Boswell

Win A Free Copy of Packt’s FIM 2010 R2 Handbook

Win A Free Copy of Packt’s Microsoft Forefront Identity Manager 2010 R2 Handbook e-book.

I am pleased to announce that I have teamed up with Packt Publishing and are organizing a give away especially for you. All you need to do is just comment below the post and win a free copy of Microsoft Forefront Identity Manager 2010 R2 Handbook. Two lucky winners stand a chance to win an e-copy of the book. Keep reading to find out how you can be one of the Lucky One.

5368EN_FIM2010R2Handbook

Overview of Microsoft Forefront Identity Manager 2010 R2 Handbook eBook

  • Prerequisites for installing FIM 2010 R2
  • How to install and scale the solution
  • Implementation of User and Group Management including Self-Service

How to Enter?

[Competition is Closed: Congratulations to the Winners Paulo H. Campos and Joakim Ingesson!]

All you need to do is head on over to this page and look through the product description of these books and drop a line via the comments below to let us know what interests you the most about this book. It’s that simple.

DeadLine:

The contest will close on October 31 2012. Winners will be contacted by email, so be sure to use your real email address when you comment!

Hotfix rollups for FIM 2010 and FIM 2010 R2

Microsoft has released new hotfix rollups for both FIM 2010 and FIM 2010 R2. Among other things it is said to fix the “stopped-server” error in FIM 2010 Synchronization Service.

Read more at FIM 2010 hotfix rollup (build 4.0.3627.2) and at FIM 2010 R2 hotfix rollup (build 4.1.2515.0)

FIM 2010 R2 is RTM

Yesterday FIM 2010 R2 RTM release was made available on MSDN. Within a week it should be available on all license channels.

If you have been playing around with the RC release that was available on Microsoft Connect, you will not be able to upgrade to RTM. Read about that and other interesting stuff in the FIM 2010 R2 Release Notes.

BHOLD Suite for FIM 2010 avialable for download.

Microsoft BHOLD Suite extends the capabilities of FIM 2010 by adding role-based access control to FIM 2010, enabling organizations to define user roles and to control access to sensitive data and applications in a way that is appropriate for those roles.

Yesterday the BHOLD Suite addon for FIM 2010 was made available on MSDN. Within a week it should be available on all license download sites.

Please read more about it on http://aka.ms/BHOLD.