So, I had a pretty interesting support case the other day. Cx stated that people picker wasn’t returning any results for users in a trusted domain. I was thinking ok I mean how hard could this be……. well so, my brain hurts now . So hopefully this will save someone else a huge headache.
Ok so let me paint the scenario for you, but first what is the issue?
Problem:
When you perform a people picker search for a user account that resides in a trusted domain no results are returned and SharePoint reports “User SPPrincipalInfo doesn’t seem to have a user principal name value” for our user we queried for
Scenario:
-
We have two domains with a two-way external trust configured. So, everyone trusts everyone here it’s a happy family.
- Contoso.com
- Fabrikam.com
- SharePoint Servers, SharePoint Farm Service Accounts, and User accounts reside in the Contoso.com domain
- For purposes of this article only user accounts reside in the Fabrikam.com domain
-
We are using a SAML provider that is using an out of the box AD claim provider. This is created by SharePoint when we create our trusted identity token issuer using the “-UseDefaultConfiguration” parameter like so:
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2(“C:\temp\ADFSSigningCert.cer”)
New-SPTrustedRootAuthority -Name “Token Signing Cert” -Certificate $cert
$ap = New-SPTrustedIdentityTokenIssuer -Name “ADFS SAML Provider” -Description “SAML Authentication for SharePoint” -realm “urn:sharepoint:www.contoso.com” -ImportTrustCertificate $cert -SignInUrl “https://adfs.Contoso.com/adfs/ls” -UseDefaultConfiguration -IdentifierClaimIs USER-PRINCIPAL-NAME;
$ap = Get-SPTrustedIdentityTokenIssuer -Identity “ADFS SAML Provider”;
$ap.ProviderRealms.Add(“https://www.contoso.com”, “urn:sharepoint:www.contoso.com”);
$ap.ProviderSignOutUri = “https://adfs.contoso.com/adfs/ls“;
$ap.ClaimProviderName = “ADFS SAML Provider”;
$ap.UseWReplyParameter = $true;
$ap.Update();
Note:
The -UseDefaultConfiguration parameter will create a claim provider for you. Since this is our code (Microsoft) we are able to sink our hooks into AD to perform the user lookup based on the information entered into the people picker. This allows us to return a friendly display name instead of the awful identifying claim, which is some cases could be an employee ID
-
We have not disabled the out of the box AD Claim provider, but we have hidden the provider like so:
-
Do not disabled this claim provider it will break a lot of things that required windows authentication
$cpm = Get-SPClaimProviderManager
$ad = get-spclaimprovider -identity “AD”
$ad.IsVisible = $false
$cpm.Update()
-
-
We then must configure the people picker to search both domains like so:
- If your domain short name differs from the first part of your domains FQDN you will need to disable netbios DC resolve.
-
For example, your FQDN for your domain is “Contoso.com” the Pre-Windows 2000 value for your domain is “Corp” and you log in as “Corp\user”.
-
Since “Corp” does not match “Contoso” we must run this PS and set the proper value for the attribute “ShortDomainName”
Add-PSSnapin Microsoft.SharePoint.PowerShell -ea silentlycontinue
$farm = get-spfarm
$farm.Properties
$farm.Properties[“disable-netbios-dc-resolve”] = $true
$farm.Properties
$farm.Update()
-
Otherwise we only have the need to run this PowerShell
$wa = Get-SPWebApplication https://www.contoso.com
$adsearchobj1 = New-Object Microsoft.SharePoint.Administration.SPPeoplePickerSearchActiveDirectoryDomain
$adsearchobj1.DomainName = “Contoso.com”
$adsearchobj1.ShortDomainName = “Contoso” #Optional
$adsearchobj1.IsForest = $true #$true for Forest, $false for Domain
$wa.PeoplePickerSettings.SearchActiveDirectoryDomains.Add($adsearchobj1)
$wa = Get-SPWebApplication https://www.contoso.com
$adsearchobj2 = New-Object Microsoft.SharePoint.Administration.SPPeoplePickerSearchActiveDirectoryDomain
$adsearchobj2.DomainName = “Fabrikam.com”
$adsearchobj2.ShortDomainName = “Fabrikam” #Optional
$adsearchobj2.IsForest = $true #$true for Forest, $false for Domain
$wa.PeoplePickerSettings.SearchActiveDirectoryDomains.Add($adsearchobj2)
$wa.Update()
-
Note:
-
If you’re not certain if your short name differs from your FQDN here is some PowerShell to help you.
- This will only return the local domain information though.
Get the FQDN domain name
(gwmi win32_computersystem).Domain
- This will only return the local domain information though.
Get the Short name for the domain
(net config workstation) -match ‘Workstation domain\s+\S+$’ -replace ‘.+?(\S+)$’,’$1′
-
The -UseDefaultConfiguration parameter will create a claim provider for you. Since this is our code we are able to sink our hooks into AD to perform the user lookup based on the information entered into the people picker. This allows us to return a friendly display name instead of the awful identifying claim, which is some cases could be an employee ID.
Search with people picker for the user account in the fabrikam.com domain
- Attempt to grant permissions to the user in the Fabrikam domain “PeoplePickerTest” using the people picker from here: https://www.contoso.com/_layouts/15/user.aspx
- Within People Picker search for “peoplepickertest”
-
Search result:
- No results found is the result
What You’ll see in the ULS logs and Network Monitor trace
Looking at netmon trace we see the first call
24167 12:47:08 PM 11/17/2016 13.6861171 w3wp.exe 10.19.141.20 RHWDC18.contoso.com LDAPSASLBuffer LDAPSASLBuffer:BufferLength: 667, AuthMechanism: GSS-SPNEGO {LDAP:686, TCP:682, IPv4:676}
Here is our query to the Contoso.com domain
Filter: (|(&(objectCategory=person)(|(anr=peoplepickertest*)(SamAccountName=peoplepickertest*)(userPrincipalName=peoplepickertest*)))(&(objectCategory=group)(BIT_AND: (groupType)&2147483648)(|(anr=peoplepickertest*)(SamAccountName=peoplepickertest*))))
Here are the attributes we are requesting
Attributes: ( objectSID )( mail )( mobile )( displayName )( title )( department )( proxyAddresses )( cn )( samAccountName )( groupType )( userAccountControl )( distinguishedName )( objectClass )( userPrincipalName )( msexchmasteraccountsid )
24168 12:47:08 PM 11/17/2016 13.6869698 w3wp.exe RHWDC18.contoso.com 10.19.141.20 LDAPSASLBuffer LDAPSASLBuffer:BufferLength: 1027, AuthMechanism: GSS-SPNEGO {LDAP:686, TCP:682, IPv4:676}
Here is our first search result
SearchResultEntry: CN=peoplepickertest@farbrikam.com,OU=Fabrikam,OU=Contacts from SimpleSync,OU=User,OU=Contoso,DC=contoso,DC=com
Here are the attributes returned
+ PartialAttribute: objectClass=( top )( person )( organizationalPerson )( contact )
+ PartialAttribute: cn=( peoplepickertest@Fabrikam.com )
+ PartialAttribute: title=( Business Application Consultant )
+ PartialAttribute: distinguishedName=( CN=peoplepickertest@Fabrikam.com,OU=Fabrikam,OU=Contacts from SimpleSync,OU=RHDC,OU=Contoso,DC=contoso,DC=com )
+ PartialAttribute: displayName=( Jones, Bob (Fabrikam) )
+ PartialAttribute: department=( AD_Medmanagement )
+ PartialAttribute: proxyAddresses=( x500:/o=Contoso.com/ou=First Administrative Group/cn=Recipients/cn=peoplepickertest.Farbrikam.com1 )( x500:/o=Contoso/ou=First Administrative Group/cn=Recipients/cn=peoplepickertest@Fabrikam.com1 )( X500:/o=Contoso/ou=First Administrative Group/cn=R
+ PartialAttribute: mail=( peoplepickertest@Farbrikam.com )
+ PartialAttribute: msExchMasterAccountSid=( )
In ULS we’ll see these entries for this search
SearchFromGC name = Contoso.com. start
GetAccountNameFromSid “0x0105000000000005150000007B005C46C7C60121369482B9798A0500” start
“0x0105000000000005150000007B005C46C7C60121369482B9798A0500” returned. returnValue=True
GetAccountNameFromSid “0x0105000000000005150000001741550EDE09584B0363450079C20000” start
GetAccountNameFromSid “0x0105000000000005150000001741550EDE09584B0363450079C20000” returned. returnValue=True
SearchFromGC name = Contoso.com. returned. Result count = 2
0x0105000000000005150000001741550EDE09584B0363450079C20000 = Userid:S-1-5-21-240468247-1264060894-4547331-49785
We have returned an entry with the SID S-1-5-21-240468247-1264060894-4547331-49785
This SID translates to fabrikam\peoplepickertest, but it was returned from the Contoso.com domain
We can see in the network monitor trace that this is returned as a contact with the attribute value msExchMasterAccountSid and no UPN
We then call out to the Fabrikam.com domain
39482 12:47:20 PM 11/17/2016 25.4775491 w3wp.exe 10.19.141.20 dc2vm.fabrikcam.com LDAPSASLBuffer LDAPSASLBuffer:BufferLength: 418, AuthMechanism: GSS-SPNEGO {LDAP:1013, TCP:1012, IPv4:1009}
Here we search fabrikcam.com
SearchRequest: BaseDN: DC=Fabrikam,DC=com, SearchScope: WholeSubtree, SearchAlias: neverDerefAliases
We filter on SID
Filter: (&(objectSID=))
And we request these attributes
Attributes: ( objectSID )( mail )( mobile )( displayName )( title )( department )( proxyAddresses )( cn )( samAccountName )( groupType )( userAccountControl )( distinguishedName )( objectClass )( userPrincipalName )( msexchmasteraccountsid )
Here is the search result
39519 12:47:20 PM 11/17/2016 25.4835293 w3wp.exe dc2vm.fabrikam.com 10.19.141.20 LDAPSASLBuffer LDAPSASLBuffer:BufferLength: 789, AuthMechanism: GSS-SPNEGO {LDAP:1013, TCP:1012, IPv4:1009}
SearchResultEntry: CN=PeoplePickerTest,OU=Contract,DC=Fabrikam,DC=Com
We return these attribute values
Frame: Number = 39519, Captured Frame Length = 847, MediaType = ETHERNET
– Attributes: 12 Partial Attributes
+ SequenceHeader:
+ PartialAttribute: objectClass=( top )( person )( organizationalPerson )( user )
+ PartialAttribute: cn=( PeoplePickerTest )
+ PartialAttribute: title=( Business Application Consultant )
+ PartialAttribute: distinguishedName=( CN=PeoplePickerTest,OU=Contract,DC=Fabrikam,DC=Com )
+ PartialAttribute: displayName=( PeoplePickerTest)
+ PartialAttribute: department=( AD_Medmanagement )
+ PartialAttribute: proxyAddresses=( smtp:PeoplePickerTest@fabrikam.com )( SMTP:PeoplePickerTest@fabrikam.com)
+ PartialAttribute: userAccountControl=( 512 )
+ PartialAttribute: objectSid=( )
+ PartialAttribute: sAMAccountName=( PeoplePickerTest )
+ PartialAttribute: userPrincipalName=( PeoplePickerTest@fabrikam.com )
+ PartialAttribute: mail=( PeoplePickerTest@fabrikam.com )
We then see these entries in the ULS for this search
SearchFromGC name = Fabrikam.com. start
GetAccountNameFromSid “0x0105000000000005150000001741550EDE09584B0363450079C20000” start
GetAccountNameFromSid “0x0105000000000005150000001741550EDE09584B0363450079C20000” returned. returnValue=True
SearchFromGC name = Fabrikam.com. returned. Result count = 1
We then see this error
User SPPrincipalInfo doesn’t seem to have a user principal name value. User: ‘fabrikam\PeoplePickerTest’, ID: ‘-1’
Conclusion:
- We bind to the first result received with the SID
- The attributes passed in the first search result was for a contact which does not have an object SID but does have a msEXCHMasterSID.
- This attribute is included in the first search results, so we bind to this master SID and translate to a user account, which is the account we are expecting.
- However, this search result does not contain a UserPrincipalName attribute, so we do not display the user.
Workarounds (in no particular order):
-
If you change the search order of the domains in the people picker searchadforests property to search the trusted domain Fabrikam.com first, then search Contoso.com. The people picker will bind to the first result it receives which will be the correct user object.
Origninal configuration
stsadm -o setproperty -url https://www.contoso.com -pn peoplepicker-searchadforests -pv “forest:contoso.com;forest:fabrikam.com”
Reversed search order
stsadm -o setproperty -url https://www.contoso.com -pn peoplepicker-searchadforests -pv “forest:fabrikam.com;forest:contoso.com“
-
If you search for the user using domain\username that will return the correct results regardless of the people picker search order
- This does not query every domain in the list
-
This is a direct LDAP query to the domain specified
-
If you copy the account name and paste it into the people picker search this will also return the correct user
- This mimics the check name function for the account name
-
When you type in a user account we will actually start performing LDAP queries to return best bet results
- For example, if I want to search for the account UserA as I type that into the people picker search the results may start displaying all the accounts with User in them and ultimately filter down to the exact result when you finish typing in the user account name
-
If you type in the users account name and then do a CTRL + K (check name function) this is the same as pasting in the users account name
- This also uses the check name function
More of a permanent solution:
Ultimately the issue is people picker is resolving contact objects. Since contacts can’t be used to log in with we can filter these out from the people picker results with a custom filter.
The filter is a LDAP search filter: (|(objectCategory=group)(&(objectCategory=person)(!objectClass=contact))).
We can test this filter using the custom search feature in Active Directory Users and Computers.



In SharePoint, we’ll fix this issue by using the below PowerShell:
==============================PowerShell Script==============================
Add-PSSnapin Microsoft.SharePoint.PowerShell -ea silentlycontinue
$wa = Get-SPWebApplication <Put Web App Here>
$wa.PeoplePickerSettings.SearchActiveDirectoryDomains | out-file c:\temp\pp_settings_before.txt
$wa.PeoplePickerSettings.SearchActiveDirectoryDomains.Clear()
$wa.update()
$wa.PeoplePickerSettings.SearchActiveDirectoryDomains
$wa = Get-SPWebApplication <Put Web App Here>
$adsearchobj1 = New-Object Microsoft.SharePoint.Administration.SPPeoplePickerSearchActiveDirectoryDomain
$adsearchobj1.DomainName = “Contoso.com”
$adsearchobj1.ShortDomainName = “Contoso”
$adsearchobj1.IsForest = $false
$newdomain.CustomFilter = “(|(objectCategory=group)(&(objectCategory=person)(!objectClass=contact)))”
$wa.PeoplePickerSettings.SearchActiveDirectoryDomains.Add($adsearchobj1)
$wa = Get-SPWebApplication <Put Web App Here>
$adsearchobj2 = New-Object Microsoft.SharePoint.Administration.SPPeoplePickerSearchActiveDirectoryDomain
$adsearchobj2.DomainName = “Fabrikam.com”
$adsearchobj2.ShortDomainName = “fabrikam”
$adsearchobj2.IsForest = $true
$newdomain2.CustomFilter = “(|(objectCategory=group)(&(objectCategory=person)(!objectClass=contact)))”
$wa.PeoplePickerSettings.SearchActiveDirectoryDomains.Add($adsearchobj2)
$wa.Update()
==============================PowerShell Script==============================
Additional information about People Picker and the MSExchMasterAccountSid property
https://blogs.iis.net/deanc/troubleshooting-people-picker-with-netmon
To see what is currently set in your environment use the following PowerShell
$webService = new-object Microsoft.SharePoint.Administration.SPWebService
$webService.PeoplePickerSearchReplicatedMasterSIDPropertyName
Now this is ignored if you copy paste in a value that exists in the local domain. This will return the local domains user object instead of the referenced object that is in the MSExchMasterAccountSid property