Public/New-DLPReport.ps1
|
function New-DLPReport { <# .SYNOPSIS Generates comprehensive HTML reports from DLP policy configurations. .DESCRIPTION Creates formatted reports showing all DLP policies and their rules in HTML format. Reports include interactive collapsible sections for easy navigation. Reports include: - Policy overview with statistics - Location scope information with accurate "All" vs specific detection - Cross-tenant migration metadata (portability analysis) - Detailed policy configurations - Rule details with SIT configurations - Notification and incident reporting settings - Advanced Rule conditions (JSON-based rules) Can generate reports directly from live policies or from backup JSON files. .PARAMETER BackupFile Optional. Path to a JSON backup file created by Export-DLPConfiguration. If not provided, reports on currently configured policies (requires connection). .PARAMETER OutputPath Optional. Path where the report file will be saved. Default: [WorkspaceRoot]/Output/dlp-report-[timestamp].html .PARAMETER IncludeDisabledPolicies Include disabled policies in the report. Default: $true .PARAMETER IncludeDisabledRules Include disabled rules in the report. Default: $true .PARAMETER Title Custom title for the report. Default: "Microsoft Purview DLP Policy Report" .OUTPUTS String. Path to the generated report file. .EXAMPLE New-DLPReport Generate report from currently configured policies (requires connection). .EXAMPLE New-DLPReport -BackupFile ".\Output\backup.json" Generate report from a backup JSON file. .EXAMPLE New-DLPReport -OutputPath "C:\Reports\dlp-policies.html" Generate report with custom output path. .EXAMPLE New-DLPReport -BackupFile ".\Output\backup.json" -IncludeDisabledPolicies $false Generate report excluding disabled policies. .EXAMPLE New-DLPReport -Title "Production DLP Configuration" Generate report with custom title. .NOTES Requires: Active connection to Security & Compliance Center (use Connect-PurviewDLP first) unless using -BackupFile parameter Author: PurviewDLP Module Report Features: - Interactive collapsible policy sections - Color-coded policy modes (Enforce, Test, Disable) - Cross-tenant portability indicators - Location scope analysis (All vs Specific) - Rule priority and action summaries - SIT configuration details - Advanced Rule condition parsing - Distribution status tracking .LINK https://github.com/uniQuk/PurviewDLP #> [CmdletBinding()] param( [Parameter(Mandatory = $false, Position = 0)] [string]$BackupFile, [Parameter(Mandatory = $false)] [string]$OutputPath, [Parameter(Mandatory = $false)] [bool]$IncludeDisabledPolicies = $true, [Parameter(Mandatory = $false)] [bool]$IncludeDisabledRules = $true, [Parameter(Mandatory = $false)] [string]$Title = "Microsoft Purview DLP Policy Report" ) begin { Write-Verbose "Starting New-DLPReport" # Resolve output path if (-not $OutputPath) { $timestamp = Get-Date -Format "yyyy-MM-dd_HHmmss" $defaultPath = Get-DefaultOutputPath -OutputPath "." $OutputPath = Join-Path $defaultPath "dlp-report-$timestamp.html" } Write-Verbose "Output path: $OutputPath" } process { try { # Display banner Write-Banner -Message "DLP Policy Report Generation" -Type "Info" # Load policy data if ($BackupFile) { Write-ColorOutput "Loading policies from backup file: $BackupFile" -Type Info if (-not (Test-Path $BackupFile)) { throw "Backup file not found: $BackupFile" } try { $policiesData = Get-Content $BackupFile -Raw -Encoding UTF8 | ConvertFrom-Json } catch { throw "Failed to parse backup file as JSON: $($_.Exception.Message)" } # Ensure array $policies = if ($policiesData -is [array]) { $policiesData } else { @($policiesData) } Write-ColorOutput "✓ Loaded $($policies.Count) policy/policies from backup`n" -Type Success } else { Write-ColorOutput "Retrieving policies from Security & Compliance Center..." -Type Info Test-DLPConnection # Throws if not connected # Get live policies using the Export cmdlet logic Write-ColorOutput "Fetching DLP policies..." -Type Info $dlpPolicies = Get-DlpCompliancePolicy -ErrorAction Stop Write-ColorOutput "Found $($dlpPolicies.Count) policy/policies" -Type Info Write-ColorOutput "Processing policy details...`n" -Type Info # Process each policy (reuse logic from Export-DLPConfiguration) $policies = @() $counter = 0 foreach ($policy in $dlpPolicies) { $counter++ Write-Verbose "[$counter/$($dlpPolicies.Count)] Processing: $($policy.Name)" # Use the same internal function from Export-DLPConfiguration # For simplicity, we'll fetch basic details here $policyInfo = [PSCustomObject]@{ PolicyName = $policy.Name PolicyId = $policy.Guid Mode = $policy.Mode Enabled = $policy.Enabled CreatedBy = $policy.CreatedBy CreatedDateTime = $policy.WhenCreated ModifiedBy = $policy.LastModifiedBy ModifiedDateTime = $policy.WhenChanged Comment = $policy.Comment Workload = $policy.Workload -join ", " # Cross-tenant metadata (simplified) HasSpecificTargeting = $false # Would need full analysis HasLocationExceptions = $false IsCrossTenantPortable = $true # Location info (simplified for report) ExchangeLocation = $policy.ExchangeLocation -join ", " ExchangeLocationIsAll = ($policy.ExchangeLocation -contains "All") SharePointLocation = $policy.SharePointLocation -join ", " SharePointLocationIsAll = ($policy.SharePointLocation -contains "All") OneDriveLocation = $policy.OneDriveLocation -join ", " OneDriveLocationIsAll = ($policy.OneDriveLocation -contains "All") TeamsLocation = $policy.TeamsLocation -join ", " TeamsLocationIsAll = ($policy.TeamsLocation -contains "All") EndpointDlpLocation = $policy.EndpointDlpLocation -join ", " EndpointDlpLocationIsAll = ($policy.EndpointDlpLocation -contains "All") # Exceptions ExchangeLocationException = $policy.ExchangeLocationException -join ", " SharePointLocationException = $policy.SharePointLocationException -join ", " OneDriveLocationException = $policy.OneDriveLocationException -join ", " TeamsLocationException = $policy.TeamsLocationException -join ", " DistributionStatus = $policy.DistributionStatus Priority = $policy.Priority RuleCount = 0 Rules = @() } # Get rules try { $rules = Get-DlpComplianceRule -Policy $policy.Name -ErrorAction Stop $policyInfo.RuleCount = ($rules | Measure-Object).Count foreach ($rule in $rules) { $policyInfo.Rules += [PSCustomObject]@{ RuleName = $rule.Name RuleId = $rule.Guid PolicyName = $policy.Name Disabled = $rule.Disabled Mode = $rule.Mode Priority = $rule.Priority AdvancedRule = $rule.AdvancedRule ContentContainsSensitiveInformation = if ($rule.ContentContainsSensitiveInformation) { ($rule.ContentContainsSensitiveInformation | ConvertTo-Json -Depth 10 -Compress) } else { $null } NotifyUser = $rule.NotifyUser -join "; " NotifyUserType = $rule.NotifyUserType NotifyAllowOverride = $rule.NotifyAllowOverride -join "; " NotifyPolicyTipCustomText = $rule.NotifyPolicyTipCustomText NotifyEmailCustomText = $rule.NotifyEmailCustomText GenerateIncidentReport = $rule.GenerateIncidentReport -join "; " ReportSeverityLevel = $rule.ReportSeverityLevel BlockAccess = $rule.BlockAccess BlockAccessScope = $rule.BlockAccessScope EncryptRMSTemplate = $rule.EncryptRMSTemplate CreatedBy = $rule.CreatedBy CreatedDateTime = $rule.WhenCreated ModifiedBy = $rule.LastModifiedBy ModifiedDateTime = $rule.WhenChanged Comment = $rule.Comment } } } catch { Write-Verbose " Warning: Could not retrieve rules for policy '$($policy.Name)'" } $policies += $policyInfo } Write-ColorOutput "✓ Processed $($policies.Count) policies`n" -Type Success } # Filter policies if needed if (-not $IncludeDisabledPolicies) { $originalCount = $policies.Count $policies = $policies | Where-Object { $_.Enabled -eq $true } Write-ColorOutput "Filtered out $($originalCount - $policies.Count) disabled policy/policies" -Type Info } # Filter rules if needed if (-not $IncludeDisabledRules) { foreach ($policy in $policies) { $originalRuleCount = $policy.Rules.Count $policy.Rules = $policy.Rules | Where-Object { $_.Disabled -ne $true } if ($originalRuleCount -ne $policy.Rules.Count) { Write-Verbose " Filtered $($originalRuleCount - $policy.Rules.Count) disabled rules from policy '$($policy.PolicyName)'" } } } # Generate statistics Write-ColorOutput "Generating report statistics..." -Type Info $stats = Get-PolicyStatisticsInternal -Policies $policies # Build HTML report Write-ColorOutput "Building HTML report..." -Type Info $reportHtml = Build-ReportHTML -Policies $policies -Statistics $stats -ReportTitle $Title # Save report Write-ColorOutput "Saving report to: $OutputPath" -Type Info # Ensure output directory exists $outputDir = Split-Path $OutputPath -Parent if ($outputDir -and -not (Test-Path $outputDir)) { New-Item -ItemType Directory -Path $outputDir -Force | Out-Null } $reportHtml | Out-File -FilePath $OutputPath -Encoding UTF8 -Force # Display summary Write-Banner -Message "Report Summary" -Type "Info" Write-ColorOutput "Report Statistics:" -Type Info Write-Host " Total policies: $($stats.TotalPolicies)" Write-Host " Enabled policies: $($stats.EnabledPolicies)" Write-Host " Disabled policies: $($stats.DisabledPolicies)" Write-Host " Total rules: $($stats.TotalRules)" Write-Host " Enforce mode: $($stats.EnforceMode)" Write-Host " Test mode: $($stats.TestMode)" Write-Host "" Write-ColorOutput "Report saved:" -Type Success Write-Host " Location: $OutputPath" -ForegroundColor Green Write-Host " Size: $([Math]::Round((Get-Item $OutputPath).Length / 1KB, 2)) KB" -ForegroundColor Green Write-Host "" Write-ColorOutput "✓ Report generation completed successfully!" -Type Success Write-Host "" # Return report path return $OutputPath } catch { Write-Host "" Write-ColorOutput "✗ Error generating report:" -Type Error Write-ColorOutput $_.Exception.Message -Type Error Write-Verbose "Stack Trace: $($_.ScriptStackTrace)" Write-Host "" Write-ColorOutput "Troubleshooting tips:" -Type Warning Write-ColorOutput " 1. If not using -BackupFile, ensure you are connected: Connect-PurviewDLP" -Type Info Write-ColorOutput " 2. Verify the backup file path is correct (if using -BackupFile)" -Type Info Write-ColorOutput " 3. Check that the output directory is writable" -Type Info Write-ColorOutput " 4. Ensure the backup file contains valid JSON" -Type Info Write-ColorOutput " 5. Use -Verbose for detailed diagnostic output" -Type Info Write-Host "" throw } } end { Write-Verbose "New-DLPReport completed" } } #region Internal Helper Functions function Get-PolicyStatisticsInternal { <# .SYNOPSIS Internal function to calculate policy statistics for report. #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [array]$Policies ) $stats = @{ TotalPolicies = $Policies.Count EnabledPolicies = ($Policies | Where-Object { $_.Enabled -eq $true }).Count DisabledPolicies = ($Policies | Where-Object { $_.Enabled -eq $false }).Count TotalRules = ($Policies | ForEach-Object { $_.Rules.Count } | Measure-Object -Sum).Sum TestMode = ($Policies | Where-Object { $_.Mode -like "*Test*" }).Count EnforceMode = ($Policies | Where-Object { $_.Mode -eq "Enforce" }).Count PoliciesWithRules = ($Policies | Where-Object { $_.RuleCount -gt 0 }).Count PoliciesWithoutRules = ($Policies | Where-Object { $_.RuleCount -eq 0 }).Count Workloads = @{} } # Count workload distribution foreach ($policy in $Policies) { if ($policy.Workload) { $workloads = $policy.Workload -split ", " | Where-Object { $_ } foreach ($workload in $workloads) { if (-not $stats.Workloads.ContainsKey($workload)) { $stats.Workloads[$workload] = 0 } $stats.Workloads[$workload]++ } } } return $stats } #endregion |