Private/Logging-Functions.ps1

#Requires -Version 5.1
<#
.SYNOPSIS
    Event logging functions for MakeMeAdminCLI.

.DESCRIPTION
    Provides functions to write events to the Windows Application Event Log
    and manage the event log source for MakeMeAdminCLI.

.NOTES
    Author: MakeMeAdminCLI
    Version: 1.0.0

    Event IDs:
    - 1000: Service started
    - 1001: Service stopped
    - 1005: Admin rights granted
    - 1006: Admin rights removed
    - 1010: Warning (general)
    - 1020: Error (general)
    - 1030: Configuration change
    - 1040: Request received
    - 1050: Request denied
#>


# Event ID constants
$script:EventIds = @{
    ServiceStarted = 1000
    ServiceStopped = 1001
    AdminRightsGranted = 1005
    AdminRightsRemoved = 1006
    Warning = 1010
    Error = 1020
    ConfigChange = 1030
    RequestReceived = 1040
    RequestDenied = 1050
}

# Default event source
$script:DefaultEventSource = "MakeMeAdminCLI"
$script:LogName = "Application"

function Initialize-MakeMeAdminEventLog {
    <#
    .SYNOPSIS
        Initializes the Windows Event Log source for MakeMeAdminCLI.

    .DESCRIPTION
        Creates the event log source if it doesn't exist. This operation requires
        administrative privileges.

    .PARAMETER EventSource
        The name of the event source to create. Defaults to "MakeMeAdminCLI".

    .EXAMPLE
        Initialize-MakeMeAdminEventLog

    .EXAMPLE
        Initialize-MakeMeAdminEventLog -EventSource "MyCustomSource"
    #>

    [CmdletBinding()]
    param(
        [string]$EventSource = $script:DefaultEventSource
    )

    try {
        if (-not [System.Diagnostics.EventLog]::SourceExists($EventSource)) {
            [System.Diagnostics.EventLog]::CreateEventSource($EventSource, $script:LogName)
            Write-Verbose "Event log source '$EventSource' created successfully."
        } else {
            Write-Verbose "Event log source '$EventSource' already exists."
        }
        return $true
    }
    catch [System.Security.SecurityException] {
        Write-Warning "Administrative privileges required to create event log source '$EventSource'."
        return $false
    }
    catch {
        Write-Warning "Failed to create event log source '$EventSource': $($_.Exception.Message)"
        return $false
    }
}

function Get-SafeEventSource {
    <#
    .SYNOPSIS
        Gets a valid event source for writing to the Application log.

    .DESCRIPTION
        Attempts to use the preferred event source. If it doesn't exist and cannot
        be created, falls back to a built-in source that's always available.

    .PARAMETER PreferredSource
        The preferred event source name.

    .OUTPUTS
        String containing a valid event source name, or $null if none available.
    #>

    [CmdletBinding()]
    [OutputType([string])]
    param(
        [string]$PreferredSource = $script:DefaultEventSource
    )

    try {
        # Try the preferred source first
        if ($PreferredSource -and [System.Diagnostics.EventLog]::SourceExists($PreferredSource)) {
            return $PreferredSource
        }

        # Fallback to well-known sources
        $fallbackSources = @(
            'Application',
            'Windows PowerShell',
            'EventLog',
            'MsiInstaller',
            'Application Error',
            'Windows Error Reporting'
        )

        foreach ($source in $fallbackSources) {
            try {
                if ([System.Diagnostics.EventLog]::SourceExists($source)) {
                    return $source
                }
            }
            catch {
                continue
            }
        }
    }
    catch {
        Write-Verbose "Error finding event source: $($_.Exception.Message)"
    }

    return $null
}

function Write-MakeMeAdminLog {
    <#
    .SYNOPSIS
        Writes an entry to the Windows Event Log.

    .DESCRIPTION
        Writes an event to the Application event log using the MakeMeAdminCLI
        event source, or a fallback source if necessary.

    .PARAMETER Message
        The message to log.

    .PARAMETER EventId
        The event ID. Defaults to 1000.

    .PARAMETER EntryType
        The type of event: Information, Warning, or Error. Defaults to Information.

    .PARAMETER EventSource
        The event source to use. Defaults to the configured source.

    .EXAMPLE
        Write-MakeMeAdminLog -Message "Admin rights granted to DOMAIN\User" -EventId 1005

    .EXAMPLE
        Write-MakeMeAdminLog -Message "Failed to remove user" -EventId 1020 -EntryType Error
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$Message,

        [int]$EventId = 1000,

        [ValidateSet('Information', 'Warning', 'Error')]
        [string]$EntryType = 'Information',

        [string]$EventSource
    )

    # Get the configured event source if not specified
    if (-not $EventSource) {
        try {
            # Try to get from config, but don't fail if config isn't available yet
            $configPath = Join-Path (Split-Path -Parent $PSScriptRoot) "config.json"
            if (Test-Path $configPath) {
                $config = Get-Content -Path $configPath -Raw | ConvertFrom-Json
                $EventSource = $config.EventLogSource
            }
        }
        catch {
            # Ignore errors, use default
        }
        if (-not $EventSource) {
            $EventSource = $script:DefaultEventSource
        }
    }

    # Get a valid event source
    $validSource = Get-SafeEventSource -PreferredSource $EventSource

    if ($null -eq $validSource) {
        Write-Warning "No valid event log source available. Message not logged to Event Viewer: $Message"
        return
    }

    try {
        Write-EventLog -LogName $script:LogName `
                       -Source $validSource `
                       -EventId $EventId `
                       -EntryType $EntryType `
                       -Message $Message
        Write-Verbose "Event logged: [$EntryType] EventId=$EventId Source=$validSource"
    }
    catch {
        Write-Warning "Failed to write to Event Log: $($_.Exception.Message)"
        Write-Warning "Original message: $Message"
    }
}

function Write-ServiceStartedEvent {
    <#
    .SYNOPSIS
        Logs that the MakeMeAdminCLI service has started.
    #>

    [CmdletBinding()]
    param(
        [string]$AdditionalInfo = ""
    )

    $message = "MakeMeAdminCLI service started."
    if ($AdditionalInfo) {
        $message += " $AdditionalInfo"
    }

    Write-MakeMeAdminLog -Message $message `
                         -EventId $script:EventIds.ServiceStarted `
                         -EntryType Information
}

function Write-ServiceStoppedEvent {
    <#
    .SYNOPSIS
        Logs that the MakeMeAdminCLI service has stopped.
    #>

    [CmdletBinding()]
    param(
        [string]$Reason = ""
    )

    $message = "MakeMeAdminCLI service stopped."
    if ($Reason) {
        $message += " Reason: $Reason"
    }

    Write-MakeMeAdminLog -Message $message `
                         -EventId $script:EventIds.ServiceStopped `
                         -EntryType Information
}

function Write-AdminRightsGrantedEvent {
    <#
    .SYNOPSIS
        Logs that temporary admin rights were granted to a user.

    .PARAMETER Username
        The username that was granted admin rights.

    .PARAMETER DurationMinutes
        The duration for which admin rights were granted.

    .PARAMETER ExpiresAt
        The datetime when the admin rights will expire.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$Username,

        [Parameter(Mandatory)]
        [int]$DurationMinutes,

        [Parameter(Mandatory)]
        [datetime]$ExpiresAt
    )

    $message = "Temporary administrator rights granted to '$Username' for $DurationMinutes minutes. Expires at: $($ExpiresAt.ToString('yyyy-MM-dd HH:mm:ss'))"

    Write-MakeMeAdminLog -Message $message `
                         -EventId $script:EventIds.AdminRightsGranted `
                         -EntryType Information
}

function Write-AdminRightsRemovedEvent {
    <#
    .SYNOPSIS
        Logs that admin rights were removed from a user.

    .PARAMETER Username
        The username that had admin rights removed.

    .PARAMETER Reason
        The reason for removal (Timeout, UserRequest, ServiceStopped, etc.)
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$Username,

        [ValidateSet('Timeout', 'UserRequest', 'ServiceStopped', 'Manual', 'Unknown')]
        [string]$Reason = 'Unknown'
    )

    $message = "Administrator rights removed from '$Username'. Reason: $Reason"

    Write-MakeMeAdminLog -Message $message `
                         -EventId $script:EventIds.AdminRightsRemoved `
                         -EntryType Information
}

function Write-RequestReceivedEvent {
    <#
    .SYNOPSIS
        Logs that a request for admin rights was received.

    .PARAMETER Username
        The username that made the request.

    .PARAMETER Action
        The requested action (add, remove, status).

    .PARAMETER RequestedDuration
        The requested duration in minutes.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$Username,

        [Parameter(Mandatory)]
        [string]$Action,

        [int]$RequestedDuration = 0
    )

    $message = "Request received from '$Username'. Action: $Action"
    if ($RequestedDuration -gt 0) {
        $message += ", Requested duration: $RequestedDuration minutes"
    }

    Write-MakeMeAdminLog -Message $message `
                         -EventId $script:EventIds.RequestReceived `
                         -EntryType Information
}

function Write-RequestDeniedEvent {
    <#
    .SYNOPSIS
        Logs that a request for admin rights was denied.

    .PARAMETER Username
        The username that made the request.

    .PARAMETER Reason
        The reason for denial.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$Username,

        [Parameter(Mandatory)]
        [string]$Reason
    )

    $message = "Request denied for '$Username'. Reason: $Reason"

    Write-MakeMeAdminLog -Message $message `
                         -EventId $script:EventIds.RequestDenied `
                         -EntryType Warning
}

function Write-WarningEvent {
    <#
    .SYNOPSIS
        Logs a warning event.

    .PARAMETER Message
        The warning message to log.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$Message
    )

    Write-MakeMeAdminLog -Message $Message `
                         -EventId $script:EventIds.Warning `
                         -EntryType Warning
}

function Write-ErrorEvent {
    <#
    .SYNOPSIS
        Logs an error event.

    .PARAMETER Message
        The error message to log.

    .PARAMETER Exception
        Optional exception object for additional details.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$Message,

        [System.Exception]$Exception = $null
    )

    $fullMessage = $Message
    if ($Exception) {
        $fullMessage += "`nException: $($Exception.GetType().FullName)`nDetails: $($Exception.Message)"
    }

    Write-MakeMeAdminLog -Message $fullMessage `
                         -EventId $script:EventIds.Error `
                         -EntryType Error
}

# Export module members (when dot-sourced from module)
if ($MyInvocation.MyCommand.ScriptBlock.Module) {
    Export-ModuleMember -Function @(
        'Initialize-MakeMeAdminEventLog',
        'Get-SafeEventSource',
        'Write-MakeMeAdminLog',
        'Write-ServiceStartedEvent',
        'Write-ServiceStoppedEvent',
        'Write-AdminRightsGrantedEvent',
        'Write-AdminRightsRemovedEvent',
        'Write-RequestReceivedEvent',
        'Write-RequestDeniedEvent',
        'Write-WarningEvent',
        'Write-ErrorEvent'
    )

    Export-ModuleMember -Variable 'EventIds'
}