Use a script to create a report on holds in eDiscovery cases
Tip
eDiscovery (preview) is now available in the new Microsoft Purview portal. To learn more about using the new eDiscovery experience, see Learn about eDiscovery (preview).
The script in this article lets eDiscovery administrators and eDiscovery managers generate a report that contains information about all holds that are associated with eDiscovery (Standard) and eDiscovery (Premium) cases in the Microsoft Purview compliance portal. The report contains information such as the name of the case a hold is associated with, the content locations that are placed on hold, and whether the hold is query-based. If there are cases that don't have any holds, the script creates an additional report with a list of cases without holds.
See the More information section for a detailed description of the information included in the report.
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 trials hub. Learn details about signing up and trial terms.
Admin requirements and script information
- To generate a report on all eDiscovery cases in your organization, you have to be an eDiscovery Administrator in your organization. If you're an eDiscovery Manager, the report will only include information about the cases that you can access. For more information about eDiscovery permissions, see Assign eDiscovery permissions.
- The script in this article has minimal error handling. The primary purpose is to quickly create report about the holds that are associated with the eDiscovery cases in your organization.
- The sample scripts provided in this article 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: Connect to Security & Compliance PowerShell
The first step is to connect to Security & Compliance PowerShell for your organization. For step-by-step instructions, see Connect to Security & Compliance PowerShell.
Step 2: Run the script to report on holds associated with eDiscovery cases
After you've connected to Security & Compliance PowerShell, the next step is to create and run the script that collects information about the eDiscovery cases in your organization.
Save the following text to a Windows PowerShell script file by using a filename suffix of .ps1; for example, CaseHoldsReport.ps1.
#script begin " " write-host "***********************************************" write-host "Security & Compliance Center " -foregroundColor yellow -backgroundcolor darkgreen write-host "eDiscovery cases - Holds report " -foregroundColor yellow -backgroundcolor darkgreen write-host "***********************************************" " " #prompt users to specify a path to store the output files $time = get-date -Format dd-MM-yyyy_hh.mm $Path = Read-Host 'Enter a folder path to save the report to a .csv file (filename is created automatically)' $outputpath = $Path + '\' + 'CaseHoldsReport' + ' ' + $time + '.csv' $noholdsfilepath = $Path + '\' + 'CaseswithNoHolds' + $time + '.csv' #add case details to the csv file function add-tocasereport { Param([string]$casename, [String]$casetype, [String]$casestatus, [datetime]$casecreatedtime, [string]$casemembers, [datetime]$caseClosedDateTime, [string]$caseclosedby, [string]$holdname, [String]$Holdenabled, [string]$holdcreatedby, [string]$holdlastmodifiedby, [string]$ExchangeLocation, [string]$sharePointlocation, [string]$ContentMatchQuery, [datetime]$holdcreatedtime, [datetime]$holdchangedtime, [string]$holdstatus, [string]$holderror ) $addRow = New-Object PSObject Add-Member -InputObject $addRow -MemberType NoteProperty -Name "Case name" -Value $casename Add-Member -InputObject $addRow -MemberType NoteProperty -Name "Case type" -Value $casetype Add-Member -InputObject $addRow -MemberType NoteProperty -Name "Case status" -Value $casestatus Add-Member -InputObject $addRow -MemberType NoteProperty -Name "Case members" -Value $casemembers Add-Member -InputObject $addRow -MemberType NoteProperty -Name "Case created time" -Value $casecreatedtime Add-Member -InputObject $addRow -MemberType NoteProperty -Name "Case closed time" -Value $caseClosedDateTime Add-Member -InputObject $addRow -MemberType NoteProperty -Name "Case closed by" -Value $caseclosedby Add-Member -InputObject $addRow -MemberType NoteProperty -Name "Hold name" -Value $holdname Add-Member -InputObject $addRow -MemberType NoteProperty -Name "Hold enabled" -Value $Holdenabled Add-Member -InputObject $addRow -MemberType NoteProperty -Name "Hold created by" -Value $holdcreatedby Add-Member -InputObject $addRow -MemberType NoteProperty -Name "Hold last changed by" -Value $holdlastmodifiedby Add-Member -InputObject $addRow -MemberType NoteProperty -Name "Exchange locations" -Value $ExchangeLocation Add-Member -InputObject $addRow -MemberType NoteProperty -Name "SharePoint locations" -Value $sharePointlocation Add-Member -InputObject $addRow -MemberType NoteProperty -Name "Hold query" -Value $ContentMatchQuery Add-Member -InputObject $addRow -MemberType NoteProperty -Name "Hold created time (UTC)" -Value $holdcreatedtime Add-Member -InputObject $addRow -MemberType NoteProperty -Name "Hold changed time (UTC)" -Value $holdchangedtime Add-Member -InputObject $addrow -MemberType NoteProperty -Name "Hold Status" -Value $holdstatus Add-Member -InputObject $addrow -MemberType NoteProperty -Name "Hold Error" -Value $holderror $allholdreport = $addRow | Select-Object "Case name", "Case type", "Case status", "Hold name", "Hold enabled", "Case members", "Case created time", "Case closed time", "Case closed by", "Exchange locations", "SharePoint locations", "Hold query", "Hold created by", "Hold created time (UTC)", "Hold last changed by", "Hold changed time (UTC)", "Hold Status", "Hold Error" $allholdreport | export-csv -path $outputPath -notypeinfo -append -Encoding ascii } #get information on the cases and pass values to the case report function " " write-host "Gathering a list of eDiscovery (Standard) cases and holds..." " " $edc = Get-ComplianceCase -ErrorAction SilentlyContinue foreach ($cc in $edc) { write-host "Working on case :" $cc.name if ($cc.status -eq 'Closed') { $cmembers = ((Get-ComplianceCaseMember -Case $cc.name).windowsLiveID) -join ';' add-tocasereport -casename $cc.name -casetype $cc.casetype -casestatus $cc.Status -caseclosedby $cc.closedby -caseClosedDateTime $cc.ClosedDateTime -casemembers $cmembers } else { $cmembers = ((Get-ComplianceCaseMember -Case $cc.name).windowsLiveID) -join ';' $policies = Get-CaseHoldPolicy -Case $cc.Name | % { Get-CaseHoldPolicy $_.Name -Case $_.CaseId -DistributionDetail } if ($policies -ne $NULL) { foreach ($policy in $policies) { $rule = Get-CaseHoldRule -Policy $policy.name add-tocasereport -casename $cc.name -casetype $cc.casetype -casemembers $cmembers -casestatus $cc.Status -casecreatedtime $cc.CreatedDateTime -holdname $policy.name -holdenabled $policy.enabled -holdcreatedby $policy.CreatedBy -holdlastmodifiedby $policy.LastModifiedBy -ExchangeLocation (($policy.exchangelocation.name) -join ';') -SharePointLocation (($policy.sharePointlocation.name) -join ';') -ContentMatchQuery $rule.ContentMatchQuery -holdcreatedtime $policy.WhenCreatedUTC -holdchangedtime $policy.WhenChangedUTC -holdstatus $policy.DistributionStatus -holderror $policy.DistributionResults } } else { Write-Host "No hold policies found in case:" $cc.name -foregroundColor 'Yellow' " " [string]$cc.name | out-file -filepath $noholdsfilepath -append } } } #get information on the cases and pass values to the case report function " " write-host "Gathering a list of eDiscovery (Premium) cases and holds..." " " $edc = Get-ComplianceCase -CaseType Advanced -ErrorAction SilentlyContinue foreach ($cc in $edc) { write-host "Working on case :" $cc.name if ($cc.status -eq 'Closed') { $cmembers = ((Get-ComplianceCaseMember -Case $cc.name).windowsLiveID) -join ';' add-tocasereport -casename $cc.name -casestatus $cc.Status -casetype $cc.casetype -caseclosedby $cc.closedby -caseClosedDateTime $cc.ClosedDateTime -casemembers $cmembers } else { $cmembers = ((Get-ComplianceCaseMember -Case $cc.name).windowsLiveID) -join ';' $policies = Get-CaseHoldPolicy -Case $cc.Name | % { Get-CaseHoldPolicy $_.Name -Case $_.CaseId -DistributionDetail } if ($policies -ne $NULL) { foreach ($policy in $policies) { $rule = Get-CaseHoldRule -Policy $policy.name add-tocasereport -casename $cc.name -casetype $cc.casetype -casemembers $cmembers -casestatus $cc.Status -casecreatedtime $cc.CreatedDateTime -holdname $policy.name -holdenabled $policy.enabled -holdcreatedby $policy.CreatedBy -holdlastmodifiedby $policy.LastModifiedBy -ExchangeLocation (($policy.exchangelocation.name) -join ';') -SharePointLocation (($policy.sharePointlocation.name) -join ';') -ContentMatchQuery $rule.ContentMatchQuery -holdcreatedtime $policy.WhenCreatedUTC -holdchangedtime $policy.WhenChangedUTC -holdstatus $policy.DistributionStatus -holderror $policy.DistributionResults } } else { write-host "No hold policies found in case:" $cc.name -foregroundColor 'Yellow' " " [string]$cc.name | out-file -filepath $noholdsfilepath -append } } } " " Write-host "Script complete! Report files saved to this folder: '$Path'" " " #script end
In the Windows PowerShell session that opened in Step 1, go to the folder where you saved the script.
Run the script; for example:
.\CaseHoldsReport.ps1
The script prompts for a target folder to save the report to.
Type the full path name of the folder to save the report to, and then press Enter.
Tip
To save the report in the same folder that the script is located in, type a period (".") when prompted for a target folder. To save the report in a subfolder in the folder where the script is located, just type the name of the subfolder.
The script starts to collect information about all the eDiscovery cases in your organization. Don't access the report file while the script is running. After the script is complete, a confirmation message is displayed in the Windows PowerShell session. After this message is displayed, you can access the report in the folder that you specified in Step 4. The file name for the report is
CaseHoldsReport<DateTimeStamp>.csv
.Additionally, the script also creates a report with a list of cases that don't have any holds. The file name for this report is
CaseswithNoHolds<DateTimeStamp>.csv
.Here's an example of running the CaseHoldsReport.ps1 script.
More information
The case holds report that's created when you run the script in this article contains the following information about each hold. As previously explained, you have to be an eDiscovery Administrator to return information for all holds in your organization. For more information about case holds, see eDiscovery cases.
- The name of the hold and the name of the eDiscovery case that the hold is associated with.
- Whether the hold is associated with an eDiscovery (Standard) or eDiscovery (Premium) case.
- Whether or not the eDiscovery case is active or closed.
- Whether or not the hold is enabled or disabled.
- The members of the eDiscovery case that the hold is associated with. Case members can view or manage a case, depending on the eDiscovery permissions they've been assigned.
- The time and date the case was created.
- If a case is closed, the person who closed it and the time and date it was closed.
- The Exchange mailboxes and SharePoint sites locations that are on hold.
- If the hold is query-based, the query syntax.
- The time and date the hold was created and the person who created it.
- The time and date the hold was last changed and the person who changed it.