Background:
I work for a company that hosts Lync. We have a web based portal that allows clients to setup their Lync accounts (creates OU and users, enables for conferencing and enterprise voice etc.). The portal calls a web service which then calls a PowerShell script that has functions for dealing with the AD and with Lync.
The Problem:
The portal sometimes kicks off simultaneous calls to the web service that end up running the same backend PowerShell scripts. I make extensive use of logging in the web service for debugging purposes. From the log fragment below you can see that two simultaneous calls were made on separate threads ( [5] & [6] ). The lines in bold are the command lines sent to the PowerShell script and are written out in the line of code that immediately precedes the line to execute the PowerShell script. If you follow the threads you can see that thread [6] returned nothing meaning that the user didn’t exist (problem occurs regardless of if the users exist or not). But thread [5] fails with the “invalid enumeration context” message. If you try to rerun the command line that failed it will work. The problem only occurs with simultaneous calls to the Get-CTUser function. Basically Get-CTUser is a wrapper for the AD PowerShell Get-ADUSer cmdlet.
2012-03-23 13:11:55,404 [5] DEBUG – Begin GetCTUser
2012-03-23 13:11:55,404 [6] DEBUG – Begin GetCTUser
2012-03-23 13:11:55,435 [5] INFO – Getting user willy@uc.com
2012-03-23 13:11:55,435 [6] INFO – Getting user admin@uc.com
2012-03-23 13:11:55,451 [5] DEBUG – Begin RunScript
2012-03-23 13:11:55,451 [6] DEBUG – Begin RunScript
2012-03-23 13:12:08,338 [5] DEBUG – Get-CTUser -container ‘geeks’ -UPN ‘willy@uc.com’
2012-03-23 13:12:08,354 [6] DEBUG – Get-CTUser -container ‘geeks’ -UPN ‘admins@uc.com’
2012-03-23 13:12:08,479 [6] DEBUG -
2012-03-23 13:12:08,479 [6] DEBUG – End RunScript
2012-03-23 13:12:08,479 [6] DEBUG – End GetCTUser
2012-03-23 13:12:08,588 [5] ERROR – GetCTUser The server has returned the following error: invalid enumeration context.
Some searching of the Internet for “invalid enumeration context” showed up several references to the problem. Most of the references I found were from people that were doing things like porting users so they were dealing with large results sets with hundreds or thousands of users and most of the solutions involved setting a page size but that didn’t always work. In my case I’m only after one user but still the problem seems the same. I can’t verify that it is a bug or not but either way I can’t have my PowerShell scripts sending errors back to the portal.
So after some research I decided to use DirectorySearcher to get the user and this approach seems to be working fine in my testing. The DirectorySearcher is really quite easy to use. Basically all you need to do is setup the LDAP path, tell it how deep to search, add which properties to return then tell it what to search for.
function Get-CTUser(
[Parameter(Mandatory = $true)][string]$container,
[Parameter(Mandatory = $true)][string]$upn
)
{
try
{
$Domain = New-Object System.DirectoryServices.DirectoryEntry("LDAP://ou=" + $container + "," + $defaultHostingPath)
$Searcher = New-Object System.DirectoryServices.DirectorySearcher($Domain)
$Searcher.PageSize = 200
$Searcher.SearchScope = "subtree"
# Setup the filter to only find the one user
$Searcher.Filter = "(userPrincipalName=" + $upn + ")"
# Setup which fields we want to return
$Attributes = @("distinguishedName", "GivenName", "lastLogon", "mail", "Name", "SamAccountName", "sn", "telephoneNumber", "userAccountControl", "UserPrincipalName")
# Load the fields inthe properties array
ForEach($Attribute In $Attributes)
{
$Searcher.PropertiesToLoad.Add($Attribute) > $Null
}
# Execute the search
$Results = $Searcher.FindAll()
# Build the reyrn string
ForEach ($Result in $Results)
{
$ret = $ret + "`nDistinguishedName`t:" + $Result.Properties.Item("distinguishedName")+ "`n"
$ret = $ret + "GivenName`t`t:" + $Result.Properties.Item("GivenName")+ "`n"
$ret = $ret + "lastLogon`t`t:" + $Result.Properties.Item("lastLogon")+ "`n"
$ret = $ret + "mail`t`t`t:" + $Result.Properties.Item("mail")+ "`n"
$ret = $ret + "Name`t`t`t:" + $Result.Properties.Item("Name")+ "`n"
$ret = $ret + "SamAccountName`t`t:" + $Result.Properties.Item("SamAccountName")+ "`n"
$ret = $ret + "Surname`t`t`t:" + $Result.Properties.Item("sn")+ "`n"
$ret = $ret + "telephoneNumber`t`t:" + $Result.Properties.Item("telephoneNumber")+ "`n"
$ret = $ret + "userAccountControl`t:" + $Result.Properties.Item("userAccountControl")+ "`n"
$ret = $ret + "UserPrincipalName`t:" + $Result.Properties.Item("UserPrincipalName")+ "`n"
}
return $ret
}
catch
{
Throw (new-object Exception("Exception Get-CTUser: $_"))
}
}
The lines of script were I’m building the $ret string is done that way so that I could mimic as close as possible the way that Get-ADUser returns its results. I didn’t want to break anything upstream that parsed the result values.
Like this:
Be the first to like this post.