Background:
I had a case with a customer where they had a user that they could add to SharePoint security groups just fine and that user had no access issues. However they could not perform a search for the user in user profiles. In SQL in the UserProfile_full table and the Userinfo table for the content database showed the user being present and active.
Cause:
UserProfileManager.Search() method is using stored procedure "dbo.proc_Profile_SearchUser" in the Profile Database. This stored Procedure does a lot actually, but I will only focus on the searching aspect
- Each search word becomes a term
- each term is counted and ranked in order to produce query results
- If the search term is not NULL then we insert the userID into the our Temporary Results database
- We then select the userID from the Profile_SearchUserTable where the property value is like the search term
- if no match exists we return Null for all properties and thus you get no results returned
Resolution:
The easiest, recommended, and most efficient resolution to this issue is to simply modify a property for the user(s) in question. This will update the Profile_SearchUserTable and Profile_SearchUserTermStats, which will allow the users to be found.
For an automated approach see the Scripts section below.
To update the properties via the UI without being able to search for the user:
Where <CentralAdmin> is the host name for the central admin site, <userGuidFromUserProfileFullTable> is the value in the UserID column in the UserProfile_Full table for the affected user, and where <GUIDofUPA> is the ID for the User Profile Service Application. Example:
|
Scripts:
SQL Script to find all users affected by this issue: Remember it's not supported to modify the databases directly. We can look but don't touch!!!
select RecordID, upf.UserID, NTName, PreferredName, Email from userprofile_full upf (nolock) left join Profile_SearchUserTable sut (nolock) on sut.UserID = upf.RecordID where upf.bDeleted = 0 and sut.UserID is null |
PowerShell Script to change a property for all users then set it back. No net change for the users profile will be done.
<# #This script is provided as-is with no warranties expressed or implied. Use at your own risk. .DESCRIPTION This script will update user profiles temporarily to fix profile selection in Central Admin .EXAMPLE C:\PS>fixCAProfileSearch.ps1 -GetProperties Description ----------- Checks for the available string properties that can be used during the update .EXAMPLE C:\PS>fixCAProfileSearch.ps1 -ProfileProperty "LastName" Description -----------
Update SomeProperty temporarily for all users. #> param( [String]$ProfileProperty = "", [Switch]$GetProperties ) Add-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue Function CheckParams ($str) { If ($str -eq "" -and $GetProperties -eq $false) { write-warning "`nEither ProfileProperty or GetProperties is required`n" get-help .\fixCAProfileSearch.ps1 -examples Break } } #function return the site object function Site ($str) { Return Get-SPSite $str } #progress bar function function progress ($msg, $percent) { write-progress -activity $msg -status "Progress:" -percentcomplete $percent } #function to get all properties eligible for the update function GetProperties ($str) { clear-host "The following is a list of all string value User Profile Properties" "that do not update the colleague tracker when changed.`n" "Please use one of these properties for the ProfileProperty parameter when running the update.`n" #get the site object $spSite = Site $str #get the service context of the site $serviceContext = Get-SPServiceContext $spSite #get the User Profile Configuration Manager $upcm = New-Object Microsoft.Office.Server.UserProfiles.UserProfileConfigManager($serviceContext) #get all properties that are a single line of text that will not update the colleague tracker when changed $props = $upcm.GetProperties() | where{$_.iscolleagueeventlog -eq $false -and $_.type -eq "string (single value)"} foreach ($i in $props) { #if they begin with SPS- we need to remove the SPS- #this is done because the User Profile and User Profile Configuration Managers use SPS- #for some properties while the Microsoft.Office.Server.UserProfiles.PropertyConstants never use SPS- if ($i.name -like "SPS-*") { $i.name.replace("SPS-", "") } else { $i.name } } #end after showing the available properties break } function Update ($str) { #get the site object $spSite = Site $Site #get the service context of the site $serviceContext = Get-SPServiceContext $spSite #get the User Profile Manager $upm = New-Object Microsoft.Office.Server.UserProfiles.UserProfileManager($serviceContext) #confirmation to continue "This will update the " + $ProfileProperty + " for all users(" + $upm.count + " in total), and will then set the value back to the original." $go = read-host "Type any character and press enter to quit. Press only enter to continue" #go/no go if ($go -ne "") { #end since we have a no go situation break } else { #proceed with the profile updates #math to determine the % per profile for the progress bar $i = 100 / $upm.Count #Since the starting recordid can change, we can't rely on a traditional for loop. #We have to enumerate through all the profiles and get the record IDs foreach ($item in ($upm.GetEnumerator())) { #get $i profile $userProfile = $upm.GetProfile($item.recordid) #call the progress bar function progress ("Updating user " + $userProfile.DisplayName) ($c += $i) #debug for error profiles. Remove # from the next line to use. #$userProfile.displayname + "" + $item.recordid #Get orginal value and store it $temp = $userProfile[[Microsoft.Office.Server.UserProfiles.PropertyConstants]::$ProfileProperty].Value
#Update the value to fix the issue $userProfile[[Microsoft.Office.Server.UserProfiles.PropertyConstants]::$ProfileProperty].Value = "-" $userProfile.Commit()
# This sets it back to the original value $userProfile[[Microsoft.Office.Server.UserProfiles.PropertyConstants]::$ProfileProperty].Value = $temp $userProfile.Commit() } } } #check the parameters provided by the user CheckParams $ProfileProperty $GetProperties #Get the Central Admin site automatically $Site = ([Microsoft.SharePoint.Administration.SPAdministrationWebApplication]::local).url #call the appropriate function to either display the available properties or do an update. if ($GetProperties) { GetProperties $Site } else { Update $Site } |
More Information:
You may see these in ULS logs:
ULS logs from my lab looking up a user with no profile:
08/05/2015 08:03:16.82 w3wp.exe (0x1780) 0x1684 SharePoint Server Database tzkv Verbose SqlCommand: 'dbo.proc_Profile_SearchUser' CommandType: StoredProcedure CommandTimeout: 0 Parameter: '@partitionID' Type: UniqueIdentifier Size: 0 Direction: Input Value: '0c37852b-34d0-418e-91c6-2ac25af4be5b' Parameter: '@correlationId' Type: UniqueIdentifier Size: 0 Direction: Input Value: 'da611a9d-e2f1-48f7-8ef2-e954e9cce86e' Parameter: '@Term1' Type: NVarChar Size: 255 Direction: Input Value: '' Parameter: '@ProfileSubtypeID' Type: Int Size: 0 Direction: Input Value: '1' Parameter: '@Deleted' Type: TinyInt Size: 0 Direction: Input Value: 'False' da611a9d-e2f1-48f7-8ef2-e954e9cce86e
ULS logs from my lab looking up a user with a profile:
08/05/2015 08:03:23.40 w3wp.exe (0x1780) 0x1684 SharePoint Server Database tzkv Verbose SqlCommand: 'dbo.proc_Profile_SearchUser' CommandType: StoredProcedure CommandTimeout: 0 Parameter: '@partitionID' Type: UniqueIdentifier Size: 0 Direction: Input Value: '0c37852b-34d0-418e-91c6-2ac25af4be5b' Parameter: '@correlationId' Type: UniqueIdentifier Size: 0 Direction: Input Value: 'd6355692-5e4e-4339-b378-ca74637ba92f' Parameter: '@Term1' Type: NVarChar Size: 255 Direction: Input Value: 'brentp' Parameter: '@ProfileSubtypeID' Type: Int Size: 0 Direction: Input Value: '1' Parameter: '@Deleted' Type: TinyInt Size: 0 Direction: Input Value: 'False' d6355692-5e4e-4339-b378-ca74637ba92f
Reference: