get-userholdpolicies.ps1
<#PSScriptInfo
.VERSION 4.0 .GUID ca632a17-1da3-4069-88c1-5aaf2cce3a1b .AUTHOR Aaron Guilmette .COMPANYNAME Microsoft .COPYRIGHT 2020 .TAGS .LICENSEURI .PROJECTURI https://www.undocumented-features.com/2018/05/15/update-to-the-get-userholdpolicies-tool/ .ICONURI .EXTERNALMODULEDEPENDENCIES .REQUIREDSCRIPTS .EXTERNALSCRIPTDEPENDENCIES .RELEASENOTES .DESCRIPTION View hold policies applied to one or more mailboxes. .PRIVATEDATA #> <# .SYNOPSIS List holds applied to a user. This script will return holds applied to users through the mechanisms of: - Exchange Online in-place holds - Litigation hold - MRM policies with retention policy tags - Security & Compliance Center eDiscovery cases with holds - Security & Compliance Center retention policies In order to return data for eDiscovery cases, the account used to run the script must be a member of eDiscovery Administrators or be a member of every open case. .PARAMETER Credential Specify a PSCredential object for connecting to Exchange Online and the Security & Compliane Center. .PARAMETER ExcludeLegacyExchangePolicies Choose whether to exclude legacy MRM policies for Exchange Online mailboxes. If not specified, attempt to locate policies that have policy tags with retention enabled. Legacy Exchange MRM policies don't actually have retention, per se, but they can be the source of unexpected behavior if content is being moved/deleted without a user's knowledge. .PARAMETER Identity Specify an individual user for retreiving hold policies. .EXAMPLE .\Get-UserHoldPolicies.ps1 -Identity AdeleV Displays all of the holds applied to user AdeleV. .EXAMPLE Get-Mailbox -Resultsize Unlimited | .\Get-UserHoldPolicies.ps1 Displays all explicit holds applied to all mailboxes. .EXAMPLE Get-Mailbox -Resultsize Unlimited | .\Get-UserHoldPolicies.ps1 -IncludeInheritiedPolicies Displays all holds (explicit and inherited) applied to all mailboxes. .EXAMPLE Get-Mailbox -Resultsize Unlimited | .\Get-UserHoldPolicies.ps1 -OutputFile C:\Temp\UserHolds.csv Export all explicit holds applied to all mailboxes to CSV C:\Temp\UserHolds.csv .EXAMPLE (.\Get-UserHoldPolicies.ps1 -Identity AdeleV) -OutputFile C:\Temp\AdeleVHolds.csv Export all explicit holds applied to AdeleV to CSV C:\Temp\AdeleVHolds.csv .EXAMPLE Get-Mailbox AdeleV | .\Get-UserHoldPolicies.ps1 -IncludeInheritedPolicies Display all (including inherited) holds applied to AdeleV. .EXAMPLE $Holds = Get-Mailbox A* | .\Get-UserHoldPolicies.ps1 -IncludeInheritedPolicies Save all holds for users starting with A to $holds variable. .NOTES 2019-06-04 Added information for DelayHoldApplied 2019-01-23 Added display info for excluded legacy policies and disposition after expiration. Updated URL in Links. 2018-05-15 Added capability to display global inherited policies. Added OutputFile parameter. Updated output for MRM policies to display object guid. 2017-10-31 Initial release. .LINK https://www.undocumented-features.com/2019/01/23/update-to-the-get-userholdpolicies-tool-2/ .LINK https://www.undocumented-features.com/2018/05/15/update-to-the-get-userholdpolicies-tool/ .LINK https://www.undocumented-features.com/2017/10/31/display-or-export-all-user-mailbox-holds/ #> [CmdletBinding()] param ( [System.Management.Automation.PSCredential]$Credential, [switch]$ExcludeLegacyExchangePolicies = $True, [Parameter(Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelineByPropertyName = $True,Position=1)] $Identity, [switch]$IncludeInheritedPolicies, [string]$OutputFile ) begin { If (!(Get-Command Get-Mailbox -ea silentlycontinue)) { $Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $Credential -Authentication Basic -AllowRedirection Import-PSSession $Session } If (!(Get-Command Get-CaseHoldPolicy -ea silently continue)) { $ComplianceSession = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.compliance.protection.outlook.com/powershell-liveid -Credential $Credential -Authentication Basic -AllowRedirection Import-PSSession $ComplianceSession -AllowClobber } # Check parameters If ($PSBoundParameters.ContainsKey('Identity') -and $MyInvocation.PipelineLength -eq 1) { [object[]]$Identity = Get-Mailbox $Identity } [pscustomobject]$ErrorData = @() [pscustomobject]$Data = @() # Exclude processing of legacy Exchange Retention Policies if # -ExcludeLegacyExchangePolicies is set. This checks for policies that have # policy tags with retention enabled. If (!($ExcludeLegacyExchangePolicies)) { $LegacyRetentionPolicies = Get-RetentionPolicy $LegacyRetentionPoliciesWithRetentionEnabled = @() Get-RetentionPolicy | % { foreach ($tag in $_.RetentionPolicyTagLinks) { If ((Get-RetentionPolicyTag $Tag).RetentionEnabled -eq $True) { $LegacyRetentionPoliciesWithRetentionEnabled += $_.Name } } } $LegacyRetentionPoliciesWithRetentionEnabled = $LegacyRetentionPoliciesWithRetentionEnabled | Sort -Unique } # End If !$ExcludeLegacyExchangePolicies # Retrieve inherited policies created in the Security & Compliance Center. # This is determined by looking for policies where the ExchangeLocation is # specified as "All", since those policies are not stamped on the mailbox. If ($IncludeInheritedPolicies) { $InheritedPolicies = (Get-RetentionCompliancePolicy -DistributionDetail) | ? { $_.ExchangeLocation -match "All" -and $_.Enabled -eq $True -and $_.DistributionStatus -eq "Success" -and $_.Mode -eq "Enforce"} } # End If $IncludeInheritedPolicies } process { If ($PSBoundParameters.ContainsKey('Identity') -and $MyInvocation.PipelineLength -eq 1) { $DisplayName = $Identity.Name $PrimarySmtp = $Identity.PrimarySmtpAddress $InPlaceHoldPolicies = $Identity.InPlaceHolds [bool]$LitigationHold = $Identity.LitigationHoldEnabled If ($Identity.RetentionPolicy -iin $LegacyRetentionPoliciesWithRetentionEnabled) { $ExoRetentionPolicy = $Identity.RetentionPolicy } } Else { $DisplayName = $_.Name $PrimarySmtp = $_.PrimarySmtpAddress $InPlaceHoldPolicies = $_.InPlaceHolds [bool]$LitigationHold = $_.LitigationHoldEnabled If ($_.RetentionPolicy -iin $LegacyRetentionPoliciesWithRetentionEnabled) { $ExoRetentionPolicy = $_.RetentionPolicy } } # Process values that appear in InPlaceHoldPolicies Foreach ($pol in $InPlaceHoldPolicies) { # eDiscovery Cases if ($pol -match "UniH") { $Type = "eDiscoveryCase" $policy = $pol.Substring(4) try { $Data += Get-CaseHoldPolicy -Identity $Policy | Select ` @{ N = "Username"; E = { $DisplayName } }, @{ N = "Mail"; E = { $PrimarySmtp } }, @{ N = "Hold Placed By"; E = { $_.Name } }, @{ N = "Policy Guid"; E = { $Policy } }, @{ N = "Case Name"; E = { (Get-ComplianceCase $_.CaseID).Name } }, @{ N = "Case Guid"; E = { $_.CaseID } }, @{ N = "Hold Type"; E = { $Type } }, @{ N = "Delete Type"; E= { "N/A" } } } catch { $ErrorDetail = $_.Exception.Message.ToString() $ErrorData += @{ 'Username' = $DisplayName; 'Mail' = $PrimarySmtp; 'ErrorMesage' = $ErrorDetail } } } # Security & Compliance Center Retention Policies. These policies are # reflected in the "InPlaceHolds" property of a mailbox. if ($pol -match "^mbx") { $Type = "SecComplianceRetentionPolicy-Mailbox" $policy = $pol.Substring(3).Split(":")[0] $policyDeleteTypeValue = $pol.Substring(3).Split(":")[1] switch ($PolicyDeleteTypeValue) { 1 { $PolicyDeleteType = "DeleteOnly" } 2 { $PolicyDeleteType = "RetainNoDeleteAtExpiration" } 3 { $PolicyDeleteType = "RetainAndDeleteAtExpiration"} } $Data += Get-RetentionCompliancePolicy $policy | select ` @{ N = "Username"; E = { $DisplayName } }, @{ N = "Mail"; E = { $PrimarySmtp } }, @{ N = "Hold Placed By"; E = { $_.Name } }, @{ N = "Policy Guid"; E = { $policy } }, @{ N = "Case Name"; E = { "Not Applicable" } }, @{ N = "Case Guid"; E = { "Not Applicable" } }, @{ N = "Hold Type"; E = { $Type } }, @{ N = "Delete Type"; E = { $PolicyDeleteType } } } if ($pol -match "^\-mbx") { $Type = "ExcludedSecComplianceRetentionPolicy" $policy = $pol.Substring(4).Split(":")[0] $policyDeleteTypeValue = $pol.Substring(3).Split(":")[1] switch ($PolicyDeleteTypeValue) { 1 { $PolicyDeleteType = "DeleteOnly" } 2 { $PolicyDeleteType = "RetainNoDeleteAtExpiration" } 3 { $PolicyDeleteType = "RetainAndDeleteAtExpiration" } } $Data += Get-RetentionCompliancePolicy $policy | select ` @{ N = "Username"; E = { $DisplayName } }, @{ N = "Mail"; E = { $PrimarySmtp } }, @{ N = "Hold Placed By"; E = { $_.Name } }, @{ N = "Policy Guid"; E = { $policy } }, @{ N = "Case Name"; E = { "Not Applicable" } }, @{ N = "Case Guid"; E = { "Not Applicable" } }, @{ N = "Hold Type"; E = { $Type } }, @{ N = "Delete Type"; E = { $PolicyDeleteType } } } if ($pol -match "^skp") { $Type = "SecComplianceRetentionPolicy-Skype" $policy = $pol.Substring(3).Split(":")[0] $policyDeleteTypeValue = $pol.Substring(3).Split(":")[1] switch ($PolicyDeleteTypeValue) { 1 { $PolicyDeleteType = "DeleteOnly" } 2 { $PolicyDeleteType = "RetainNoDeleteAtExpiration" } 3 { $PolicyDeleteType = "RetainAndDeleteAtExpiration" } } $Data += Get-RetentionCompliancePolicy $policy | select ` @{ N = "Username"; E = { $DisplayName } }, @{ N = "Mail"; E = { $PrimarySmtp } }, @{ N = "Hold Placed By"; E = { $_.Name } }, @{ N = "Policy Guid"; E = { $policy } }, @{ N = "Case Name"; E = { "Not Applicable" } }, @{ N = "Case Guid"; E = { "Not Applicable" } }, @{ N = "Hold Type"; E = { $Type } }, @{ N = "Delete Type"; E = { $PolicyDeleteType } } } } # End Foreach $pol in $InPlaceHoldPolicies # Check for Object's LitigationHold property. You can query this property # via Get-Mailbox and look for the LitigationHoldEnabled property. If ($LitigationHold -eq $True) { $Type = "LitigationHold" $Policy = "Mailbox Litigation Hold" $LitigationHoldData = @{ 'Username' = $DisplayName; 'Mail' = $PrimarySmtp; 'Hold Placed By' = $Policy; 'Policy Guid' = "Not Applicable"; 'Case Name' = "Not Applicable"; 'Case Guid' = "Not Applicable"; 'Hold Type' = $Type; 'Delete Type' = "Not Applicable" } $LitigationHoldRowData = [pscustomobject]$LitigationHoldData $Data += $LitigationHoldRowData } # End If $LitigationHold # Include Inherited policies from the Security & Compliance Center. These # policies are not stamped on the mailbox. If ($IncludeInheritedPolicies) { foreach ($InheritedPolicy in $InheritedPolicies) { $Type = "SecComplianceRetentionPolicy (Inherited)" $Policy = $InheritedPolicy.Name $Guid = $InheritedPolicy.Guid $InheritedPolicyData = @{ 'Username' = $DisplayName; 'Mail' = $PrimarySmtp; 'Hold Placed By' = $Policy; 'Policy Guid' = $Guid; 'Case Name' = "Not Applicable"; 'Case Guid' = "Not Applicable"; 'Hold Type' = $Type; 'Delete Type' = "Undetermined" } $InheritedPolicyRowData = [pscustomobject]$InheritedPolicyData $Data += $InheritedPolicyRowData } } # End If IncludeInheritedPolicies # If parameter -ExcludeLegacyExchangePolicies is not set, check the legacy # Exchange policies to see what policies with retention are applied to the # mailbox. If (!($ExcludeLegacyExchangePolicies)) { If ($ExoRetentionPolicy) { $Type = "LegacyRetentionPolicy" $Policy = $ExoRetentionPolicy $PolicyGuid = ($LegacyRetentionPolicies | ? { $_.Name -eq $Policy }).Guid $ExoRetentionPolicyData = @{ 'Username' = $DisplayName; 'Mail' = $PrimarySmtp; 'Hold Placed By' = $Policy; 'Policy Guid' = $PolicyGuid; 'Case Name' = "Not Applicable"; 'Case Guid' = "Not Applicable"; 'Hold Type' = $Type; 'Delete Type' = "Not Applicable" } $ExoRetentionPolicyRowData = [pscustomobject]$ExoRetentionPolicyData $Data += $ExoRetentionPolicyRowData } } # If !$ExcludeLegacyExchangePolicies # Finally, check for DelayHold If ($Identity.DelayHoldApplied -eq $True) { $Type = "Delayed Hold" $DelayHoldPolicyData = @{ 'Username' = $DisplayName; 'Mail' = $PrimarySmtp; 'Hold Placed By' = "DelayHoldProcess"; 'Policy Guid' = "Not Applicable"; 'Case Name' = "Not Applicable"; 'Case Guid' = "Not Applicable"; 'Hold Type' = $Type; 'Delete Type' = "Not Applicable" } $DelayHoldRowData = [pscustomobject]$DelayHoldPolicyData $Data += $DelayedHoldRowData } } End { If ($OutputFile) { $Data | Export-Csv $OutputFile -Force -Confirm:$False -NoTypeInformation if ($ErrorData) { $ErrorData | Export-Csv $OutputFile+"_Errors.txt" -Force -Confirm:$false -NoTypeInformation } } Else { Write-Output $Data } } |