Private/Initialize-EventLogging.ps1

function Initialize-EventLogging {

    <#
        .SYNOPSIS
            Initializes event logging by creating and configuring a new event log.

        .DESCRIPTION
            This function checks if an event log exists and creates it if it doesn't.
            It also configures the log with a maximum size, retention policy, and error-handling retry logic.
            The function supports verbose output, what-if, and confirmation prompts.

        .PARAMETER MaximumKilobytes
            Specifies the maximum size of the event log in kilobytes. Default is 16384 KB (16 MB).

        .PARAMETER RetentionDays
            Specifies the number of days to retain event log entries. Default is 30 days.

        .PARAMETER LogName
            The name of the Windows Event Log to create or configure.
            Default is the value from $Variables.LogConfig.LogName or "EguibarIT-Events" if not set.

        .PARAMETER Source
            The source identifier for the event log entries.
            Default is the value from $Variables.LogConfig.Source or "EguibarIT-PowerShellModule" if not set.

        .EXAMPLE
            Initialize-EventLogging -MaximumKilobytes 8192 -RetentionDays 15 -Verbose

            Initializes event logging with a log size of 8192 KB and retention period of 15 days, with verbose output enabled.

        .EXAMPLE
            Initialize-EventLogging -LogName "MyCustomLog" -Source "MyApp" -WhatIf

            Shows what would happen if a custom event log were initialized, without making any changes.

        .INPUTS
            [int] - MaximumKilobytes
            [int] - RetentionDays
            [string] - LogName
            [string] - Source

        .OUTPUTS
            [bool] - Returns $true if initialization was successful, $false otherwise.

        .NOTES
            Used Functions:
                Name ║ Module/Namespace
                ════════════════════════════════════════╬══════════════════════════════
                Get-FunctionDisplay ║ EguibarIT
                Get-Date ║ Microsoft.PowerShell.Utility
                New-EventLog ║ Microsoft.PowerShell.Management
                Limit-EventLog ║ Microsoft.PowerShell.Management
                Write-EventLog ║ Microsoft.PowerShell.Management
                Write-Error ║ Microsoft.PowerShell.Utility
                Write-Verbose ║ Microsoft.PowerShell.Utility
                Write-Warning ║ Microsoft.PowerShell.Utility
                Start-Sleep ║ Microsoft.PowerShell.Utility

        .NOTES
            Version: 1.2
            DateModified: 22/May/2025
            LastModifiedBy: Vicente Rodriguez Eguibar
                            vicente@eguibar.com
                            Eguibar IT
                            http://www.eguibarit.com

        .LINK
            https://github.com/vreguibar/EguibarIT/blob/main/Private/Initialize-EventLogging.ps1

        .COMPONENT
            Event Management

        .ROLE
            System Administration

        .FUNCTIONALITY
            Event Logging, System Configuration
    #>


    [CmdletBinding(
        SupportsShouldProcess = $true,
        ConfirmImpact = 'Low'
    )]
    [OutputType([bool])]

    param (
        [Parameter(Mandatory = $false,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $true,
            HelpMessage = 'Maximum size of the Event file.',
            Position = 0)]
        [ValidateRange(64, 1048576)]  # Minimum of 64 KB, max of 1 GB
        [PSDefaultValue(
            Help = 'Default Value is "16384"',
            value = 16384
        )]
        [int]
        $MaximumKilobytes, # default to 16 MB

        [Parameter(Mandatory = $false,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $true,
            HelpMessage = 'Maximum day to retain events.',
            Position = 1)]
        [ValidateRange(1, 365)]  # Minimum of 1 day, max of 1 year
        [PSDefaultValue(
            Help = 'Default Value is "30"',
            Value = 30
        )]
        [int]
        $RetentionDays, # default to 30 days

        [Parameter(Mandatory = $false,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $false,
            HelpMessage = 'Name of the Windows Event Log.',
            Position = 2)]
        [ValidateNotNullOrEmpty()]
        [string]
        $LogName,

        [Parameter(Mandatory = $false,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            ValueFromRemainingArguments = $false,
            HelpMessage = 'Source identifier for the event log entries.',
            Position = 3)]
        [ValidateNotNullOrEmpty()]
        [string]
        $Source
    )

    Begin {
        Set-StrictMode -Version Latest

        # Show ONLY if not Initialized.
        If (-not $Variables.EventLogInitialized) {
            $txt = ($Variables.Header -f
                (Get-Date).ToString('dd/MMM/yyyy'),
                $MyInvocation.Mycommand,
                (Get-FunctionDisplay -Hashtable $PsBoundParameters -Verbose:$False)
            )
            Write-Verbose -Message $txt
        } #end If

        ##############################
        # Module imports

        ##############################
        # Variables Definition

        # parameters variable for splatting CMDlets
        [hashtable]$Splat = [hashtable]::New([StringComparer]::OrdinalIgnoreCase)

        # Retry logic in case of failure
        [int]$RetryCount = 0

        # Check for administrative privileges
        [bool]$IsAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] 'Administrator')

        if (-not $IsAdmin) {
            Write-Warning -Message 'This function requires administrative privileges to create event logs.'
        } #end If


        # Ensure LogConfig exists in $Variables
        if (-not $Variables.ContainsKey('LogConfig')) {
            $Variables['LogConfig'] = @{
                LogName = 'EguibarIT-Events'
                Source  = 'EguibarIT-PowerShellModule'
            }
        } #end If

        # Set default values for LogName and Source if not provided
        if (-not $PSBoundParameters.ContainsKey('LogName')) {
            $LogName = $Variables.LogConfig.LogName
        } else {
            # Update the global variable with the new value
            $Variables.LogConfig.LogName = $LogName
        } #end If-Else

        if (-not $PSBoundParameters.ContainsKey('Source')) {
            $Source = $Variables.LogConfig.Source
        } else {
            # Update the global variable with the new value
            $Variables.LogConfig.Source = $Source
        } #end If-Else

        # Update maximum size and retention days
        $Variables.LogConfig.MaximumKilobytes = $MaximumKilobytes
        $Variables.LogConfig.RetentionDays = $RetentionDays

    } #end Begin

    Process {
        # Check if the event log is already initialized
        if (-not $Variables.EventLogInitialized) {

            try {
                # Check if the event source exists, and if not, create it
                if (-not [System.Diagnostics.EventLog]::SourceExists($Source)) {

                    if ($PSCmdlet.ShouldProcess("Event log source '$Source'", 'Create')) {

                        Write-Verbose -Message ('Creating event log source {0} for log {1}' -f $Source, $LogName)

                        $Splat = @{
                            LogName = $LogName
                            Source  = $Source
                        }

                        # Add Verbose if specified
                        if ($PSBoundParameters['Verbose']) {
                            $Splat['Verbose'] = $true
                        } #end If

                        New-EventLog @Splat

                        Write-Verbose -Message ('Log source {0} created in log {1}.' -f $Source, $LogName)

                        $Splat = @{
                            LogName        = $LogName
                            MaximumSize    = $MaximumKilobytes * 1KB  # Convert to bytes
                            OverflowAction = 'OverwriteOlder'
                            RetentionDays  = $RetentionDays
                        }

                        # Add Verbose if specified
                        if ($PSBoundParameters['Verbose']) {
                            $Splat['Verbose'] = $true
                        } #end If

                        Limit-EventLog @Splat

                        Write-Verbose -Message (
                            'Log {0} was configured with {1} KB size and {2} days retention.' -f
                            $LogName,
                            $MaximumKilobytes,
                            $RetentionDays
                        )
                    } #end If ShouldProcess

                } else {
                    Write-Verbose -Message ('Event log source {0} already exists.' -f $Source)

                    # Update the log configuration even if source exists
                    if ($PSCmdlet.ShouldProcess("Event log '$LogName'", 'Update configuration')) {

                        $Splat = @{
                            LogName        = $LogName
                            MaximumSize    = $MaximumKilobytes * 1KB  # Convert to bytes
                            OverflowAction = 'OverwriteOlder'
                            RetentionDays  = $RetentionDays
                        }

                        # Add Verbose if specified
                        if ($PSBoundParameters['Verbose']) {
                            $Splat['Verbose'] = $true
                        } #end If

                        Limit-EventLog @Splat

                        Write-Verbose -Message (
                            'Updated log {0} configuration: {1} KB size, {2} days retention.' -f
                            $LogName,
                            $MaximumKilobytes,
                            $RetentionDays
                        )
                    } #end If ShouldProcess
                } #end If-Else SourceExists

                # Set Global Variable
                $Variables.EventLogInitialized = $true
                Write-Verbose -Message 'Event logging initialized successfully.'

                # Write a test entry to verify everything is working
                if ($PSCmdlet.ShouldProcess('Test event log entry', 'Write to Event Log')) {

                    $Splat = @{
                        LogName   = $LogName
                        Source    = $Source
                        EventId   = 1000  # Information event ID
                        EntryType = 'Information'
                        Message   = 'Event logging initialized successfully by EguibarIT module.'
                    }
                    Write-EventLog @Splat

                } #end If ShouldProcess

            } catch [System.Security.SecurityException] {
                $RetryCount++

                Write-Warning -Message (
                    'Security exception encountered. Administrative privileges may be required. Retrying... ({0}/3)' -f
                    $RetryCount
                )
                Start-Sleep -Seconds 2

            } catch [System.InvalidOperationException] {
                $RetryCount++

                Write-Warning -Message ('Invalid operation when initializing event log. Retrying... ({0}/3)' -f $RetryCount)
                Start-Sleep -Seconds 2

            } catch {
                $retryCount++

                Write-Warning -Message (
                    'Failed to initialize event logging: {0}. Retrying... ({1}/3)' -f
                    $_.Exception.Message,
                    $RetryCount
                )

                Start-Sleep -Seconds 2
            } #end Try-Catch

        } #end if

        if (-not $Variables.EventLogInitialized) {
            throw 'Failed to initialize event log after 3 attempts.'
            return $false
        } #end If

        return $true

    } #end Process

    End {
        # Show ONLY if not Initialized.
        If (-not $Variables.EventLogInitialized) {
            $txt = ($Variables.Footer -f $MyInvocation.InvocationName,
                'initializing Event Logging.'
            )
            Write-Verbose -Message $txt
        } #end If
    } #end End
} #end Function Initialize-EventLogging