Recently we had an issue with the synchronization of user profile data to the user list of a SharePoint site collection. Although all of the user e-mail addresses were synchronized from the Active Directory to the user profile service (UPS), in the case of a few, seemingly random users this piece of information remained empty in the SharePoint user list.
As written in the referenced post above, there are two timer jobs that should keep user information in sync between UPS and the user list.
- User Profile to SharePoint Full Synchronization
- User Profile to SharePoint Quick Synchronization
We did not find any problems with any of these timer jobs. After researching the web for a solution I found these post that suggests using the sync operation of the stsadm command to resolve UPS to SharePoint synchronization issues.
As I wrote here last year, one of the most important drawbacks of stsadm (beside of not being trendy any more), that you should have local administrator rights on the server where you run this commands, otherwise you receive an “Access denied.” error message. The reason, that for most of the stsadm operations, the following security check is performed in the entry method (public static int Main) of the Microsoft.SharePoint.StsAdmin.SPStsAdmin class:
if (!SPAdministrationServiceUtilities.IsCurrentUserMachineAdmin())
{
Console.WriteLine(SPResource.GetString("AccessDenied", new object[0]));
Console.WriteLine();
return -2147024891;
}
In our case we have no local admin permissions on the server, so I decided to use Reflector to see, what the sync operation of ststadm exactly does, and rewrite its functionality using PowerShell.
I found that processing both of the listolddatabases and deleteolddatabases parameters we need are implemented in the ProcessStaleDataParams method of the Microsoft.Office.Server.CommandLine.SyncCommands class (Microsoft.Office.Server.UserProfiles assembly)
If the value of the listolddatabases parameter is not null (it is an integer, let’s say X), the command iterates through all of the content databases of all of the web applications in the farm, and displays the values of the Id and the LastProfileSyncTime properties of the content database if the difference (in days) between now (UTC value) and the value of the LastProfileSyncTime property is greater than or equals to X.
Without any kind of filtering, we can rewrite this in PowerShell as:
Get-SPContentDatabase | Select-Object -Property ID, LastProfileSyncTime
If you wish, you can extend it easily to filter, for example on the value of the LastProfileSyncTime property.
If the value of the deleteolddatabases parameter is not null (it is an integer, let’s say X), the command (similarly to the listolddatabases parameter described above) iterates through all of the content databases of all of the web applications in the farm, and if the difference (in days) between now (UTC value) and the value of the LastProfileSyncTime property is greater than or equals to X, it invokes the static ClearSyncDataForContentDatabase method of the WSSProfileSynch class (Microsoft.Office.Server.UserProfiles namespace and Microsoft.Office.Server.UserProfiles assembly), passing the content database (whose LastProfileSyncTime property is greater than or equals to X) and the UPS proxy assigned to the web application that belongs to the content database.
The only problem with this ClearSyncDataForContentDatabase method (having two parameters) is that it is declared an internal. Of course, we could invoke it using Reflection, however it is not necessary, since there is an other, public overload of the same ClearSyncDataForContentDatabase method, having a single, content database parameter, that determines the UPS proxy itself instead of expecting it as a parameter. So we are going with this public method, and assume, you already know, on which database you would like to reset the LastProfileSyncTime property.
$cdb = Get-SPContentDatabase e0ab0f2f-8f31-4e0f-82f4-7f7c48912d07 # ID the content DB
# or if you already know the name of the content DB you could use the name as well
# $cdb = Get-SPContentDatabase NameOfYourContentDB
[Microsoft.Office.Server.UserProfiles.WSSProfileSynch]::ClearSyncDataForContentDatabase($cdb)
Although using this PowerShell-based approach we were able to reset the LastProfileSyncTime property, it has not solved our original problem, but it is already an other story. Despite of it, I hope the PowerShell samples above may help others to override the security limitation of the stsadm command in the case of the sync operation.