Good day everyone,
I need to copy documents and metadata from a library in site collection A (legacy) to another one in site collection B (brand new) via PowerShell. Lucky me, there's is only the native metadata but the SPFieldUser ones (Author, Editor) give me a hard time.
Of course, there are many values related to people who since left the company and have their account disabled/deleted. And as the source and target site collections are different, they don't rely on the same User Information List, making things really tricky to migrate them.
What I've tried so far :
I have no issue to copy files and folders themselves. For the sake of readability, I'll just display the method copying metadata from $srcItem (the original SPListItem in sitecoll A) to $tgtItem (its copy in sitecoll B).
1. The "brute-force" way
function Copy-Metadata($srcItem, $tgtItem) {
$tgtItem["Created"] = $srcItem["Created"]
$tgtItem["Author"] = $srcItem["Author"]
$tgtItem["Modified"] = $srcItem["Modified"]
$tgtItem["Editor"] = $srcItem["Editor"]
$tgtItem.Update();
}
Of course that doesn't work properly, because User IDs don't match between both sitecolls.
2. The "classic" way
function Copy-Metadata($srcItem, $tgtItem) {
$tgtItem["Created"] = $srcItem["Created"]
$tgtItem["Author"] = $tgtItem.Web.EnsureUser((new-object Microsoft.SharePoint.SPFieldUserValue($srcItem.Web, $srcItem["Author"])).User.LoginName)
$tgtItem["Modified"] = $srcItem["Modified"]
$tgtItem["Editor"] = $tgtItem.Web.EnsureUser((new-object Microsoft.SharePoint.SPFieldUserValue($srcItem.Web, $srcItem["Editor"])).User.LoginName)
$tgtItem.Update();
}
Works great for active users, but raises the following exception for orphan ones : Exception calling "EnsureUser" with "1" argument(s): "The specified user domain\goneuser could not be found.
3. The "desperate" way
function Copy-Metadata($srcItem, $tgtItem) {
$tgtItem["Created"] = $srcItem["Created"]
$tgtItem["Author"] = Build-SPFieldUserValue $srcItem $tgtItem "Author"
$tgtItem["Modified"] = $srcItem["Modified"]
$tgtItem["Editor"] = Build-SPFieldUserValue $srcItem $tgtItem "Editor"
$tgtItem.Update();
}
function Build-SPFieldUserValue ($srcItem, $tgtItem, $fieldInternalName) {
$srcUser = (new-object Microsoft.SharePoint.SPFieldUserValue($srcItem.Web, $srcItem[$fieldInternalName])).User
try {
$tgtUser = $tgtItem.Web.EnsureUser($srcUser.LoginName)
}
catch {
New-SPUser -UserAlias $srcUser.LoginName -DisplayName $srcUser.Name -Web $tgtItem.Web.Url
$tgtUser = $tgtItem.Web.EnsureUser($srcUser.LoginName)
}
return $tgtUser
}
Basically, I'm trying to explicitly add the orphan user in the target sitecoll cache, but it fails too as the New-SPUser command interrogates Active Directory, hence raising the following exception : New-SPUser : The specified user domain\goneuser could not be found.
[EDIT following Zdeněk Vinduška's answer] 4. Adding user as a SPListItem in SiteUserInfoList
$userItem = $tgtItem.Web.SiteUserInfoList.Items.Add()
$userItem["Name"] = "domain\goneuser"
$userItem.Update()
Throws Exception calling "Update" with "0" argument(s): "Cannot complete this action. Please try again."
I'm now out of ideas. Does anyone know how to achieve this ? Thank you very much.