Updated 9/22/2015
After migrating from a SharePoint 2010 site using Classic Mode Authentication(default) to SharePoint 2013 site in claims mode (default again) using database attach, you will run into a situation where document authors are not converted to the new claims identities by the migration process. This is not limited to Excel documents, but is more relevant to us as PowerPivot uses these IDs while performing specific actions.
Symptoms and Overview:
In the scope of PowerPivot, this is an issue as our timer jobs expect that the user names will be in the correct format when we need to utilize them. If they are not, you can run into a very specific issue where the PowerPivot Management Dashboard Processing Timer Job fails with a "User cannot be found." error.
“Microsoft.SharePoint.SPException: User cannot be found” at Microsoft.AnalysisServices.SPAddin.UsageProcessingTimerJob.PrepareUsageData
Some or all of your PowerPivot files likely have their author property set in the old Windows style name like “MYDOMAIN\someuser”. If the author property was set to a claims format it would look like this: “i:0#.w|MYDOMAIN\someuser” and your timer job would run without issues.
Workaround:
The "easy" workaround for this issue is to delete and re-upload the PowerPivot workbooks affected. The author field cannot be fully examined from the SharePoint UI, so it can be difficult to track down all of these workbooks. Also, this workaround does not always modify the document properly.
Attached to this post (very bottom) is sample code that shows one way to list all of the workbooks for a PowerPivot service application that have an author property in the Windows style format (these files will throw and error when the "PowerPivot Management Dashboard Processing Timer Job" runs).
We have also run into scenarios where the timer job will fail, but our detection tool will not pick up the improperly formatted user name in the database. The workaround of re-uploading or modifying via script is the same regardless.
In the second scenario, in the ULS logs you will see the timer job iterate through all of your PowerPivot workbooks and fail on a specific book. This stops the timer job in its tracks and it will not proceed:
OWSTIMER.EXE (ServerName) 0x879C SharePoint Foundation General g3ql Verbose GetUriScheme(/site/subsite/anothersubsite/PowerPivotGallery/workbookname.xlsx)
OWSTIMER.EXE (ServerName) 0x879C SharePoint Foundation Security ahluw Verbose Entering: GetByLoginNoThrow(domain\username)
OWSTIMER.EXE (ServerName) 0x879C SharePoint Foundation Security ahluz Verbose Exiting: GetByLoginNoThrow(domain\username)
OWSTIMER.EXE (ServerName) 0x879C PowerPivot Service Usage 99 High EXCEPTION: Microsoft.SharePoint.SPException: User cannot be found. at Microsoft.SharePoint.SPUserCollection.get_Item(String loginName) at Microsoft.AnalysisServices.SPAddin.UsageProcessingTimerJob.UpdateDocumentIfNeeded(SPFile file, SqlDataReader reader, SqlCommand updateCommand) at Microsoft.AnalysisServices.SPAddin.UsageProcessingTimerJob.UpdateDocuments(GeminiServiceApplication application, SqlConnection conn1, SqlConnection conn2) at Microsoft.AnalysisServices.SPAddin.UsageProcessingTimerJob.PrepareUsageData(GeminiServiceApplication application) at Microsoft.AnalysisServices.SPAddin.UsageProcessingTimerJob.Execute(Guid targetInstanceId)
In a working scenario, you would see the following for each workbook. Take note of the claims identifier prior to the domain:
OWSTIMER.EXE (ServerName) 0x879C SharePoint Foundation General g3ql Verbose GetUriScheme(/site/AnotherWorkbook.xlsx)
OWSTIMER.EXE (ServerName) 0x879C SharePoint Foundation Security ahluw Verbose Entering: GetByLoginNoThrow(i:0#.w|domain\username)
OWSTIMER.EXE (ServerName) 0x879C SharePoint Foundation Security ahluz Verbose Exiting: GetByLoginNoThrow(i:0#.w|domain\username)
After this entry, you will see normal traffic and the process moving on to the next document or ending the sequence if there are no other documents to parse.
** THIS SAMPLE CODE AND POWERSHELL SCRIPT ARE PROVIED AS IS, WITH NO WARRANTEES OR SUPPORT OF ANY KIND, USE AT YOUR OWN RISK **
PPTimerUserError.exe Usage:
Extract the zip and navigate to the following path:
DRIVE:\filelocation\PPTimerUserError\PPTimerUserError\bin\Debug
Rename PPTimerUserError.bak to PPTimerUserError.exe so you can run the program (.bak so zip scanners don't block the file)
The .exe must be run from one of the farm SharePoint 2013 servers as an administrator account that has access to your SharePoint Service Application databases. The output is tab delimited so you can open with Excel to sort and filter as needed.
Usage: PPTimerUserError <DB Server> <PowerPiviot DB Name>
Example: PPTimerUserError warrenr-ws1 DefaultPowerPivotServiceApplicationDB-57bc03ae-bd41-4102-ab6f-f03201e3b583
Output will be placed here: C:\temp\PPTimerAuthorReport.txt and PPTimerAuthorReportDetails.txt
You will find the information you need in the detailed report (PPTimerAuthorReportDetails.txt).
Find all of the documents that error with "User cannot be found".
You then have 2 options for fixing this. You can either download the document, delete it from the library and re-upload it, or we have built the following script to auto modify the document with an ID of your choosing:
**USE AT YOUR OWN RISK **
$web = get-spweb http://SITECOLLECTIONURLHERE/sites/MORE
$list = $web.lists['LIBRARY NAME']
$user = get-spuser -Web $web -limit all | ? {$_.userlogin -match "username"}
$sUser = $user.id.ToString() + ';#' + $user.DisplayName
$rItem = $list.items | ?{$_.name -eq "workbookname.xlsx"}
write-host "Current Author is:" $ritem.properties["vti_author"]
$ritem["Author"] = $sUser
$ritem.properties["vti_author"] = $user.loginname
$ritem["Editor"] = $sUser
$ritem.UpdateOverwriteVersion()
write-host "Updated Author is:" $ritem.properties["vti_author"]
You will need to modify the highlighted fields to fit your situation. Also note that the "username" field is literally that, just a user name. Do not input a user in "domain\username" format. You can run this script line by line as you test and validate its functionality. Lastly, when you run this, you may also see another PowerShell window open with some messages about snapshots. Just let it run. It is the GallerySnapshot process running manually due to the document update.
A note about the above script. We decided not to expand on this script as each person's usage may be different. Feel free to use this skeleton script in any way fits your situation (put it in a loop for a library or document type etc….) or even modify it to detect the current author and change it to the claims ID of the same user.
Please make sure you test this script in some kind of test farm thoroughly!!