A - OBJECTIVE:
During process automation in SharePoint Online, Powershell script can be considered to programmatically declare (or undeclare) items and files as a record .
The manual steps that can be automated for multiple items are as follows:
B - ARCHITECTURE:
1. The site collection feature "In Place Records Management" must be activated; then, at each individual list / library settings (Record declaration settings), we need to configure the manual records management settings.
2. Field Value _vti_ItemHoldRecordStatus (int) can be used to identify the record status of the item (reference 3):
- _vti_ItemHoldRecordStatus = 0 : a normal document / item
- _vti_ItemHoldRecordStatus = 273 : a record
- _vti_ItemHoldRecordStatus = 4353: an item on hold
3. Field Value _vti_ItemDeclaredRecord (datetime) is set to the current date time soon after the associated item is set as a record; and this value is cleared once the item is undeclared as a record.
4. The functions DeclareItemAsRecord / UndeclareItemAsRecord (using ClientRuntimeContext) in the assembly Microsoft.SharePoint.Client.RecordsRepository.Records of the library Microsoft.Office.Client.Policy.dll is used to declare / undeclare a record:
#declare the record
if($DocItem.FieldValues["_vti_ItemHoldRecordStatus"] -eq 0)
{
[Microsoft.SharePoint.Client.RecordsRepository.Records]::DeclareItemAsRecord($clientRuntimeContext,$DocItem)
}
#undeclare the record
if($DocItem.FieldValues["_vti_ItemHoldRecordStatus"] -eq 273)
{
[Microsoft.SharePoint.Client.RecordsRepository.Records]::UndeclareItemAsRecord($clientRuntimeContext,$DocItem)
}
5. The declared item has a LOCK icon at the file type and the datetime value in the column Declared Record:
C - SOLUTION:
1. Powershell tool must be Windows Powershell or Windows Powershell ISE. The script does not work in SharePoint Online Management Shell at the moment (Microsoft.Online.SharePoint.PowerShell version 16.0.7414.0) , and it will throw an error:
Cannot convert argument "context", with value: "Microsoft.SharePoint.Client.ClientContext", for "IsRecord" to type "Microsoft.SharePoint.Client.ClientRuntimeContext": "Cannot convert the "Microsoft.SharePoint.Client.ClientContext" value of type "Microsoft.SharePoint.Client.ClientContext" to type "Microsoft.SharePoint.Client.ClientRuntimeContext"." ---> System.Management.Automation.PSInvalidCastException: Cannot convert the "Microsoft.SharePoint.Client.ClientContext" value of type "Microsoft.SharePoint.Client.ClientContext" to type "Microsoft.SharePoint.Client.ClientRuntimeContext".
2. SharePoint Online Client Components SDK (link here): the latest version should be installed and referenced to in the Powershell script:
Add-Type -Path "C:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll"
Add-Type -Path "C:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Runtime.dll"
Add-Type -Path "C:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\ISAPI\Microsoft.Office.Client.Policy.dll"
3. DLL version must be 16+ (i.e. the version 15.x is not recommended)
4. The function IsRecord does not return a proper value as expected in this DLL version; hence, we should use the field value $DocItem.FieldValues["_vti_ItemHoldRecordStatus"]
[Boolean] $isRecord = [Microsoft.SharePoint.Client.RecordsRepository.Records]::IsRecord($clientRuntimeContext,$DocItem)
D - SOURCE CODE:
#region Load SharePoint Client Assemblies Add-Type -Path "C:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll" Add-Type -Path "C:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Runtime.dll" Add-Type -Path "C:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\ISAPI\Microsoft.Office.Client.Policy.dll" #endregion # Document Library $libRequestDocs = "Request Documents" # Connection Credetials $UserName = "admin@nhn.onmicrosoft.com" $PASSWORD = "YourPassword" $SecurePassword = ConvertTo-SecureString -String $PASSWORD -AsPlainText -Force # Target environment $SiteUrl = "https://nhn.sharepoint.com/sites/records" # Global variables $archivedIDs = [System.Collections.ArrayList]@() ### Connection to SP Online Archive subsites to get the reference Try { #connect/authenticate to SharePoint Online and get ClientContext object.. $clientContextArchive = New-Object Microsoft.SharePoint.Client.ClientContext($SiteUrl) [Microsoft.SharePoint.Client.ClientRuntimeContext]$clientRuntimeContext=[Microsoft.SharePoint.Client.ClientRuntimeContext]$clientContextArchive.Site.Context $credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($UserName, $SecurePassword) $clientContextArchive.Credentials = $credentials $clientContextArchive.Load($clientContextArchive.Web) $clientContextArchive.ExecuteQuery() Write-Host "Connected to SharePoint Online site: " $SiteUrl"/Archive001/" -ForegroundColor Green } Catch { $SPOConnectionException = $_.Exception.Message Write-Host "" Write-Host "Connection Error:" $SPOConnectionException -ForegroundColor Red Write-Host "" Break } Try { #Get the Document Library $libDocs =$clientContextArchive.Web.Lists.GetByTitle($libRequestDocs) $clientContextArchive.Load($libDocs) $clientContextArchive.ExecuteQuery() #Define CAML Query to Get All Files $queryCommand = "" $DataCollection = @() $Count = 1; $position = $null $queryCAML = New-Object Microsoft.SharePoint.Client.CamlQuery $queryCAML.ViewXml = $queryCommand #handle large library with more than 5000 items Do { Try{ #powershell sharepoint online list all documents $currentDocCollection = $libDocs.GetItems($queryCAML) $clientContextArchive.Load($currentDocCollection) $clientContextArchive.ExecuteQuery() $queryCAML.ListItemCollectionPosition = $currentDocCollection.ListItemCollectionPosition Write-host -f Green "Collection Index " $currentDocCollection.ListItemCollectionPosition.pagingInfo #Iterate through each document in the library ForEach($DocItem in $currentDocCollection) { $clientContextArchive.Load($DocItem) $clientContextArchive.Load($DocItem.File) $clientContextArchive.ExecuteQuery() #this function does not return expected results [Boolean] $isRecord = [Microsoft.SharePoint.Client.RecordsRepository.Records]::IsRecord($clientRuntimeContext,$DocItem) $clientContextArchive.ExecuteQuery() Write-host -f Green "Doc:" $DocItem.FieldValues Write-host -f Green "[Record Details] _vti_ItemHoldRecordStatus: " $DocItem.FieldValues["_vti_ItemHoldRecordStatus"] Write-host -f Green "[Record Details] _vti_ItemDeclaredRecord: " $DocItem.FieldValues["_vti_ItemDeclaredRecord"] #undeclare the record if($DocItem.FieldValues["_vti_ItemHoldRecordStatus"] -eq 273){ [Microsoft.SharePoint.Client.RecordsRepository.Records]::UndeclareItemAsRecord($clientRuntimeContext,$DocItem) try { $clientRuntimeContext.ExecuteQuery() Write-Host "...Item has been undeclared a record" -ForegroundColor Green Write-host -f Blue "Doc:" $DocItem.FieldValues } catch { Write-Host "...Item could not be undeclared a record" -ForegroundColor Red write-host -f Red "Error Level 3:" $_.Exception } } #declare the record if($DocItem.FieldValues["_vti_ItemHoldRecordStatus"] -eq 0){ [Microsoft.SharePoint.Client.RecordsRepository.Records]::DeclareItemAsRecord($clientRuntimeContext,$DocItem) try { $clientRuntimeContext.ExecuteQuery() Write-Host "...Item has been declared a record" -ForegroundColor Green Write-host -f Blue "Doc:" $DocItem.FieldValues } catch { Write-Host "...Item could not be declared a record" -ForegroundColor Red write-host -f Red "Error Level 3:" $_.Exception } } } } Catch{ write-host -f Red "Error Level 2:" $_.Exception } } While($queryCAML.ListItemCollectionPosition -ne $null) } Catch { write-host -f Red "Error Level 1:" $_.Exception.Message } 1000
REFERENCE:
1. https://gallery.technet.microsoft.com/office/SharePoint-Online-f6bae254/view/Discussions#content
2. https://www.sharepointpals.com/post/check-whether-a-document-is-already-a-record-or-not-isrecord-in-sharepoint-office-365-programmatically-using-c-csom/
3. A sample of a file's field values (or properties):
[ID, 1638][ContentTypeId, 0x010100A5FD4705EF695745935DCFF362D96FD9002FFDDFEEE3A428488245399665244602][Created, 31/01/2020 3:55:24 AM][Author, Microsoft.SharePoint.Client.FieldUserValue][Modified, 31/01/2020 3:55:24 AM][Editor, Microsoft.SharePoint.Client.FieldUserValue][_HasCopyDestinations, ][_CopySource, ][_ModerationStatus, 3][_ModerationComments, ][FileRef, /sites/ourbriefings-test/Archive001/RequestDocuments/BAC-100/ArchiveFlagNHN-2020-01-31.txt][FileDirRef, /sites/ourbriefings-test/Archive001/RequestDocuments/BAC-100][Last_x0020_Modified, 2020-01-31T03:55:24Z][Created_x0020_Date, 2020-01-31T03:55:24Z][File_x0020_Size, 11228][FSObjType, 0][SortBehavior, Microsoft.SharePoint.Client.FieldLookupValue][CheckedOutUserId, Microsoft.SharePoint.Client.FieldLookupValue][IsCheckedoutToLocal, 0][CheckoutUser, Microsoft.SharePoint.Client.FieldUserValue][FileLeafRef, ArchiveFlagNHN-2020-01-31.txt][UniqueId, df9fe88c-4a14-47ca-a8ae-20bbcfc2a680][SyncClientId, Microsoft.SharePoint.Client.FieldLookupValue][ProgId, ][ScopeId, {8024A1D9-532B-4461-BF67-F2A53DE8A3B9}][VirusStatus, Microsoft.SharePoint.Client.FieldLookupValue][CheckedOutTitle, Microsoft.SharePoint.Client.FieldLookupValue][_CheckinComment, ][Modified_x0020_By, i:0#.f|membership|anthony.nguyen@an.com][Created_x0020_By, i:0#.f|membership|anthony.nguyen@an.com][File_x0020_Type, txt][HTML_x0020_File_x0020_Type, ][_SourceUrl, ][_SharedFileIndex, ][MetaInfo, SendEmailToAuthors:SW|Send Emailvti_parserversion:SR|16.0.0.19708vti_previewinvalidtime:TX|31 Jan 2020 03:55:22 -0000vti_author:SR|i:0#.f|membership|anthony.nguyen@an.comvti_dbschemaversion:SR|16.0.204.0vti_sprocsschemaversion:SR|16.0.452.0CBSStatus:SW|DraftIconOverlay:SW||txt|lockoverlay.pngecm_ItemDeleteBlockHolders:SW|ecm_InPlaceRecordLockvti_writevalidationtoken:SW|GQIq0B5WkD/F3Wg7yUVPfqvtYLk=DocumentSetDescription:SW|vti_modifiedby:SR|i:0#.f|membership|anthony.nguyen@an.comecm_RecordRestrictions:SW|BlockDelete, BlockEditvti_foldersubfolderitemcount:IR|0ContentTypeId:SW|0x010100A5FD4705EF695745935DCFF362D96FD9002FFDDFEEE3A428488245399665244602ecm_ItemLockHolders:SW|ecm_InPlaceRecordLockvti_folderitemcount:IR|0_vti_ItemDeclaredRecord:SW|2020-01-30T19:56:26Z_vti_ItemHoldRecordStatus:IW|273HideFromDelve:BW|true][_Level, 2][_IsCurrentVersion, True][ItemChildCount, 0][FolderChildCount, 0][Restricted, ][OriginatorId, ][NoExecute, 0][ContentVersion, 1][_ComplianceFlags, ][_ComplianceTag, ][_ComplianceTagWrittenTime, ][_ComplianceTagUserId, ][BSN, Microsoft.SharePoint.Client.FieldLookupValue][_ListSchemaVersion, ][_Dirty, 0][_Parsable, 0][_StubFile, 0][AccessPolicy, 0][_VirusStatus, ][_VirusVendorID, ][_VirusInfo, ][_CommentFlags, ][_CommentCount, ][_LikeCount, ][_RmsTemplateId, ][_IpLabelId, ][_DisplayName, ][_IpLabelAssignmentMethod, ][AppAuthor, ][AppEditor, ][SMTotalSize, Microsoft.SharePoint.Client.FieldLookupValue][SMLastModifiedDate, 2020-01-31T03:56:27Z][SMTotalFileStreamSize, 11228][SMTotalFileCount, Microsoft.SharePoint.Client.FieldLookupValue][owshiddenversion, 2][_UIVersion, 1][_UIVersionString, 0.1][InstanceID, ][Order, 163800][GUID, f5cb7fe8-1bc2-4a25-b164-614d2feb94d4][WorkflowVersion, 1][WorkflowInstanceID, ][ParentVersionString, Microsoft.SharePoint.Client.FieldLookupValue][ParentLeafName, Microsoft.SharePoint.Client.FieldLookupValue][DocConcurrencyNumber, 2][ParentUniqueId, {E14CB12D-6970-45C6-8452-8B2C50A6F25F}][StreamHash, 0x0219022AD01E56903FC5DD683BC9454F7EABED60B9][ComplianceAssetId, ][Title, ][TemplateUrl, ][xd_ProgID, ][xd_Signature, ][_ShortcutUrl, ][_ShortcutSiteId, ][_ShortcutWebId, ][_ShortcutUniqueId, ][CBSStatus, Draft][CBSDocComments, ][CBSDueBy, ][CBSDocType, ][CBSFileName, ][CBSReviewersContributors, ][SendEmailToAuthors, Send Email][RequestID, ][DocumentSetDescription, ][Send_x0020_Email_x0020_To_x0020_Reviewer, ][Update_x0020_Request_x0020_Number, ][UpdateDo, ][UpdateNe, ][MediaServiceMetadata, ][MediaServiceFastMetadata, ][Send_x0020_Email_x0020_To_x0020_Reviewer_x0028_1_x0029_, ][Update_x0020_Request_x0020_number0, ][UpdateDo0, ][UpdateNe0, ][MediaServiceAutoTags, ][MediaServiceOCR, ][Document_x0020_Status, ][UploadedBy, ][CBSDocumentType, ][CBSAuthor, ][CBSComments, ][CBSDocStatus, ][CBSDocDueDate, ][CBSSendToAuthor, ][CBSRepeaterDocID, ][FileUpdate, ][MediaServiceDateTaken, ][SharedWithUsers, ][SharedWithDetails, ][MediaServiceGenerationTime, ][MediaServiceEventHashCode, ][RequestTitle, ][TaskDueDate, ][Status, ][MediaServiceLocation, ][HideFromDelve, True][RequestNumber, ][RequestNumber_x003a_Current_x0020_Status, ][_vti_ItemDeclaredRecord, 30/01/2020 8:56:26 AM][_vti_ItemHoldRecordStatus, 273]