Public/Export-DLPConfiguration.ps1

function Export-DLPConfiguration {
    <#
    .SYNOPSIS
        Exports Microsoft Purview DLP policy and rule configurations.
    
    .DESCRIPTION
        Retrieves DLP policy configurations from Security & Compliance Center including
        all rules and their notification settings. Exports data to JSON and/or CSV formats
        for backup, documentation, and migration purposes.
        
        The export includes:
        - Policy metadata and settings
        - Location scope information (accurate "All" vs specific detection)
        - All rule configurations and conditions
        - Notification settings (policy tips and emails)
        - Cross-tenant migration metadata
        - Distribution status
    
    .PARAMETER PolicyName
        Optional. Specific policy name to export. If not provided, all policies are exported.
    
    .PARAMETER PolicyId
        Optional. Specific policy GUID to export. If not provided, all policies are exported.
    
    .PARAMETER OutputPath
        Optional. Directory path where export files will be saved.
        Default: Current directory
    
    .PARAMETER ExportFormat
        Export format(s) to generate.
        Valid values: JSON, CSV, Both
        Default: Both
    
    .PARAMETER IncludeRules
        Include detailed rule information in the export.
        Default: $true
    
    .OUTPUTS
        None. Exports files to the specified output path.
    
    .EXAMPLE
        Export-DLPConfiguration
        
        Exports all policies and rules to both JSON and CSV in the current directory.
    
    .EXAMPLE
        Export-DLPConfiguration -OutputPath "C:\Backups\DLP"
        
        Exports all policies to the specified directory.
    
    .EXAMPLE
        Export-DLPConfiguration -PolicyName "GDPR Enhanced" -ExportFormat JSON
        
        Exports a specific policy to JSON format only.
    
    .EXAMPLE
        Export-DLPConfiguration -PolicyId "12345678-1234-1234-1234-123456789012"
        
        Exports a specific policy by GUID.
    
    .EXAMPLE
        Export-DLPConfiguration -IncludeRules $false -ExportFormat CSV
        
        Exports only policy summaries (no rules) to CSV.
    
    .NOTES
        Requires: Active connection to Security & Compliance Center (use Connect-PurviewDLP first)
        Author: PurviewDLP Module
        
        Output files are timestamped automatically:
        - dlp-policies-export-YYYY-MM-DD_HHMMSS.json
        - dlp-policies-summary-YYYY-MM-DD_HHMMSS.csv
        - dlp-rules-detail-YYYY-MM-DD_HHMMSS.csv
    
    .LINK
        https://github.com/uniQuk/PurviewDLP
    #>

    
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $false)]
        [string]$PolicyName,
        
        [Parameter(Mandatory = $false)]
        [string]$PolicyId,
        
        [Parameter(Mandatory = $false)]
        [string]$OutputPath,
        
        [Parameter(Mandatory = $false)]
        [ValidateSet("JSON", "CSV", "Both")]
        [string]$ExportFormat = "Both",
        
        [Parameter(Mandatory = $false)]
        [bool]$IncludeRules = $true
    )
    
    begin {
        Write-Verbose "Starting Export-DLPConfiguration"
        
        # Resolve output path (defaults to current directory)
        $resolvedOutputPath = Get-DefaultOutputPath -OutputPath $OutputPath
        Write-Verbose "Resolved output path: $resolvedOutputPath"
    }
    
    process {
        try {
            # Display banner
            Write-Banner -Message "DLP Policy Configuration Export" -Type "Info"
            
            # Verify connection
            Write-ColorOutput "Checking connection to Security & Compliance Center..." -Type Info
            Test-DLPConnection  # Throws if not connected
            Write-ColorOutput "✓ Connected successfully`n" -Type Success
            
            # Display export settings
            Write-ColorOutput "Export Settings:" -Type Info
            Write-ColorOutput " Output Path: $resolvedOutputPath" -Type Info
            Write-ColorOutput " Export Format: $ExportFormat" -Type Info
            Write-ColorOutput " Include Rules: $IncludeRules`n" -Type Info
            
            # Retrieve policies
            Write-ColorOutput "Retrieving DLP policies..." -Type Info
            
            $dlpPolicies = if ($PolicyId) {
                Write-ColorOutput "Filtering by Policy ID: $PolicyId" -Type Info
                Get-DlpCompliancePolicy -Identity $PolicyId -ErrorAction Stop
            }
            elseif ($PolicyName) {
                Write-ColorOutput "Filtering by Policy Name: $PolicyName" -Type Info
                Get-DlpCompliancePolicy | Where-Object { $_.Name -eq $PolicyName }
            }
            else {
                Write-ColorOutput "Retrieving all policies..." -Type Info
                Get-DlpCompliancePolicy -ErrorAction Stop
            }
            
            $policyCount = ($dlpPolicies | Measure-Object).Count
            Write-ColorOutput "Found $policyCount policy/policies`n" -Type Success
            
            if ($policyCount -eq 0) {
                Write-ColorOutput "No policies found matching the criteria." -Type Warning
                return
            }
            
            # Process each policy
            $policies = @()
            $counter = 0
            
            foreach ($policy in $dlpPolicies) {
                $counter++
                Write-ColorOutput "[$counter/$policyCount] Processing: $($policy.Name)" -Type Info
                
                $policyDetails = Get-PolicyDetailsInternal -Policy $policy -IncludeRules $IncludeRules
                $policies += $policyDetails
            }
            
            # Generate timestamp for filenames
            $timestamp = Get-Date -Format "yyyy-MM-dd_HHmmss"
            
            # Export results
            Write-Banner -Message "Exporting Results" -Type "Info"
            
            $exportSuccess = $true
            
            # Export JSON
            if ($ExportFormat -eq "JSON" -or $ExportFormat -eq "Both") {
                $jsonPath = Join-Path $resolvedOutputPath "dlp-policies-export-$timestamp.json"
                Write-ColorOutput "Exporting to JSON..." -Type Info
                
                try {
                    $policies | ConvertTo-Json -Depth 10 | Out-File -FilePath $jsonPath -Encoding UTF8 -Force
                    Write-ColorOutput "✓ JSON export saved: $jsonPath" -Type Success
                }
                catch {
                    Write-ColorOutput "✗ Failed to export JSON: $($_.Exception.Message)" -Type Error
                    $exportSuccess = $false
                }
            }
            
            # Export CSV
            if ($ExportFormat -eq "CSV" -or $ExportFormat -eq "Both") {
                $policyCSVPath = Join-Path $resolvedOutputPath "dlp-policies-summary-$timestamp.csv"
                $ruleCSVPath = Join-Path $resolvedOutputPath "dlp-rules-detail-$timestamp.csv"
                Write-ColorOutput "Exporting to CSV..." -Type Info
                
                try {
                    # Export policy summary
                    $policySummary = $policies | Select-Object PolicyName, PolicyId, Mode, Enabled, RuleCount, `
                        CreatedBy, CreatedDateTime, ModifiedBy, ModifiedDateTime, Comment, Workload, `
                        HasSpecificTargeting, HasLocationExceptions, IsCrossTenantPortable
                    
                    $policySummary | Export-Csv -Path $policyCSVPath -NoTypeInformation -Encoding UTF8 -Force
                    Write-ColorOutput "✓ Policy CSV export saved: $policyCSVPath" -Type Success
                    
                    # Export rules
                    $allRules = $policies | ForEach-Object { $_.Rules }
                    if ($allRules.Count -gt 0) {
                        $formattedRules = $allRules | ForEach-Object { Format-RuleForCSVInternal -Rule $_ }
                        $formattedRules | Export-Csv -Path $ruleCSVPath -NoTypeInformation -Encoding UTF8 -Force
                        Write-ColorOutput "✓ Rule CSV export saved: $ruleCSVPath" -Type Success
                    }
                    else {
                        Write-ColorOutput " No rules to export to CSV" -Type Warning
                    }
                }
                catch {
                    Write-ColorOutput "✗ Failed to export CSV: $($_.Exception.Message)" -Type Error
                    $exportSuccess = $false
                }
            }
            
            # Summary
            Write-Banner -Message "Export Summary" -Type "Info"
            
            Write-ColorOutput "Policies exported: $policyCount" -Type Info
            $totalRules = ($policies | ForEach-Object { $_.RuleCount } | Measure-Object -Sum).Sum
            Write-ColorOutput "Total rules: $totalRules" -Type Info
            Write-ColorOutput "Export location: $resolvedOutputPath" -Type Info
            Write-ColorOutput "Timestamp: $timestamp" -Type Info
            
            if ($exportSuccess) {
                Write-Host ""
                Write-ColorOutput "✓ Export completed successfully!" -Type Success
                Write-Host ""
            }
            else {
                Write-Host ""
                Write-ColorOutput "⚠ Export completed with warnings. Check messages above." -Type Warning
                Write-Host ""
            }
        }
        catch {
            Write-Host ""
            Write-ColorOutput "✗ Error during export:" -Type Error
            Write-ColorOutput $_.Exception.Message -Type Error
            Write-Verbose "Stack Trace: $($_.ScriptStackTrace)"
            
            Write-Host ""
            Write-ColorOutput "Troubleshooting tips:" -Type Warning
            Write-ColorOutput " 1. Ensure you are connected: Connect-PurviewDLP" -Type Info
            Write-ColorOutput " 2. Verify you have permissions to read DLP policies" -Type Info
            Write-ColorOutput " 3. Check that the output path is writable" -Type Info
            Write-Host ""
            
            throw
        }
    }
    
    end {
        Write-Verbose "Export-DLPConfiguration completed"
    }
}

#region Internal Helper Functions

function Get-PolicyDetailsInternal {
    <#
    .SYNOPSIS
        Internal function to extract detailed policy information.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [object]$Policy,
        
        [Parameter(Mandatory = $false)]
        [bool]$IncludeRules = $true
    )
    
    Write-ColorOutput " Processing policy: $($Policy.Name)" -Type Info
    
    # Extract active workloads
    $activeWorkloads = @()
    if ($Policy.ExchangeLocation -and $Policy.ExchangeLocation.Count -gt 0) { $activeWorkloads += "Exchange" }
    if ($Policy.SharePointLocation -and $Policy.SharePointLocation.Count -gt 0) { $activeWorkloads += "SharePoint" }
    if ($Policy.OneDriveLocation -and $Policy.OneDriveLocation.Count -gt 0) { $activeWorkloads += "OneDriveForBusiness" }
    if ($Policy.TeamsLocation -and $Policy.TeamsLocation.Count -gt 0) { $activeWorkloads += "Teams" }
    
    # Analyze location scopes using Private/LocationScope.ps1 functions
    $exchangeScope = Get-LocationScopeInfo -LocationArray $Policy.ExchangeLocation -LocationName "Exchange" `
        -ScopingProperties @($Policy.ExchangeSender, $Policy.ExchangeSenderMemberOf) `
        -AdaptiveScopeProperties @($Policy.ExchangeAdaptiveScopes)
    
    $sharePointScope = Get-LocationScopeInfo -LocationArray $Policy.SharePointLocation -LocationName "SharePoint" `
        -AdaptiveScopeProperties @($Policy.SharePointAdaptiveScopes)
    
    $oneDriveScope = Get-LocationScopeInfo -LocationArray $Policy.OneDriveLocation -LocationName "OneDrive" `
        -ScopingProperties @($Policy.OneDriveSharedBy, $Policy.OneDriveSharedByMemberOf) `
        -AdaptiveScopeProperties @($Policy.OneDriveAdaptiveScopes)
    
    $teamsScope = Get-LocationScopeInfo -LocationArray $Policy.TeamsLocation -LocationName "Teams" `
        -AdaptiveScopeProperties @($Policy.TeamsAdaptiveScopes)
    
    $endpointScope = Get-LocationScopeInfo -LocationArray $Policy.EndpointDlpLocation -LocationName "Endpoint" `
        -AdaptiveScopeProperties @($Policy.EndpointDlpAdaptiveScopes)
    
    # Analyze exceptions
    $exchangeExceptionScope = Get-LocationScopeInfo -LocationArray $Policy.ExchangeLocationException -LocationName "ExchangeException"
    $sharePointExceptionScope = Get-LocationScopeInfo -LocationArray $Policy.SharePointLocationException -LocationName "SharePointException"
    $oneDriveExceptionScope = Get-LocationScopeInfo -LocationArray $Policy.OneDriveLocationException -LocationName "OneDriveException"
    $teamsExceptionScope = Get-LocationScopeInfo -LocationArray $Policy.TeamsLocationException -LocationName "TeamsException"
    
    # Determine cross-tenant portability
    $hasSpecificTargeting = $exchangeScope.IsSpecific -or $sharePointScope.IsSpecific -or 
                            $oneDriveScope.IsSpecific -or $teamsScope.IsSpecific -or $endpointScope.IsSpecific
    
    $hasExceptions = $exchangeExceptionScope.IsConfigured -or $sharePointExceptionScope.IsConfigured -or
                     $oneDriveExceptionScope.IsConfigured -or $teamsExceptionScope.IsConfigured
    
    # Build policy object
    $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 = $activeWorkloads -join ", "
        
        # Cross-tenant migration metadata
        HasSpecificTargeting = $hasSpecificTargeting
        HasLocationExceptions = $hasExceptions
        IsCrossTenantPortable = -not ($hasSpecificTargeting -or $hasExceptions)
        
        # Location inclusions
        ExchangeLocation = $exchangeScope.Locations
        ExchangeLocationIsAll = $exchangeScope.IsAll
        SharePointLocation = $sharePointScope.Locations
        SharePointLocationIsAll = $sharePointScope.IsAll
        OneDriveLocation = $oneDriveScope.Locations
        OneDriveLocationIsAll = $oneDriveScope.IsAll
        TeamsLocation = $teamsScope.Locations
        TeamsLocationIsAll = $teamsScope.IsAll
        EndpointDlpLocation = $endpointScope.Locations
        EndpointDlpLocationIsAll = $endpointScope.IsAll
        
        # Location exceptions
        ExchangeLocationException = $exchangeExceptionScope.Locations
        SharePointLocationException = $sharePointExceptionScope.Locations
        OneDriveLocationException = $oneDriveExceptionScope.Locations
        TeamsLocationException = $teamsExceptionScope.Locations
        
        # Distribution status
        DistributionStatus = $Policy.DistributionStatus
        Priority = $Policy.Priority
        Type = $Policy.Type
        
        RuleCount = 0
        Rules = @()
    }
    
    # Get rules
    if ($IncludeRules) {
        try {
            $rules = Get-DlpComplianceRule -Policy $Policy.Name -ErrorAction Stop
            $policyInfo.RuleCount = ($rules | Measure-Object).Count
            
            Write-ColorOutput " Found $($policyInfo.RuleCount) rule(s)" -Type Info
            
            foreach ($rule in $rules) {
                Write-Verbose " Processing rule: $($rule.Name)"
                $policyInfo.Rules += Get-RuleDetailsInternal -Rule $rule -PolicyName $Policy.Name
            }
        }
        catch {
            Write-ColorOutput " Warning: Could not retrieve rules for policy '$($Policy.Name)': $($_.Exception.Message)" -Type Warning
        }
    }
    
    return $policyInfo
}

function Get-RuleDetailsInternal {
    <#
    .SYNOPSIS
        Internal function to extract detailed rule information.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [object]$Rule,
        
        [Parameter(Mandatory = $true)]
        [string]$PolicyName
    )
    
    return [PSCustomObject]@{
        RuleName = $Rule.Name
        RuleId = $Rule.Guid
        PolicyName = $PolicyName
        Disabled = $Rule.Disabled
        Mode = $Rule.Mode
        Priority = $Rule.Priority
        
        # Advanced Rule (JSON-based conditions)
        AdvancedRule = $Rule.AdvancedRule
        
        # SIT configuration
        ContentContainsSensitiveInformation = if ($Rule.ContentContainsSensitiveInformation) { 
            ($Rule.ContentContainsSensitiveInformation | ConvertTo-Json -Depth 10 -Compress)
        } else { $null }
        
        # Notification settings
        NotifyUser = $Rule.NotifyUser -join "; "
        NotifyUserType = $Rule.NotifyUserType
        NotifyAllowOverride = $Rule.NotifyAllowOverride -join "; "
        NotifyPolicyTipCustomText = $Rule.NotifyPolicyTipCustomText
        NotifyEmailCustomText = $Rule.NotifyEmailCustomText
        
        # Incident reporting
        GenerateIncidentReport = $Rule.GenerateIncidentReport -join "; "
        ReportSeverityLevel = $Rule.ReportSeverityLevel
        
        # Actions
        BlockAccess = $Rule.BlockAccess
        BlockAccessScope = $Rule.BlockAccessScope
        EncryptRMSTemplate = $Rule.EncryptRMSTemplate
        
        # Metadata
        CreatedBy = $Rule.CreatedBy
        CreatedDateTime = $Rule.WhenCreated
        ModifiedBy = $Rule.LastModifiedBy
        ModifiedDateTime = $Rule.WhenChanged
        Comment = $Rule.Comment
    }
}

function Format-RuleForCSVInternal {
    <#
    .SYNOPSIS
        Formats a rule object for CSV export readability.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [object]$Rule
    )
    
    $formatted = $Rule.PSObject.Copy()
    
    # Truncate long HTML content for CSV readability
    if ($Rule.NotifyEmailCustomText -and $Rule.NotifyEmailCustomText.Length -gt 100) {
        $formatted.NotifyEmailCustomText = $Rule.NotifyEmailCustomText.Substring(0, 97) + "..."
    }
    
    if ($Rule.NotifyPolicyTipCustomText -and $Rule.NotifyPolicyTipCustomText.Length -gt 100) {
        $formatted.NotifyPolicyTipCustomText = $Rule.NotifyPolicyTipCustomText.Substring(0, 97) + "..."
    }
    
    return $formatted
}

#endregion