Use a script to add users to a hold in an eDiscovery case in the Office 365 Security & Compliance Center

The Office 365 Security & Compliance Center provides lots of Windows PowerShell cmdlets that let you automate time-consuming tasks related to creating and managing eDiscovery cases. Currently, using the eDiscovery case tool in the Security & Compliance Center 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. In future releases of the Security & Compliance Center, this will get easier to do. Until then, you can use the script in this article to automate this process.

The script prompts you for the name of your organization's MySite 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

Before you begin

  • You have to be a member of the eDiscovery Manager role group in the Security & Compliance Center and a SharePoint Online global 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 Security & Compliance Center. 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 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.

Return to top

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 Windows PowerShell environment and perform Step 1 and Step 2 to install the SharePoint Online Management Shell on your local computer.

Return to top

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 Windows 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 a PowerShell command (that you run by using remote PowerShell connected to your Exchange Online organization) 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.

Return to top

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 the Security & Compliance Center with remote 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 MySite domain   The MySite domain is the domain that contains all the OneDrive for Business sites in your organization. For example, if the URL for your MySite domain is https://contoso-my.sharepoint.com, then you would enter contoso when the script prompts you for the name of your MySite domain.

  • 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.

  • Whether or not to turn the hold on   You can have the script turn the hold on 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 Security & Compliance Center 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 "   Office 365 Security & Compliance Center   " -foregroundColor yellow -backgroundcolor darkgreen
    write-host "   eDiscovery cases - Add users to a hold   " -foregroundColor yellow -backgroundcolor darkgreen 
    write-host "***********************************************"
    " " 
    # Get user credentials & Connect to Office 365 SCC, SPO
    
    $credentials = Get-Credential -Message "Specify your credentials to connect to the Office 365 Security & Compliance Center and SharePoint Online"
    $s = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "https://ps.compliance.protection.outlook.com/powershell-liveid" -Credential $credentials -Authentication Basic -AllowRedirection -SessionOption (New-PSSessionOption -SkipCACheck -SkipCNCheck -SkipRevocationCheck)
    $a = Import-PSSession $s -AllowClobber
    
        if (!$s)
        {
            Write-Error "Couldn't create PowerShell session."
            return;
        }
    
    # Load the SharePoint assemblies from the SharePoint Online Management Shell
    # To install, go to http://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: http://go.microsoft.com/fwlink/p/?LinkId=255251 and then re-run this script."
            return;
        }
    }
    
    if (!$spCreds)
    {
        $spCreds = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($credentials.UserName, $credentials.Password)
    }
    
    # Get the user's MySite domain name. We use this to create the admin URL and root URL for OneDrive for Business
    ""
    $mySiteDomain = Read-Host "Enter the name of your organization's MySite domain. For example, 'contoso' for 'https://contoso-my.sharepoint.com'"
    ""
    
    # 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 Site 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"
    
    # Add the path of the User Profile Service to the SPO admin URL, then create a new webservice proxy to access it
    $proxyaddr = "$AdminUrl/_vti_bin/UserProfileService.asmx?wsdl"
    $UserProfileService= New-WebServiceProxy -Uri $proxyaddr -UseDefaultCredential False
    $UserProfileService.Credentials = $credentials
    
    # Take care of auth cookies
    $strAuthCookie = $spCreds.GetAuthenticationCookie($AdminUrl)
    $uri = New-Object System.Uri($AdminUrl)
    $container = New-Object System.Net.CookieContainer
    $container.SetCookies($uri, $strAuthCookie)
    $UserProfileService.CookieContainer = $container
    $urls = @()
    foreach($emailAddress in $emailAddresses)
    {
          try{
            $prop = $UserProfileService.GetUserProfileByName("i:0#.f|membership|$emailAddress") | Where-Object { $_.Name -eq "PersonalSpace" }
            $url = $prop.values[0].value
    		if($url -ne $null){
            $furl = $mySiteUrlRoot + $url
            $urls += $furl
            Write-Host "- $emailAddress => $furl"
    		[array]$ODadded += $furl}
    	else{    
            Write-Warning "Couldn't locate OneDrive for $emailAddress"
    		[array]$ODExluded += $emailAddress
        }}
    	catch { 
    	Write-Warning "Could not locate OneDrive for $emailAddress"
    	[array]$ODExluded += $emailAddress
    	Continue }
    }
    
    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 $true | 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."}
    ""
    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 Center using remote 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 Security & Compliance Center 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.

Return to top

Expand your skills
Explore training
Get new features first
Join Office Insiders

Was this information helpful?

Thank you for your feedback!

Thank you for your feedback! It sounds like it might be helpful to connect you to one of our Office support agents.

×