Use a script to add users to a hold in a eDiscovery (Standard) case

Security & Compliance PowerShell provides cmdlets that let you automate time-consuming tasks related to creating and managing eDiscovery cases. Currently, using the Microsoft Purview eDiscovery (Standard) case in the Microsoft Purview compliance portal to place a large number of custodian content locations on hold takes time and preparation. For example, before you create a hold, you have to collect the URL for each OneDrive for Business site that you want to place on hold. Then for each user you want to place on hold, you have to add their mailbox and their OneDrive for Business site to the hold. You can use the script in this article to automate this process.

The script prompts you for the name of your organization's My Site domain (for example, contoso in the URL https://contoso-my.sharepoint.com), the name of an existing eDiscovery case, the name of the new hold that associated with the case, a list of email addresses of the users you want to put on hold, and a search query to use if you want to create a query-based hold. The script then gets the URL for the OneDrive for Business site for each user in the list, creates the new hold, and then adds the mailbox and OneDrive for Business site for each user in the list to the hold. The script also generates log files that contain information about the new hold.

Here are the steps to make this happen:

Step 1: Install the SharePoint Online Management Shell Step 2: Generate a list of users Step 3: Run the script to create a hold and add users

Tip

If you're not an E5 customer, use the 90-day Microsoft Purview solutions trial to explore how additional Purview capabilities can help your organization manage data security and compliance needs. Start now at the Microsoft Purview compliance portal trials hub. Learn details about signing up and trial terms.

Before you add users to a hold

  • You have to be a member of the eDiscovery Manager role group in the compliance portal and a SharePoint Online administrator to run the script in Step 3. For more information, see Assign eDiscovery permissions in the Office 365 Security & Compliance Center.
  • A maximum of 1,000 mailboxes and 100 sites can be added to a hold that's associated with an eDiscovery case in the compliance portal. Assuming that every user that you want to place on hold has a OneDrive for Business site, you can add a maximum of 100 users to a hold using the script in this article.
  • Be sure to save the list of users that you create in Step 2 and the script in Step 3 to the same folder. That will make it easier to run the script.
  • The script adds the list of users to a new hold that is associated with an existing case. Be sure the case that you want to associate the hold with is created before you run the script.
  • The script in this article supports modern authentication when connecting to Security & Compliance PowerShell and SharePoint Online Management Shell. You can use the script as-is if you are a Microsoft 365 or a Microsoft 365 GCC organization. If you are an Office 365 Germany organization, a Microsoft 365 GCC High organization, or a Microsoft 365 DoD organization, you will have to edit the script to successfully run it. Specifically, you have to edit the line Connect-IPPSSession and use the ConnectionUri and AzureADAuthorizationEndpointUri parameters (and the appropriate values for your organization type) to connect to Security & Compliance PowerShell. For more information, see the examples in Connect to Security & Compliance PowerShell.
  • The script automatically disconnects from Security & Compliance PowerShell and SharePoint Online Management Shell.
  • The script includes minimal error handling. Its primary purpose is to quickly and easily place the mailbox and OneDrive for Business site of each user on hold.
  • The sample scripts provided in this topic aren't supported under any Microsoft standard support program or service. The sample scripts are provided AS IS without warranty of any kind. Microsoft further disclaims all implied warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular purpose. The entire risk arising out of the use or performance of the sample scripts and documentation remains with you. In no event shall Microsoft, its authors, or anyone else involved in the creation, production, or delivery of the scripts be liable for any damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of business information, or other pecuniary loss) arising out of the use of or inability to use the sample scripts or documentation, even if Microsoft has been advised of the possibility of such damages.

Step 1: Install the SharePoint Online Management Shell

The first step is to install the SharePoint Online Management Shell if it's not already installed on your local computer. You don't have to use the shell in this procedure, but you have to install it because it contains pre-requisites required by the script that you run in Step 3. These prerequisites allow the script to communicate with SharePoint Online to get the URLs for the OneDrive for Business sites.

Go to Set up the SharePoint Online Management Shell environment and perform Step 1 and Step 2 to install the SharePoint Online Management Shell on your local computer.

Step 2: Generate a list of users

The script in Step 3 will create a hold that's associated with an eDiscovery case, and the add the mailboxes and OneDrive for Business sites of a list of users to the hold. You can just type the email addresses in a text file, or you can run a command in PowerShell to get a list of email addresses and save them to a file (located in same folder that you'll save the script to in Step 3).

Here's an Exchange Online PowerShell command to get a list of email addresses for all users in your organization and save it to a text file named HoldUsers.txt.

Get-Mailbox -ResultSize unlimited -Filter { RecipientTypeDetails -eq 'UserMailbox'} | Select-Object PrimarySmtpAddress > HoldUsers.txt

After you run this command, open the text file and remove the header that contains the property name, PrimarySmtpAddress. Then remove all email addresses except the ones for the users that you want to add to the hold that you'll create in Step 3. Make sure there are no blank rows before or after the list of email addresses.

Step 3: Run the script to create a hold and add users

When you run the script in this step, it will prompt you for the following information. Be sure to have this information ready before you run the script.

  • Your user credentials: The script will use your credentials to connect to Security & Compliance PowerShell. It will also use these credentials to access SharePoint Online to get the OneDrive for Business URLs for the list of users.

  • Name of your SharePoint domain: The script prompts you to enter this name so it can connect to the SharePoint admin center. It also uses the domain name for the OneDrive URLs in your organization. For example, if the URL for your admin center is https://contoso-admin.sharepoint.com and the URL for OneDrive is https://contoso-my.sharepoint.com, then you would enter contoso when the script prompts you for your domain name.

  • Name of the case: The name of an existing case. The script will create a new hold that is associated with this case.

  • Name of the hold: The name of the hold the script will create and associate with the specified case.

  • Search query for a query-based hold: You can create a query-based hold so that only the content that meets the specified search criteria is placed on hold. To place all content on hold, just press Enter when you're prompted for a search query.

  • Turning on the hold or not: You can have the script turn on the hold after it's created or you can have the script create the hold without enabling it. If you don't have the script turn on the hold, you can turn it on later in the compliance portal or by running the following PowerShell commands:

    Set-CaseHoldPolicy -Identity <name of the hold> -Enabled $true
    
    Set-CaseHoldRule -Identity <name of the hold> -Disabled $false
    
  • Name of the text file with the list of users - The name of the text file from Step 2 that contains the list of users to add to the hold. If this file is located in the same folder as the script, just type the name of the file (for example, HoldUsers.txt). If the text file is in another folder, type the full pathname of the file.

After you've collected the information that the script will prompt you for, the final step is to run the script to create the new hold and add users to it.

  1. Save the following text to a Windows PowerShell script file by using a filename suffix of .ps1. For example, AddUsersToHold.ps1.

    #script begin
    " "
    write-host "***********************************************"
    write-host "   Security & Compliance PowerShell  " -foregroundColor yellow -backgroundcolor darkgreen
    write-host "   eDiscovery (Standard) cases - Add users to a hold   " -foregroundColor yellow -backgroundcolor darkgreen
    write-host "***********************************************"
    " "
    # Connect to Security & Compliance PowerShell using modern authentication
    if (!$SccSession)
    {
      Import-Module ExchangeOnlineManagement
      Connect-IPPSSession
    }
    
    # Get the organization's domain name. We use this to create the SharePoint admin URL and root URL for OneDrive for Business.
    ""
    $mySiteDomain = Read-Host "Enter the domain name for your SharePoint organization. We use this name to connect to SharePoint admin center and for the OneDrive URLs in your organization. For example, 'contoso' in 'https://contoso-admin.sharepoint.com' and 'https://contoso-my.sharepoint.com'"
    ""
    
    # Connect to PnP Online using modern authentication
    Import-Module PnP.PowerShell
    Connect-PnPOnline -Url https://$mySiteDomain-admin.sharepoint.com -UseWebLogin
    
    # Load the SharePoint assemblies from the SharePoint Online Management Shell
    # To install, go to https://go.microsoft.com/fwlink/p/?LinkId=255251
    if (!$SharePointClient -or !$SPRuntime -or !$SPUserProfile)
    {
        $SharePointClient = [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client")
        $SPRuntime = [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.Runtime")
        $SPUserProfile = [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.UserProfiles")
        if (!$SharePointClient)
        {
            Write-Error "The SharePoint Online Management Shell isn't installed. Please install it from: https://go.microsoft.com/fwlink/p/?LinkId=255251 and then re-run this script."
            return;
        }
    }
    
    # Get other required information
    do{
    $casename = Read-Host "Enter the name of the case"
    $caseexists = (get-compliancecase -identity "$casename" -erroraction SilentlyContinue).isvalid
    if($caseexists -ne 'True')
    {""
    write-host "A case named '$casename' doesn't exist. Please specify the name of an existing case, or create a new case and then re-run the script." -foregroundColor Yellow
    ""}
    }While($caseexists -ne 'True')
    ""
    do{
    $holdName = Read-Host "Enter the name of the new hold"
    $holdexists=(get-caseholdpolicy -identity "$holdname" -case "$casename" -erroraction SilentlyContinue).isvalid
    if($holdexists -eq 'True')
    {""
    write-host "A hold named '$holdname' already exists. Please specify a new hold name." -foregroundColor Yellow
    ""}
    }While($holdexists -eq 'True')
    ""
    $holdQuery = Read-Host "Enter a search query to create a query-based hold, or press Enter to hold all content"
    ""
    $holdstatus = read-host "Do you want the hold enabled after it's created? (Yes/No)"
    do{
    ""
    $inputfile = read-host "Enter the name of the text file that contains the email addresses of the users to add to the hold"
    ""
    $fileexists = test-path -path $inputfile
    if($fileexists -ne 'True'){write-host "$inputfile doesn't exist. Please enter a valid file name." -foregroundcolor Yellow}
    }while($fileexists -ne 'True')
    #Import the list of addresses from the txt file.  Trim any excess spaces and make sure all addresses
        #in the list are unique.
      [array]$emailAddresses = Get-Content $inputfile -ErrorAction SilentlyContinue | where {$_.trim() -ne ""}  | foreach{ $_.Trim() }
      [int]$dupl = $emailAddresses.count
      [array]$emailAddresses = $emailAddresses | select-object -unique
      $dupl -= $emailAddresses.count
    #Validate email addresses so the hold creation does not run in to an error.
    if($emailaddresses.count -gt 0){
    write-host ($emailAddresses).count "addresses were found in the text file. There were $dupl duplicate entries in the file." -foregroundColor Yellow
    ""
    Write-host "Validating the email addresses. Please wait..." -foregroundColor Yellow
    ""
    $finallist =@()
    foreach($emailAddress in $emailAddresses)
    {
    if((get-recipient $emailaddress -erroraction SilentlyContinue).isvalid -eq 'True')
    {$finallist += $emailaddress}
    else {"Unable to find the user $emailaddress"
    [array]$excludedlist += $emailaddress}
    }
    ""
    #Find user's OneDrive account URL using email address
    Write-Host "Getting the URL for each user's OneDrive for Business site." -foregroundColor Yellow
    ""
    $AdminUrl = "https://$mySiteDomain-admin.sharepoint.com"
    $mySiteUrlRoot = "https://$mySiteDomain-my.sharepoint.com"
    $urls = @()
    foreach($emailAddress in $finallist)
    {
    try
    {
    $url=Get-PnPUserProfileProperty -Account $emailAddress | Select PersonalUrl
    $urls += $url.PersonalUrl
           Write-Host "- $emailAddress => $url"
           [array]$ODadded += $url.PersonalUrl
           }catch {
     Write-Warning "Could not locate OneDrive for $emailAddress"
     [array]$ODExluded += $emailAddress
     Continue }
    }
    $urls | FL
    if(($finallist.count -gt 0) -or ($urls.count -gt 0)){
    ""
    Write-Host "Creating the hold named $holdname. Please wait..." -foregroundColor Yellow
    if(($holdstatus -eq "Y") -or ($holdstatus -eq  "y") -or ($holdstatus -eq "yes") -or ($holdstatus -eq "YES")){
    New-CaseHoldPolicy -Name "$holdName" -Case "$casename" -ExchangeLocation $finallist -SharePointLocation $urls -Enabled $True | out-null
    New-CaseHoldRule -Name "$holdName" -Policy "$holdname" -ContentMatchQuery $holdQuery | out-null
    }
    else{
    New-CaseHoldPolicy -Name "$holdName" -Case "$casename" -ExchangeLocation $finallist -SharePointLocation $urls -Enabled $false | out-null
    New-CaseHoldRule -Name "$holdName" -Policy "$holdname" -ContentMatchQuery $holdQuery -disabled $false | out-null
    }
    ""
    }
    else {"No valid locations were identified. Therefore, the hold wasn't created."}
    #write log files (if needed)
    $newhold=Get-CaseHoldPolicy -Identity "$holdname" -Case "$casename" -erroraction SilentlyContinue
    $newholdrule=Get-CaseHoldRule -Identity "$holdName" -erroraction SilentlyContinue
    if(($ODAdded.count -gt 0) -or ($ODExluded.count -gt 0) -or ($finallist.count -gt 0) -or ($excludedlist.count -gt 0) -or ($newhold.isvalid -eq 'True') -or ($newholdrule.isvalid -eq 'True'))
    {
    Write-Host "Generating output files..." -foregroundColor Yellow
    if($ODAdded.count -gt 0){
    "OneDrive Locations" | add-content .\LocationsOnHold.txt
    "==================" | add-content .\LocationsOnHold.txt
    $newhold.SharePointLocation.name | add-content .\LocationsOnHold.txt}
    if($ODExluded.count -gt 0){
    "Users without OneDrive locations" | add-content .\LocationsNotOnHold.txt
    "================================" | add-content .\LocationsNotOnHold.txt
    $ODExluded | add-content .\LocationsNotOnHold.txt}
    if($finallist.count -gt 0){
    " " | add-content .\LocationsOnHold.txt
    "Exchange Locations" | add-content .\LocationsOnHold.txt
    "==================" | add-content .\LocationsOnHold.txt
    $newhold.ExchangeLocation.name | add-content .\LocationsOnHold.txt}
    if($excludedlist.count -gt 0){
    " "| add-content .\LocationsNotOnHold.txt
    "Mailboxes not added to the hold" | add-content .\LocationsNotOnHold.txt
    "===============================" | add-content .\LocationsNotOnHold.txt
    $excludedlist | add-content .\LocationsNotOnHold.txt}
    $FormatEnumerationLimit=-1
    if($newhold.isvalid -eq 'True'){$newhold|fl >.\GetCaseHoldPolicy.txt}
    if($newholdrule.isvalid -eq 'True'){$newholdrule|Fl >.\GetCaseHoldRule.txt}
    }
    }
    else {"The hold wasn't created because no valid entries were found in the text file."}
    ""
    #Disconnect from SCC PowerShell and PnPOnline
    
    Write-host "Disconnecting from SCC PowerShell and PnP Online" -foregroundColor Yellow
    Get-PSSession | Remove-PSSession
    Disconnect-PnPOnline
    
    Write-host "Script complete!" -foregroundColor Yellow
    ""
    #script end
    
  2. On your local computer, open Windows PowerShell and go to the folder where you saved the script.

  3. Run the script; for example:

    .\AddUsersToHold.ps1
    
  4. Enter the information that the script prompts you for.

    The script connects to Security & Compliance PowerShell, and then creates the new hold in the eDiscovery case and adds the mailboxes and OneDrive for Business for the users in the list. You can go to the case on the eDiscovery page in the compliance portal to view the new hold.

After the script is finished running, it creates the following log files, and saves them to the folder where the script is located.

  • LocationsOnHold.txt: Contains a list of mailboxes and OneDrive for Business sites that the script successfully placed on hold.
  • LocationsNotOnHold.txt: Contains a list of mailboxes and OneDrive for Business sites that the script did not place on hold. If a user has a mailbox, but not a OneDrive for Business site, the user would be included in the list of OneDrive for Business sites that weren't placed on hold.
  • GetCaseHoldPolicy.txt: Contains the output of the Get-CaseHoldPolicy cmdlet for the new hold, which the script ran after creating the new hold. The information returned by this cmdlet includes a list of users whose mailboxes and OneDrive for Business sites were placed on hold and whether the hold is enabled or disabled.
  • GetCaseHoldRule.txt: Contains the output of the Get-CaseHoldRule cmdlet for the new hold, which the script ran after creating the new hold. The information returned by this cmdlet includes the search query if you used the script to create a query-based hold.