Entra / Microsoft 365 · Compliance & audit
Find targeted collection folder identifiers
Generate folder identifiers for use in a compliance content search targeting specific Recoverable Items folders in primary and archive mailboxes.
Connect & set up
Run these once per session. All scopes are read-only unless the script makes changes.
Connect-ExchangeOnlineConnect-IPPSSession
Run it
The main script. Copy it, or download the .ps1 and run it from your console.
$Modules = Get-Module | Select-Object -ExpandProperty NameIf ("ExchangeOnlineManagement" -notin $Modules) { Write-Host "Please connect to the Exchange Online management module and restart." ; break }$Target = Read-Host "Enter the user principal name of the mailbox to check"[array]$Mbx = Get-ExoMailbox -Identity $Target -ErrorAction SilentlyContinueIf ($Mbx.count -ne 1) { Write-Host ("Sorry - can't find a mailbox for {0}" -f $Target) ; break }$UserAccount = $Mbx.UserPrincipalName$FolderQueries = @()$ArchiveQueries = @()$Encoding = [System.Text.Encoding]::GetEncoding("us-ascii")$Nibbler = $Encoding.GetBytes("0123456789ABCDEF")Write-Host ("Checking primary mailbox for {0}" -f $UserAccount)[array]$Folders = Get-ExoMailboxFolderStatistics -Identity $UserAccount -FolderScope RecoverableItemsIf (!($Folders)) { Write-Host ("Unable to retrieve mailbox folder statistics for {0} - exiting" -f $UserAccount) ; break }ForEach ($Folder in $Folders) {$FolderPath = $Folder.FolderPath;If (($FolderPath -eq "/Versions") -or ($FolderPath -eq "/Deletions") -or ($FolderPath -eq "/Purges") -or ($FolderPath -eq "/DiscoveryHolds") -or ($FolderPath -eq "/SubstrateHolds")) {$FolderId = $Folder.FolderId$FolderIdBytes = [Convert]::FromBase64String($folderId)$IndexIdBytes = New-Object byte[] 48$IndexIdIdx=0$FolderIdBytes | Select-object -skip 23 -First 24 | %{$indexIdBytes[$indexIdIdx++]=$nibbler[$_ -shr 4];$indexIdBytes[$indexIdIdx++]=$nibbler[$_ -band 0xF]}$FolderQuery = "folderid:$($encoding.GetString($indexIdBytes))";$FolderDetails = New-Object PSObjectAdd-Member -InputObject $FolderDetails -MemberType NoteProperty -Name FolderPath -Value $FolderPathAdd-Member -InputObject $FolderDetails -MemberType NoteProperty -Name FolderQuery -Value $folderQuery$FolderQueries += $FolderDetails} # End if} # End Foreach# Content search will always process an archive mailbox if one is available, so we need to fetch the identifiers for the target folders in the archive too$ArchiveDatabase = Get-ExoMailbox -Identity $UserAccount -PropertySet Archive | Select-Object -ExpandProperty ArchiveDatabaseIf ($ArchiveDatabase) { # We need to process archive folders tooWrite-Host ("Checking archive mailbox for {0}" -f $UserAccount)[array]$Folders = Get-ExoMailboxFolderStatistics -Identity $UserAccount -FolderScope RecoverableItems -ArchiveIf (!($Folders)) { Write-Host ("Unable to retrieve archive mailbox folder statistics for {0}" -f $UserAccount) }ForEach ($Folder in $Folders) {$FolderPath = $Folder.FolderPath;If (($FolderPath -eq "/Versions") -or ($FolderPath -eq "/Deletions") -or ($FolderPath -eq "/Purges") -or ($FolderPath -eq "/DiscoveryHolds") -or ($FolderPath -eq "/SubstrateHolds")) {$FolderId = $Folder.FolderId$FolderIdBytes = [Convert]::FromBase64String($folderId)$IndexIdBytes = New-Object byte[] 48$IndexIdIdx=0$FolderIdBytes | Select-object -skip 23 -First 24 | %{$indexIdBytes[$indexIdIdx++]=$nibbler[$_ -shr 4];$indexIdBytes[$indexIdIdx++]=$nibbler[$_ -band 0xF]}$FolderQuery = "folderid:$($encoding.GetString($indexIdBytes))";$FolderDetails = New-Object PSObjectAdd-Member -InputObject $FolderDetails -MemberType NoteProperty -Name FolderPath -Value $FolderPathAdd-Member -InputObject $FolderDetails -MemberType NoteProperty -Name FolderQuery -Value $folderQuery$ArchiveQueries += $FolderDetails} # End if Folders} # End Foreach folders} # End if ArchiveWrite-Host ""Write-Host "Folder identifiers to use for content search"Write-Host "--------------------------------------------"Write-Host ""Write-Host "Primary mailbox"$FolderQueries | Format-Table$KQLQuery = $FolderQueries.FolderQuery -join " OR "Write-Host ""If ($ArchiveDatabase) {Write-Host "Archive mailbox"$ArchiveQueries | Format-Table$KQLQuery2 = $ArchiveQueries.FolderQuery -join " OR "$KQLQuery = $KQLQuery + " OR " + $KQLQuery2}Write-Host ("And here's the KQL query to insert into the content search: {0}" -f $KQLQuery)Connect-IPPSSessionWrite-Host "Creating the compliance search..."$SearchName = "Focused Mailbox Search"Remove-ComplianceSearch -Identity $SearchName -Confirm:$False -ErrorAction SilentlyContinueNew-ComplianceSearch -Name $SearchName -ContentMatchQuery $KQLQuery -Description ("Focused folder search for mailbox {0}" -f $UserAccount) -ExchangeLocation $UserAccountWrite-Host "Starting search"Start-ComplianceSearch -Identity $SearchNameDo {Write-Host ("Waiting for search {0} to comlete..." -f $SearchName)Start-Sleep -Seconds 5$ComplianceSearch = Get-ComplianceSearch -Identity $SearchName} While ($ComplianceSearch.Status -ne 'Completed')Write-Host ("Search found {0} items in mailbox {1}" -f $ComplianceSearch.Items, $UserAccount)Write-Host ""
Attribution
Author
Office365itpros