This week my customer and a peer both asked for a sample PowerShell script to automate the creation of an Office 365 Security and Compliance Center eDisovery case, hold, and content search. This post will share out that script and a few things to be aware of (ex. deprecating basic authentication) that are important.
Background
The below script accomplishes the following tasks:
- Create a Security and Compliance Center eDiscovery case
- Place an in-place hold on multiple users’ Exchange Online mailboxes
- Create a content search within eDiscovery case for any folders named “Legal Hold” and the child folders under them
Important Note
As of the publish date (Mar 4th, 2020) the Security and Compliance Center remote PowerShell module relies on basic authentication. The Exchange team has publicly shared that basic authentication for Exchange Online will be deprecated by Oct 2020. As such that means the below script may not be usable in its current form in ~6 months. When a replacement or update is available I will attempt to update this sample to reflect that.
Exchange Online deprecating Basic Authentication
https://support.microsoft.com/en-us/help/4521831/exchange-online-deprecating-basic-auth
In terms of the Exchange Online remote PowerShell module there is a v2 module being developed (active development, not ready for production) which you can find on the PowerShell Gallery. This new module support OAuth authentication which resolves the issue of deprecated basic authentication.
ExchangeOnlineManagement module on PSGallery
https://www.powershellgallery.com/packages/ExchangeOnlineManagement
Solution
Before running this script, ensure that the account you log in with has the appropriate permissions to both Exchange Online as well as Security and Compliance Center. My sample uses a single admin account but you may adapt the script to use separate accounts if needed. See the following articles for more details.
Connect to Exchange Online PowerShell
https://docs.microsoft.com/en-us/powershell/exchange/exchange-online/connect-to-exchange-online-powershell/connect-to-exchange-online-powershell?view=exchange-ps
Connect to Office 365 Security & Compliance Center PowerShell
https://docs.microsoft.com/en-us/powershell/exchange/office-365-scc/connect-to-scc-powershell/connect-to-scc-powershell?view=exchange-ps
Note: If you do not see the below Gist please refer to code at this location: EXO_New-SCCeDiscoveryCaseAndHold.ps1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Set-StrictMode –Version "Latest" | |
# eDiscovery case creation | |
$caseName = 'Smith v. Johnson'; | |
$UPN = 'user1@contoso.onmicrosoft.com', 'user2@contoso.onmicrosoft.com' | |
$description = "$caseName" | |
$policyName = "$caseName – Hold Policy" | |
$ruleName = "$caseName – Hold Rule" | |
$searchName = "$caseName – Search Name" | |
$rootFolderNameQuery = "Legal Hold" | |
function GetFolderQueries { | |
param ( | |
[string] | |
$rootFolderNameQuery, | |
[string[]] | |
$UPN | |
) | |
$folderQueries = @() | |
foreach($user in $UPN) | |
{ | |
$rootFolderStats = Get-MailboxFolderStatistics –Identity $user | Where-Object name -eq $rootFolderNameQuery | |
$childFolderStats = Get-MailboxFolderStatistics –Identity $user | Where-Object FolderPath -like "$($rootFolderStats.FolderPath)*" | |
# sample script to convert folderId: https://docs.microsoft.com/en-us/microsoft-365/compliance/use-content-search-for-targeted-collections?view=o365-worldwide#step-1-run-the-script-to-get-a-list-of-folders-for-a-mailbox-or-site | |
foreach ($folderStatistic in $childFolderStats) | |
{ | |
$folderId = $folderStatistic.FolderId; | |
$folderPath = $folderStatistic.FolderPath; | |
$encoding= [System.Text.Encoding]::GetEncoding("us-ascii") | |
$nibbler= $encoding.GetBytes("0123456789ABCDEF"); | |
$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))"; | |
$folderStat = New-Object PSObject | |
Add-Member –InputObject $folderStat –MemberType NoteProperty –Name UPN –Value $user | |
Add-Member –InputObject $folderStat –MemberType NoteProperty –Name FolderPath –Value $folderPath | |
Add-Member –InputObject $folderStat –MemberType NoteProperty –Name FolderQuery –Value $folderQuery | |
$folderQueries += $folderStat | |
} | |
} | |
return $folderQueries | |
} | |
# Connection to EXO and SCC PowerShell Modules | |
$UserCredential = Get-Credential | |
$Session = New-PSSession –ConfigurationName Microsoft.Exchange –ConnectionUri https://outlook.office365.com/powershell–liveid/ –Credential $UserCredential –Authentication Basic –AllowRedirection | |
Import-PSSession $Session –AllowClobber | |
$SccSession = New-PSSession –ConfigurationName Microsoft.Exchange –ConnectionUri https://ps.compliance.protection.outlook.com/powershell–liveid –Credential $UserCredential –Authentication Basic –AllowRedirection | |
Import-PSSession $SccSession –AllowClobber –DisableNameChecking | |
# Create eDiscovery case, hold, and compliance search | |
New-ComplianceCase –Name $caseName –Description $description | |
New-CaseHoldPolicy –Name $policyName –Case $caseName –ExchangeLocation $UPN –Enabled $true | |
New-CaseHoldRule –Name $ruleName –Policy $policyName –Disabled $false | |
$folderQueries = GetFolderQueries –rootFolderNameQuery $rootFolderNameQuery –UPN $UPN | |
New-ComplianceSearch –Name $searchName –Case $caseName –HoldNames "All" –ContentMatchQuery $folderQueries.FolderQuery |
Conclusion
In this post I shared a sample script for automating the creation of an Office 365 Security and Compliance Center eDiscovery case, hold, and folder scoped content search. The folder scoping was an interesting detour as I had to track down the way to gather folder IDs from a product group engineer sample (linked in the above sample). I hope you find this useful and good luck scripting.
-Frog Out