Security/Get-DlpPolicyReport.ps1

<#
.SYNOPSIS
    Reports DLP policies, DLP rules, and sensitivity labels from Microsoft Purview.
.DESCRIPTION
    Retrieves DLP compliance policies, their associated rules, and sensitivity labels
    from the Microsoft Purview compliance portal. Produces a unified report showing
    each item type, name, enabled/priority state, and relevant configuration details.
 
    Useful for compliance assessments, data protection reviews, and verifying that
    DLP and information protection controls are properly configured for the tenant.
 
    Handles tenants where Purview is not available or not licensed by skipping
    unavailable cmdlets gracefully.
 
    Requires ExchangeOnlineManagement module and an active Purview/Compliance
    connection (Connect-IPPSSession).
.PARAMETER OutputPath
    Optional path to export results as CSV. If not specified, results are returned
    to the pipeline.
.EXAMPLE
    PS> . .\Common\Connect-Service.ps1
    PS> Connect-Service -Service Purview
    PS> .\Security\Get-DlpPolicyReport.ps1
 
    Displays all DLP policies, rules, and sensitivity labels in the tenant.
.EXAMPLE
    PS> .\Security\Get-DlpPolicyReport.ps1 -OutputPath '.\dlp-report.csv'
 
    Exports DLP policies, rules, and sensitivity labels to CSV.
.EXAMPLE
    PS> .\Security\Get-DlpPolicyReport.ps1 -Verbose
 
    Displays the report with verbose processing details, including skip messages
    for unavailable features.
#>

[CmdletBinding()]
param(
    [Parameter()]
    [string]$OutputPath
)

$ErrorActionPreference = 'Stop'

$results = [System.Collections.Generic.List[PSCustomObject]]::new()

# --- DLP Compliance Policies ---
Write-Verbose "Retrieving DLP compliance policies..."
try {
    $dlpPolicies = Get-DlpCompliancePolicy -ErrorAction Stop

    if (-not $dlpPolicies -or @($dlpPolicies).Count -eq 0) {
        Write-Verbose "No DLP compliance policies found."
    }
    else {
        foreach ($policy in @($dlpPolicies)) {
            # Build a locations summary from the workload-specific location properties
            $locations = [System.Collections.Generic.List[string]]::new()

            if ($policy.ExchangeLocation -and @($policy.ExchangeLocation).Count -gt 0) {
                $locations.Add("Exchange")
            }
            if ($policy.SharePointLocation -and @($policy.SharePointLocation).Count -gt 0) {
                $locations.Add("SharePoint")
            }
            if ($policy.OneDriveLocation -and @($policy.OneDriveLocation).Count -gt 0) {
                $locations.Add("OneDrive")
            }
            if ($policy.TeamsLocation -and @($policy.TeamsLocation).Count -gt 0) {
                $locations.Add("Teams")
            }
            if ($policy.EndpointDlpLocation -and @($policy.EndpointDlpLocation).Count -gt 0) {
                $locations.Add("Endpoints")
            }

            $locationSummary = if ($locations.Count -gt 0) {
                $locations -join ', '
            }
            else {
                'None'
            }

            $mode = if ($policy.Mode) { $policy.Mode } else { 'N/A' }
            $enabled = if ($null -ne $policy.Enabled) { $policy.Enabled } else { $mode -ne 'Disable' }

            $details = "Mode=$mode; Locations=$locationSummary"

            $results.Add([PSCustomObject]@{
                ItemType = 'DlpPolicy'
                Name     = $policy.Name
                Enabled  = $enabled
                Priority = if ($null -ne $policy.Priority) { $policy.Priority } else { 'N/A' }
                Details  = $details
            })
        }
        Write-Verbose "Found $(@($dlpPolicies).Count) DLP compliance policies."
    }
}
catch {
    if ($_.Exception.Message -match 'is not recognized') {
        Write-Warning "Get-DlpCompliancePolicy not available. Skipping DLP policies."
    }
    else {
        Write-Warning "Failed to retrieve DLP compliance policies: $_"
    }
}

# --- DLP Compliance Rules ---
Write-Verbose "Retrieving DLP compliance rules..."
try {
    $dlpRules = Get-DlpComplianceRule -ErrorAction Stop

    if (-not $dlpRules -or @($dlpRules).Count -eq 0) {
        Write-Verbose "No DLP compliance rules found."
    }
    else {
        foreach ($rule in @($dlpRules)) {
            # Build a conditions summary
            $conditionParts = [System.Collections.Generic.List[string]]::new()

            if ($rule.ContentContainsSensitiveInformation) {
                $sensitiveTypes = @($rule.ContentContainsSensitiveInformation)
                $typeNames = foreach ($st in $sensitiveTypes) {
                    if ($st.Name) { $st.Name } elseif ($st.name) { $st.name } else { 'SensitiveInfo' }
                }
                $conditionParts.Add("SensitiveInfo=($($typeNames -join ', '))")
            }

            if ($rule.ParentPolicyName) {
                $conditionParts.Add("Policy=$($rule.ParentPolicyName)")
            }

            if ($rule.BlockAccess) {
                $conditionParts.Add("BlockAccess=$($rule.BlockAccess)")
            }

            if ($rule.NotifyUser) {
                $notifyUsers = if ($rule.NotifyUser -is [System.Collections.IEnumerable] -and $rule.NotifyUser -isnot [string]) {
                    ($rule.NotifyUser | ForEach-Object { $_.ToString() }) -join ', '
                }
                else {
                    [string]$rule.NotifyUser
                }
                $conditionParts.Add("NotifyUser=$notifyUsers")
            }

            $conditionSummary = if ($conditionParts.Count -gt 0) {
                $conditionParts -join '; '
            }
            else {
                'No conditions specified'
            }

            $enabled = if ($null -ne $rule.Disabled) { -not $rule.Disabled } else { $true }

            $results.Add([PSCustomObject]@{
                ItemType = 'DlpRule'
                Name     = $rule.Name
                Enabled  = $enabled
                Priority = if ($null -ne $rule.Priority) { $rule.Priority } else { 'N/A' }
                Details  = $conditionSummary
            })
        }
        Write-Verbose "Found $(@($dlpRules).Count) DLP compliance rules."
    }
}
catch {
    if ($_.Exception.Message -match 'is not recognized') {
        Write-Warning "Get-DlpComplianceRule not available. Skipping DLP rules."
    }
    else {
        Write-Warning "Failed to retrieve DLP compliance rules: $_"
    }
}

# --- Sensitivity Labels ---
Write-Verbose "Retrieving sensitivity labels..."
try {
    $labels = Get-Label -ErrorAction Stop

    if (-not $labels -or @($labels).Count -eq 0) {
        Write-Verbose "No sensitivity labels found."
    }
    else {
        foreach ($label in @($labels)) {
            $tooltip = if ($label.Tooltip) { $label.Tooltip } else { 'No description' }
            $parentLabel = if ($label.ParentId) { "ParentId=$($label.ParentId); " } else { '' }
            $contentType = if ($label.ContentType) { "ContentType=$($label.ContentType); " } else { '' }

            $details = "${parentLabel}${contentType}Tooltip=$tooltip"

            $enabled = if ($null -ne $label.Disabled) { -not $label.Disabled } else { $true }

            $results.Add([PSCustomObject]@{
                ItemType = 'SensitivityLabel'
                Name     = $label.DisplayName
                Enabled  = $enabled
                Priority = if ($null -ne $label.Priority) { $label.Priority } else { 'N/A' }
                Details  = $details
            })
        }
        Write-Verbose "Found $(@($labels).Count) sensitivity labels."
    }
}
catch {
    if ($_.Exception.Message -match 'is not recognized') {
        Write-Warning "Get-Label not available. Skipping sensitivity labels."
    }
    else {
        Write-Warning "Failed to retrieve sensitivity labels: $_"
    }
}

# Output results
if ($results.Count -eq 0) {
    Write-Warning "No DLP policies, rules, or sensitivity labels found. Verify that the tenant has Purview features configured."
    return
}

Write-Verbose "Total items found: $($results.Count)"

if ($OutputPath) {
    $results | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8
    Write-Output "Exported $($results.Count) DLP/label items to $OutputPath"
}
else {
    Write-Output $results
}