Private/Common.ps1

<#
.SYNOPSIS
    Common utility functions used across the PurviewDLP module.

.DESCRIPTION
    This file contains shared utility functions that are used by multiple cmdlets
    in the PurviewDLP module, including console output, connection testing,
    module management, and path resolution.

.NOTES
    Internal module file - not exported to users.
    Functions are available to all module cmdlets.
#>


function Write-ColorOutput {
    <#
    .SYNOPSIS
        Writes colored output to the console.
    
    .DESCRIPTION
        Provides consistent colored console output across all module cmdlets.
        Supports different message types with predefined colors.
    
    .PARAMETER Message
        The message to display.
    
    .PARAMETER Type
        The type of message which determines the color.
        Valid values: Info, Success, Warning, Error, Header
        Default: Info
    
    .EXAMPLE
        Write-ColorOutput -Message "Operation completed" -Type Success
    
    .EXAMPLE
        Write-ColorOutput "Processing..." -Type Info
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true, Position = 0)]
        [string]$Message,
        
        [Parameter(Mandatory = $false, Position = 1)]
        [ValidateSet('Info', 'Success', 'Warning', 'Error', 'Header')]
        [string]$Type = 'Info'
    )
    
    $color = switch ($Type) {
        'Success' { 'Green' }
        'Error'   { 'Red' }
        'Warning' { 'Yellow' }
        'Info'    { 'Cyan' }
        'Header'  { 'Magenta' }
    }
    
    Write-Host $Message -ForegroundColor $color
}

function Write-Banner {
    <#
    .SYNOPSIS
        Writes a formatted banner to the console.
    
    .DESCRIPTION
        Creates a visually distinct banner with decorative borders.
        Useful for section headers and important status messages.
    
    .PARAMETER Message
        The message to display in the banner.
    
    .PARAMETER Type
        The type/color of the banner.
        Valid values: Info, Success, Warning, Error
        Default: Info
    
    .PARAMETER Char
        The character to use for the border line.
        Default: "═"
    
    .EXAMPLE
        Write-Banner -Message "DLP Policy Export" -Type Info
    
    .EXAMPLE
        Write-Banner "Export Complete" -Type Success -Char "-"
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true, Position = 0)]
        [string]$Message,
        
        [Parameter(Mandatory = $false)]
        [ValidateSet('Info', 'Success', 'Warning', 'Error')]
        [string]$Type = 'Info',
        
        [Parameter(Mandatory = $false)]
        [string]$Char = '═'
    )
    
    $color = switch ($Type) {
        'Success' { 'Green' }
        'Error'   { 'Red' }
        'Warning' { 'Yellow' }
        'Info'    { 'Cyan' }
    }
    
    $border = $Char * 80
    
    Write-Host ""
    Write-Host $border -ForegroundColor $color
    Write-Host " $Message" -ForegroundColor $color
    Write-Host $border -ForegroundColor $color
    Write-Host ""
}

function Test-SecurityComplianceConnection {
    <#
    .SYNOPSIS
        Tests if there is an active connection to Security & Compliance Center.
    
    .DESCRIPTION
        Attempts to retrieve a DLP policy to verify that a connection to
        Security & Compliance Center is active and functional.
    
    .OUTPUTS
        System.Boolean
        Returns $true if connected, $false otherwise.
    
    .EXAMPLE
        if (Test-SecurityComplianceConnection) {
            Write-Host "Connected"
        }
    
    .EXAMPLE
        $connected = Test-SecurityComplianceConnection
    #>

    [CmdletBinding()]
    [OutputType([bool])]
    param()
    
    try {
        # Try to get a DLP policy to verify connection
        $null = Get-DlpCompliancePolicy -ErrorAction Stop | Select-Object -First 1
        return $true
    }
    catch {
        return $false
    }
}

function Test-ModuleInstalled {
    <#
    .SYNOPSIS
        Tests if a PowerShell module is installed.
    
    .DESCRIPTION
        Checks if a specified module is available in the current PowerShell session.
    
    .PARAMETER ModuleName
        The name of the module to check.
    
    .OUTPUTS
        System.Boolean
        Returns $true if module is installed, $false otherwise.
    
    .EXAMPLE
        if (Test-ModuleInstalled -ModuleName "ExchangeOnlineManagement") {
            Write-Host "Module is installed"
        }
    
    .EXAMPLE
        $hasModule = Test-ModuleInstalled "ExchangeOnlineManagement"
    #>

    [CmdletBinding()]
    [OutputType([bool])]
    param(
        [Parameter(Mandatory = $true, Position = 0)]
        [string]$ModuleName
    )
    
    $module = Get-Module -ListAvailable -Name $ModuleName
    return ($null -ne $module)
}

function Install-RequiredModule {
    <#
    .SYNOPSIS
        Installs a required PowerShell module.
    
    .DESCRIPTION
        Attempts to install a PowerShell module from the PowerShell Gallery
        in the current user scope. Provides feedback on success or failure.
    
    .PARAMETER ModuleName
        The name of the module to install.
    
    .OUTPUTS
        System.Boolean
        Returns $true if installation succeeds, $false otherwise.
    
    .EXAMPLE
        if (Install-RequiredModule -ModuleName "ExchangeOnlineManagement") {
            Write-Host "Installation successful"
        }
    
    .EXAMPLE
        $installed = Install-RequiredModule "ExchangeOnlineManagement"
    #>

    [CmdletBinding()]
    [OutputType([bool])]
    param(
        [Parameter(Mandatory = $true, Position = 0)]
        [string]$ModuleName
    )
    
    Write-ColorOutput "Module '$ModuleName' not found. Attempting to install..." -Type Warning
    
    try {
        Install-Module -Name $ModuleName -Scope CurrentUser -Force -AllowClobber -ErrorAction Stop
        Write-ColorOutput "✓ Module '$ModuleName' installed successfully." -Type Success
        return $true
    }
    catch {
        Write-ColorOutput "✗ Failed to install module '$ModuleName'." -Type Error
        Write-ColorOutput "Error: $($_.Exception.Message)" -Type Error
        return $false
    }
}

function Get-DefaultOutputPath {
    <#
    .SYNOPSIS
        Returns the default output path for generated files.
    
    .DESCRIPTION
        Determines the appropriate output path for generated files.
        If no path is specified, uses the current working directory.
        Creates the directory if it doesn't exist.
    
    .PARAMETER OutputPath
        Optional. The desired output path.
        If not specified, uses current location.
    
    .OUTPUTS
        System.String
        Returns the resolved output path.
    
    .EXAMPLE
        $path = Get-DefaultOutputPath
        # Returns current directory
    
    .EXAMPLE
        $path = Get-DefaultOutputPath -OutputPath "C:\Reports"
        # Returns "C:\Reports", creates if needed
    #>

    [CmdletBinding()]
    [OutputType([string])]
    param(
        [Parameter(Mandatory = $false)]
        [string]$OutputPath
    )
    
    # If no path specified, use current directory
    if ([string]::IsNullOrWhiteSpace($OutputPath)) {
        $OutputPath = (Get-Location).Path
    }
    
    # Resolve to absolute path
    $resolvedPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($OutputPath)
    
    # Create directory if it doesn't exist
    if (-not (Test-Path -Path $resolvedPath)) {
        try {
            New-Item -Path $resolvedPath -ItemType Directory -Force | Out-Null
            Write-Verbose "Created output directory: $resolvedPath"
        }
        catch {
            Write-ColorOutput "Warning: Could not create directory '$resolvedPath'. Using current directory." -Type Warning
            $resolvedPath = (Get-Location).Path
        }
    }
    
    return $resolvedPath
}

function Get-TemplateFile {
    <#
    .SYNOPSIS
        Resolves the path to a template file within the module.
    
    .DESCRIPTION
        Locates template files (HTML, CSV, etc.) stored in the module's
        Templates directory. Returns the full path to the template file.
    
    .PARAMETER TemplateName
        The name of the template file (e.g., "standard-notification.html").
    
    .PARAMETER TemplateType
        The type/subdirectory of template.
        Valid values: PolicyTips, EmailBodies, Reports
    
    .OUTPUTS
        System.String
        Returns the full path to the template file, or $null if not found.
    
    .EXAMPLE
        $template = Get-TemplateFile -TemplateName "standard-notification.html" -TemplateType "EmailBodies"
    
    .EXAMPLE
        $policyTip = Get-TemplateFile "simulation-tip.html" -TemplateType PolicyTips
    #>

    [CmdletBinding()]
    [OutputType([string])]
    param(
        [Parameter(Mandatory = $true, Position = 0)]
        [string]$TemplateName,
        
        [Parameter(Mandatory = $true, Position = 1)]
        [ValidateSet('PolicyTips', 'EmailBodies', 'Reports')]
        [string]$TemplateType
    )
    
    # Get module root directory
    $moduleRoot = Split-Path -Parent $PSScriptRoot
    
    # Build path to template file
    $templatePath = Join-Path -Path $moduleRoot -ChildPath "Templates\$TemplateType\$TemplateName"
    
    if (Test-Path -Path $templatePath) {
        Write-Verbose "Found template: $templatePath"
        return $templatePath
    }
    else {
        Write-ColorOutput "Warning: Template file not found: $templatePath" -Type Warning
        return $null
    }
}

function Invoke-WithErrorHandling {
    <#
    .SYNOPSIS
        Executes a script block with standardized error handling.
    
    .DESCRIPTION
        Wraps script block execution with consistent error handling,
        logging, and error reporting. Useful for maintaining consistent
        error handling patterns across cmdlets.
    
    .PARAMETER ScriptBlock
        The script block to execute.
    
    .PARAMETER ErrorMessage
        The error message prefix to display if execution fails.
    
    .PARAMETER ContinueOnError
        If specified, errors are logged but execution continues.
        Otherwise, errors are thrown.
    
    .OUTPUTS
        System.Object
        Returns the result of the script block, or $null on error.
    
    .EXAMPLE
        Invoke-WithErrorHandling -ScriptBlock {
            Get-DlpCompliancePolicy -Identity "Test"
        } -ErrorMessage "Failed to retrieve policy"
    
    .EXAMPLE
        $result = Invoke-WithErrorHandling -ScriptBlock { Get-Content $file } -ErrorMessage "File read failed" -ContinueOnError
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true, Position = 0)]
        [ScriptBlock]$ScriptBlock,
        
        [Parameter(Mandatory = $true, Position = 1)]
        [string]$ErrorMessage,
        
        [Parameter(Mandatory = $false)]
        [switch]$ContinueOnError
    )
    
    try {
        & $ScriptBlock
    }
    catch {
        $fullErrorMessage = "$ErrorMessage : $($_.Exception.Message)"
        
        if ($ContinueOnError) {
            Write-ColorOutput $fullErrorMessage -Type Warning
            Write-Verbose "Stack Trace: $($_.ScriptStackTrace)"
            return $null
        }
        else {
            Write-ColorOutput $fullErrorMessage -Type Error
            throw $_
        }
    }
}