Get-SCCExportDataSize.ps1
<#PSScriptInfo
.VERSION 4.1 .GUID 7451e210-6009-47aa-ac9e-ea38113ed0eb .AUTHOR Aaron Guilmette .COMPANYNAME Microsoft .COPYRIGHT 2020 .TAGS .LICENSEURI .PROJECTURI https://www.undocumented-features.com/2019/02/21/calculating-your-daily-export-for-the-security-compliance-center/ .ICONURI .EXTERNALMODULEDEPENDENCIES .REQUIREDSCRIPTS .EXTERNALSCRIPTDEPENDENCIES .RELEASENOTES .DESCRIPTION Review and calculate Security & Compliance Center Export volume. .PRIVATEDATA #> <# .SYNOPSIS Review and calculate Security & Compliance Center Export volume. .PARAMETER CaseName Review export statistics for a particular case. Default will review 24 hours. Use the Hours parameter to expand the scope. .PARAMETER Credential Is what it says it is. The credential specified must have the ability to run Get-ComplianceCase. If the credential specified does not have eDiscovery Admin privileges, only eDiscovery cases that the credential has created or has been made a member of will be tallied. .PARAMETER Hours Number of hours to go back looking for export statistics. .PARAMETER IncludeContentSearch Include Content Search queries as part of the export total. .PARAMETER Logfile Specify the name of the logfile (if Report is specified). .PARAMETER Report Choose to output a CSV report. .PARAMETER SearchType Select mechanism for searching. Search can be: - SearchByCaseLastModifiedDate: Uses the LastModifiedTimeDate on the actual case. - SearchByRecentOnly: Uses the -RecentOnly switch for Get-ComplianceCase, which appears to use access time in last 24 hours. - SearchAllCases: Search all cases. You may want to use the -Hours parameter to scope further back. .EXAMPLE .\Get-SCCExportDataSize.ps1 Run with defaults for SearchType SearchByRecentOnly and looking for exports within last 24 hours. .EXAMPLE .\Get-SCCExportDataSize.ps1 -Hours 48 -SearchType SearchAllCases Search all cases for exports in the last 48 hours. .EXAMPLE .\Get-SCCExportDataSize.ps1 -Hours 48 -CaseName "My Test Case" Search case "My Test Case" for exports in the last 48 hours. .LINK https://www.undocumented-features.com/2019/02/21/calculating-your-daily-export-for-the-security-compliance-center/ .NOTES 2020-04-21 - Updated for PowerShell Gallery. 2019-03-07 - Updated to fix a typo. 2019-03-05 - Update to include job start/end times and user ID information in output. 2019-02-27 - Update to include regular content search. Updated Get-ComplianceSearchAction to use -Export filter. 2019-02-21 - Update to report on eDiscovery case Admins membership. 2019-02-21 - Initial release. #> param ( [array]$CaseName, $Credential, [int]$Hours = "24", [switch]$IncludeContentSearch, [string]$Logfile = (Get-Date -Format yyyy-MM-dd) + "_SCCExportDataSize.csv", [switch]$Report, [ValidateSet('SearchByCaseLastModifiedDate','SearchByRecentOnly','SearchAllCases')]$SearchType = "SearchByRecentOnly" ) # Check for Get-ComplianceCase cmdlet. If the Get-ComplianceCase cmdlet is not # found, it's most likely becuase no connection to the Security & Compliance Center # Powershell is not present. If (!(Get-Command Get-ComplianceCase -ea silentlycontinue)) { try { $ComplianceSession = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.compliance.protection.outlook.com/powershell-liveid -Credential $Credential -Authentication Basic -AllowRedirection } catch { Write-Error -Message "Something went wrong."} If ($Credential) { Import-PSSession $ComplianceSession -AllowClobber } } # Check for Get-ComplianceCase cmdlet again If (!(Get-Command Get-ComplianceCase -ea silentlycontinue)) { Write-Error -Message "The required command `'Get-ComplianceCase`' is not available. Please connect to the Security & Compliance Center powershell using a credential with access to Get-ComplianceCase." Exit } # Check for Get-EDiscoveryCaseAdmin cmdlet. Only members of eDiscoveryCaseAdmins # have this cmdlet available. If you are running this and are NOT a member of # this group, then you will only see results of cases for which you are owner # or member. If (!(Get-Command Get-EDiscoveryCaseAdmin -ea silentlycontinue)) { Write-Warning -Message "Current user is not a member of eDiscovery Case Admins. Only cases for which user has created or is a member will be included in output." } # If we just want to look at a few particular cases, use -CaseName parameter If ($CaseName) { $Cases = @() foreach ($obj in $CaseName) { $Cases += Get-ComplianceCase $obj } } # Default will search all recent cases using -RecentOnly parameter. RecentOnly # is an undocumented parameter, but it appears to look at cases accessed in the # last 24 hours. If SearchByCaseLastModifiedDate param is present, # select only cases that show they were modified in the last $Hours hours using # the LastModifiedDateTime property. The LastModifiedDateTime property ONLY # pertains to the actual ComplianceCase container (adding/removing members, # updating the description, or other things that impact the case display). The # LastModifiedDateTime DOES NOT get updated if case searches or holds are # added/deleted. SearchAllCases should be obvious. switch ($SearchType) { SearchByCaseLastModifiedDate { # Get only cases modified in the last $Hours period # Write-Host -ForegroundColor Green "Using SearchByCaseLastModifiedDate." $Cases = Get-ComplianceCase | ? { $_.LastModifiedDateTime -gt (Get-Date).AddHours(-$($Hours)) } } SearchByRecentOnly { # Get all cases using RecentOnly parameter. RecentOnly appears to look at # cases accessed in the last 24 hours, but it is not documented. # Write-Host -ForegroundColor Green "Using SearchByRecentOnly." If ($PSBoundParameters.Keys -match "Hours") { Write-Host -ForegroundColor Red "You can't use the Hours parameter with SearchType SearchByRecentOnly. You should use -SearchType SearchAllCases." Exit } Else { $Cases = Get-ComplianceCase -RecentOnly } } SearchAllCases { # Write-Host -ForegroundColor Green "Using SearchAllCases." $Cases = Get-ComplianceCase } } $global:Exports = @() # Process eDiscovery cases $TotalCases = $Cases.Count $i = 1 Foreach ($Case in $Cases) { Write-Progress -Activity "Processing eDiscovery cases" -PercentComplete (($i / $TotalCases) * 100) -Status "$($TotalCases - $i) cases remaining" -Id 1 Write-Progress -Activity "Processing case $($Case.Name)" -Id 2 -ParentId 1 [array]$Results = (Get-ComplianceSearchAction -Export -Case $Case.Identity | ? { $_.LastModifiedTime -gt (Get-Date).AddHours(-$($Hours))}) If ($Results.Results) { $ResultsCount = $Results.Count $r = 1 Foreach ($Result in $Results) { Write-Progress -Activity "Calculating exports" -PercentComplete (($r / $ResultsCount) * 100) -Id 3 -ParentId 2 $global:Data = $Result.Results.Split(";") [string]$Bytes = ($Data -match "Total transferred bytes") If ($Bytes -ne "") { $Bytes = $Bytes.Split(":")[1].Trim() } [int]$TotalTransferredBytes = $Bytes $global:DataObj = [ordered]@{ CaseId = $Case.Identity CaseName = $Case.Name ContentSearchName = $Result.SearchName ExportName = $Result.Name ExportedBytes = $TotalTransferredBytes CreatedBy = $Result.CreatedBy RunBy = $Result.RunBy StartTime = $Result.JobStartTime EndTime = $Result.JobEndTime } $global:ResultData = New-Object PSObject -Property $DataObj $global:Exports += $ResultData $r++ Remove-Variable Bytes,TotalTransferredBytes } } $i++ } # Process Content Search if ($IncludeContentSearch) { switch ($SearchType) { SearchByCaseLastModifiedDate { # Get only content searches modified in the last $Hours period [array]$ContentSearches = (Get-ComplianceSearchAction -Export | ? { $_.LastModifiedTime -gt (Get-Date).Subtract(- $Hours) }) } SearchByRecentOnly { # Get all content searches by date modified in the last 24 hours. # The undocumented -RecentOnly parameter only applies to eDiscovery # cases; I have substituted the value '24' to match what appears to # be the behavior. If ($PSBoundParameters.Keys -match "Hours") { Write-Host -ForegroundColor Red "You can't use the Hours parameter with SearchType SearchByRecentOnly. You should use -SearchType SearchAllCases." Exit } Else { [array]$ContentSearches = (Get-ComplianceSearchAction -Export | ? { $_.LastModifiedTime -gt (Get-Date).AddHours(-24) }) } } SearchAllCases { # Write-Host -ForegroundColor Green "Using SearchAllCases." $cmd = "[array]`$ContentSearches = Get-ComplianceSearchAction -Export -Resultsize Unlimited" [array]$ContentSearches = Get-ComplianceSearchAction -Export -Resultsize Unlimited if ($PSBoundParameters.Keys -match "Hours") { $cmd += " | ? { `$_.LastModifiedTime -gt (Get-Date).AddHours(- `$Hours) }" } Invoke-Expression $cmd } } $TotalContentSearches = $ContentSearches.Count If ($TotalContentSearches -eq 0) { Write-Host -ForegroundColor Yellow "Environment has no content searches meeting the filter criteria." } $j = 1 Foreach ($Search in $ContentSearches) { Write-Progress -Activity "Processing Content Searches" -PercentComplete (($j / $TotalContentSearches) * 100) -Status "$($TotalContentSearches - $j) searches remaining" -Id 3 Write-Progress -Activity "Processing search $($Search.Name)" -Id 4 -ParentId 3 [array]$Results = $Search.Results If ($Results) { $ResultsCount = $Results.Count $s = 1 Foreach ($Result in $Results) { Write-Progress -Activity "Calculating exports" -PercentComplete (($s / $ResultsCount) * 100) -Id 3 -ParentId 2 $global:Data = $Result.Split(";") [string]$Bytes = ($Data -match "Total transferred bytes") If ($Bytes -ne "") { $Bytes = $Bytes.Split(":")[1].Trim() } [int]$TotalTransferredBytes = $Bytes $global:DataObj = [ordered]@{ CaseId = "N/A" CaseName = "N/A" ContentSearchName = $Search.SearchName ExportName = $Search.Name ExportedBytes = $TotalTransferredBytes CreatedBy = $Search.CreatedBy RunBy = $Search.RunBy StartTime = $Search.JobStartTime EndTime = $Search.JobEndTime } $global:ResultData = New-Object PSObject -Property $DataObj $global:Exports += $ResultData $s++ Remove-Variable Bytes, TotalTransferredBytes } } $j++ } } # Add it all up $Sum = [math]::Round((($Exports.ExportedBytes | Measure-Object -Sum).Sum / 1024768)) If ($Report) { $Exports | Export-Csv $Logfile -Force -NoTypeInformation Write-Host -ForegroundColor Green "Report has been exported to $($Logfile)." } Write-Host "You have exported $($Sum) megabytes in the last $($Hours) hours. View exports stored in the `$Exports object in your current shell." |