EnhancedLoggingAO.psm1
#Region '.\Private\AppendCSVLog.ps1' -1 #Unique Tracking ID: 737397f0-c74e-4087-9b99-279b520b7448, Timestamp: 2024-03-20 12:25:26 function AppendCSVLog { param ( [string]$Message, [string]$CSVFilePath_1001 ) $csvData = [PSCustomObject]@{ TimeStamp = (Get-Date -Format 'yyyy-MM-dd HH:mm:ss') ComputerName = $env:COMPUTERNAME Message = $Message } $csvData | Export-Csv -Path $CSVFilePath_1001 -Append -NoTypeInformation -Force } #EndRegion '.\Private\AppendCSVLog.ps1' 17 #Region '.\Private\CreateEventSourceAndLog.ps1' -1 #Unique Tracking ID: 4362313d-3c19-4d0c-933d-99438a6da297, Timestamp: 2024-03-20 12:25:26 function CreateEventSourceAndLog { param ( [string]$LogName, [string]$EventSource ) # Validate parameters if (-not $LogName) { Write-Warning "LogName is required." return } if (-not $EventSource) { Write-Warning "Source is required." return } # Function to create event log and source function CreateEventLogSource($logName, $EventSource) { try { if ($PSVersionTable.PSVersion.Major -lt 6) { New-EventLog -LogName $logName -Source $EventSource } else { [System.Diagnostics.EventLog]::CreateEventSource($EventSource, $logName) } Write-Host "Event source '$EventSource' created in log '$logName'" -ForegroundColor Green } catch { Write-Warning "Error creating the event log. Make sure you run PowerShell as an Administrator." } } # Check if the event log exists if (-not (Get-WinEvent -ListLog $LogName -ErrorAction SilentlyContinue)) { # CreateEventLogSource $LogName $EventSource } # Check if the event source exists elseif (-not ([System.Diagnostics.EventLog]::SourceExists($EventSource))) { # Unregister the source if it's registered with a different log $existingLogName = (Get-WinEvent -ListLog * | Where-Object { $_.LogName -contains $EventSource }).LogName if ($existingLogName -ne $LogName) { Remove-EventLog -Source $EventSource -ErrorAction SilentlyContinue } # CreateEventLogSource $LogName $EventSource } else { Write-Host "Event source '$EventSource' already exists in log '$LogName'" -ForegroundColor Yellow } } # $LogName = (Get-Date -Format "HHmmss") + "_$LoggingDeploymentName" # $EventSource = (Get-Date -Format "HHmmss") + "_$LoggingDeploymentName" # Call the Create-EventSourceAndLog function # CreateEventSourceAndLog -LogName $LogName -EventSource $EventSource # Call the Write-CustomEventLog function with custom parameters and level # Write-CustomEventLog -LogName $LogName -EventSource $EventSource -EventMessage "Outlook Signature Restore completed with warnings." -EventID 1001 -Level 'WARNING' #EndRegion '.\Private\CreateEventSourceAndLog.ps1' 62 #Region '.\Private\Export-EventLog.ps1' -1 #Unique Tracking ID: c00ecaca-dd4b-4c7c-b80e-566b2f627e32, Timestamp: 2024-03-20 12:25:26 function Export-EventLog { param ( [Parameter(Mandatory = $true)] [string]$LogName, [Parameter(Mandatory = $true)] [string]$ExportPath ) try { wevtutil epl $LogName $ExportPath if (Test-Path $ExportPath) { Write-EnhancedLog -Message "Event log '$LogName' exported to '$ExportPath'" -Level "INFO" -ForegroundColor ([ConsoleColor]::Green) } else { Write-EnhancedLog -Message "Event log '$LogName' not exported: File does not exist at '$ExportPath'" -Level "WARNING" -ForegroundColor ([ConsoleColor]::Yellow) } } catch { Write-EnhancedLog -Message "Error exporting event log '$LogName': $($_.Exception.Message)" -Level "ERROR" -ForegroundColor ([ConsoleColor]::Red) } } # # Example usage # $LogName = '$LoggingDeploymentNameLog' # # $ExportPath = 'Path\to\your\exported\eventlog.evtx' # $ExportPath = "C:\code\$LoggingDeploymentName\exports\Logs\$logname.evtx" # Export-EventLog -LogName $LogName -ExportPath $ExportPath #EndRegion '.\Private\Export-EventLog.ps1' 30 #Region '.\Private\Initialize-CSVDirectory.ps1' -1 function Initialize-CSVDirectory { param ( [string]$deploymentName, [string]$computerName ) $isWindowsOS = $false if ($PSVersionTable.PSVersion.Major -ge 6) { $isWindowsOS = $isWindowsOS -or ($PSVersionTable.Platform -eq 'Win32NT') } else { $isWindowsOS = $isWindowsOS -or ($env:OS -eq 'Windows_NT') } $baseScriptPath = if ($isWindowsOS) { "C:\code" } else { "/home/code" } $scriptPath_1001 = Join-Path -Path $baseScriptPath -ChildPath $deploymentName $CSVDir_1001 = Join-Path -Path $scriptPath_1001 -ChildPath "exports/CSV" $CSVFilePath_1001 = Join-Path -Path $CSVDir_1001 -ChildPath "$computerName" try { if (-not (Test-Path $CSVFilePath_1001)) { Write-Host "Did not find CSV directory at $CSVFilePath_1001" -ForegroundColor Yellow Write-Host "Creating CSV directory at $CSVFilePath_1001" -ForegroundColor Yellow New-Item -ItemType Directory -Path $CSVFilePath_1001 -Force -ErrorAction Stop | Out-Null Write-Host "Created CSV directory at $CSVFilePath_1001" -ForegroundColor Green } return @{ CSVFilePath = $CSVFilePath_1001 } } catch { Write-Host "An error occurred while initializing CSV directory: $_" -ForegroundColor Red } } # # Example usage of Initialize-CSVDirectory # try { # $csvInitResult = Initialize-CSVDirectory -deploymentName "$LoggingDeploymentName" -computerName $env:COMPUTERNAME # Write-Host "CSV initialization successful. CSV directory path: $($csvInitResult.CSVFilePath)" -ForegroundColor Green # } catch { # Write-Host "CSV initialization failed: $_" -ForegroundColor Red # } #EndRegion '.\Private\Initialize-CSVDirectory.ps1' 42 #Region '.\Private\Write-EventLogMessage.ps1' -1 #Unique Tracking ID: 132a90f9-6ba2-49cd-878b-279deadb8e22, Timestamp: 2024-03-20 12:25:26 function Write-EventLogMessage { param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$Message, [string]$LogName = "$LoggingDeploymentName", [string]$EventSource, [int]$EventID = 1000 # Default event ID ) $ErrorActionPreference = 'SilentlyContinue' $hadError = $false try { if (-not $EventSource) { throw "EventSource is required." } if ($PSVersionTable.PSVersion.Major -lt 6) { # PowerShell version is less than 6, use Write-EventLog Write-EventLog -LogName $logName -Source $EventSource -EntryType Information -EventId $EventID -Message $Message } else { # PowerShell version is 6 or greater, use System.Diagnostics.EventLog $eventLog = New-Object System.Diagnostics.EventLog($logName) $eventLog.Source = $EventSource $eventLog.WriteEntry($Message, [System.Diagnostics.EventLogEntryType]::Information, $EventID) } # Write-Host "Event log entry created: $Message" } catch { Write-Host "Error creating event log entry: $_" $hadError = $true } if (-not $hadError) { # Write-Host "Event log message writing completed successfully." } } #EndRegion '.\Private\Write-EventLogMessage.ps1' 46 #Region '.\Public\Export-Data.ps1' -1 function Export-Data { <# .SYNOPSIS Exports data to various formats including CSV, JSON, XML, HTML, PlainText, Excel, PDF, Markdown, and YAML. .DESCRIPTION The Export-Data function exports provided data to multiple file formats based on switches provided. It supports CSV, JSON, XML, GridView (for display only), HTML, PlainText, Excel, PDF, Markdown, and YAML formats. This function is designed to work with any PSObject. .PARAMETER Data The data to be exported. This parameter accepts input of type PSObject. .PARAMETER BaseOutputPath The base path for output files without file extension. This path is used to generate filenames for each export format. .PARAMETER IncludeCSV Switch to include CSV format in the export. .PARAMETER IncludeJSON Switch to include JSON format in the export. .PARAMETER IncludeXML Switch to include XML format in the export. .PARAMETER IncludeGridView Switch to display the data in a GridView. .PARAMETER IncludeHTML Switch to include HTML format in the export. .PARAMETER IncludePlainText Switch to include PlainText format in the export. .PARAMETER IncludePDF Switch to include PDF format in the export. Requires intermediate HTML to PDF conversion. .PARAMETER IncludeExcel Switch to include Excel format in the export. .PARAMETER IncludeMarkdown Switch to include Markdown format in the export. Custom or use a module if available. .PARAMETER IncludeYAML Switch to include YAML format in the export. Requires 'powershell-yaml' module. .EXAMPLE PS> $data = Get-Process | Select-Object -First 10 PS> Export-Data -Data $data -BaseOutputPath "C:\exports\mydata" -IncludeCSV -IncludeJSON This example exports the first 10 processes to CSV and JSON formats. #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [psobject]$Data, [Parameter(Mandatory = $true)] [string]$BaseOutputPath, [switch]$IncludeCSV, [switch]$IncludeJSON, [switch]$IncludeXML, [switch]$IncludeGridView, [switch]$IncludeHTML, [switch]$IncludePlainText, [switch]$IncludePDF, # Requires intermediate HTML to PDF conversion [switch]$IncludeExcel, [switch]$IncludeMarkdown, # Custom or use a module if available [switch]$IncludeYAML # Requires 'powershell-yaml' module ) Begin { # $modules = @('ImportExcel', 'powershell-yaml' , 'PSWriteHTML') # Install-MissingModules -RequiredModules $modules -Verbose # Setup the base path without extension Write-Host "BaseOutputPath before change: '$BaseOutputPath'" $basePathWithoutExtension = [System.IO.Path]::ChangeExtension($BaseOutputPath, $null) # Remove extension manually if it exists $basePathWithoutExtension = if ($BaseOutputPath -match '\.') { $BaseOutputPath.Substring(0, $BaseOutputPath.LastIndexOf('.')) } else { $BaseOutputPath } # Ensure no trailing periods $basePathWithoutExtension = $basePathWithoutExtension.TrimEnd('.') } Process { try { if ($IncludeCSV) { $csvPath = "$basePathWithoutExtension.csv" $Data | Export-Csv -Path $csvPath -NoTypeInformation } if ($IncludeJSON) { $jsonPath = "$basePathWithoutExtension.json" $Data | ConvertTo-Json -Depth 10 | Set-Content -Path $jsonPath } if ($IncludeXML) { $xmlPath = "$basePathWithoutExtension.xml" $Data | Export-Clixml -Path $xmlPath } if ($IncludeGridView) { $Data | Out-GridView -Title "Data Preview" } if ($IncludeHTML) { # Assumes $Data is the dataset you want to export to HTML # and $basePathWithoutExtension is prepared earlier in your script $htmlPath = "$basePathWithoutExtension.html" # Convert $Data to HTML using PSWriteHTML New-HTML -Title "Data Export Report" -FilePath $htmlPath -ShowHTML { New-HTMLSection -HeaderText "Data Export Details" -Content { New-HTMLTable -DataTable $Data -ScrollX -HideFooter } } Write-Host "HTML report generated: '$htmlPath'" } if ($IncludePlainText) { $txtPath = "$basePathWithoutExtension.txt" $Data | Out-String | Set-Content -Path $txtPath } if ($IncludeExcel) { $excelPath = "$basePathWithoutExtension.xlsx" $Data | Export-Excel -Path $excelPath } # Assuming $Data holds the objects you want to serialize to YAML if ($IncludeYAML) { $yamlPath = "$basePathWithoutExtension.yaml" # Check if the powershell-yaml module is loaded if (Get-Module -ListAvailable -Name powershell-yaml) { Import-Module powershell-yaml # Process $Data to handle potentially problematic properties $processedData = $Data | ForEach-Object { $originalObject = $_ $properties = $_ | Get-Member -MemberType Properties $clonedObject = New-Object -TypeName PSObject foreach ($prop in $properties) { try { $clonedObject | Add-Member -MemberType NoteProperty -Name $prop.Name -Value $originalObject.$($prop.Name) -ErrorAction Stop } catch { # Optionally handle or log the error. Skipping problematic property. $clonedObject | Add-Member -MemberType NoteProperty -Name $prop.Name -Value "Error serializing property" -ErrorAction SilentlyContinue } } return $clonedObject } # Convert the processed data to YAML and save it with UTF-16 LE encoding $processedData | ConvertTo-Yaml | Set-Content -Path $yamlPath -Encoding Unicode Write-Host "YAML export completed successfully: $yamlPath" } else { Write-Warning "The 'powershell-yaml' module is not installed. YAML export skipped." } } if ($IncludeMarkdown) { # You'll need to implement or find a ConvertTo-Markdown function or use a suitable module $markdownPath = "$basePathWithoutExtension.md" $Data | ConvertTo-Markdown | Set-Content -Path $markdownPath } if ($IncludePDF) { # Convert HTML to PDF using external tool # This is a placeholder for the process. You will need to generate HTML first and then convert it. $pdfPath = "$basePathWithoutExtension.pdf" # Assuming you have a Convert-HtmlToPdf function or a similar mechanism $htmlPath = "$basePathWithoutExtension.html" $Data | ConvertTo-Html | Convert-HtmlToPdf -OutputPath $pdfPath } } catch { Write-Error "An error occurred during export: $_" } } End { Write-Verbose "Export-Data function execution completed." } } #EndRegion '.\Public\Export-Data.ps1' 206 #Region '.\Public\Get-FunctionModule.ps1' -1 function Get-FunctionModule { param ( [string]$FunctionName ) # Get all imported modules $importedModules = Get-Module # Iterate through the modules to find which one exports the function foreach ($module in $importedModules) { if ($module.ExportedFunctions[$FunctionName]) { return $module.Name } } # If the function is not found in any module, return null return $null } #EndRegion '.\Public\Get-FunctionModule.ps1' 19 #Region '.\Public\Get-PSFCSVLogFilePath.ps1' -1 function Get-PSFCSVLogFilePath { [CmdletBinding()] param ( [Parameter(Mandatory = $true, Position = 0, HelpMessage = "Specify the base path where the logs will be stored.")] [string]$LogsPath, [Parameter(Mandatory = $true, Position = 1, HelpMessage = "Specify the job name to be used in the log file name.")] [string]$JobName, [Parameter(Mandatory = $true, Position = 2, HelpMessage = "Specify the name of the parent script.")] [string]$parentScriptName ) Begin { Write-EnhancedLog -Message "Starting Get-PSFCSVLogFilePath function..." -Level "NOTICE" Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters # Ensure the destination directory exists if (-not (Test-Path -Path $LogsPath)) { New-Item -ItemType Directory -Path $LogsPath -Force | Out-Null Write-EnhancedLog -Message "Created Logs directory at: $LogsPath" -Level "INFO" } } Process { try { # Get the current username $username = if ($env:USERNAME) { $env:USERNAME } else { "UnknownUser" } Write-EnhancedLog -Message "Current username: $username" -Level "INFO" # Log the parent script name Write-EnhancedLog -Message "Script name: $parentScriptName" -Level "INFO" # Check if running as SYSTEM $isSystem = Test-RunningAsSystem Write-EnhancedLog -Message "Is running as SYSTEM: $isSystem" -Level "INFO" # Get the current date for folder creation $currentDate = Get-Date -Format "yyyy-MM-dd" # Construct the hostname and timestamp for the log filename $hostname = $env:COMPUTERNAME $timestamp = Get-Date -Format "yyyy-MM-dd-HH-mm-ss" $logFolderPath = "$LogsPath\$currentDate\$parentScriptName" # Ensure the log directory exists if (-not (Test-Path -Path $logFolderPath)) { New-Item -Path $logFolderPath -ItemType Directory -Force | Out-Null Write-EnhancedLog -Message "Created directory for log file: $logFolderPath" -Level "INFO" } # Generate log file path based on context $logFilePath = if ($isSystem) { "$logFolderPath\$hostname-$JobName-SYSTEM-$parentScriptName-log-$timestamp.csv" } else { "$logFolderPath\$hostname-$JobName-$username-$parentScriptName-log-$timestamp.csv" } $logFilePath = Sanitize-LogFilePath -LogFilePath $logFilePath # Validate the log file path before using it Validate-LogFilePath -LogFilePath $logFilePath Write-EnhancedLog -Message "Generated PSFramework CSV log file path: $logFilePath" -Level "INFO" return $logFilePath } catch { Write-EnhancedLog -Message "An error occurred in Get-PSFCSVLogFilePath: $_" -Level "ERROR" Handle-Error -ErrorRecord $_ throw $_ # Re-throw the error after logging it } } End { Write-EnhancedLog -Message "Exiting Get-PSFCSVLogFilePath function" -Level "NOTICE" } } #EndRegion '.\Public\Get-PSFCSVLogFilePath.ps1' 79 #Region '.\Public\Get-TranscriptFilePath.ps1' -1 function Get-TranscriptFilePath { <# .SYNOPSIS Generates a file path for storing PowerShell transcripts. .DESCRIPTION The Get-TranscriptFilePath function constructs a unique transcript file path based on the provided transcript directory, job name, and parent script name. It ensures the transcript directory exists, handles context (e.g., SYSTEM account), and logs each step of the process. .PARAMETER TranscriptsPath The base directory where transcript files will be stored. .PARAMETER JobName The name of the job or task, used to distinguish different log files. .PARAMETER ParentScriptName The name of the parent script that is generating the transcript. .EXAMPLE $params = @{ TranscriptsPath = 'C:\Transcripts' JobName = 'BackupJob' ParentScriptName = 'BackupScript.ps1' } Get-TranscriptFilePath @params Generates a transcript file path for a script called BackupScript.ps1. #> [CmdletBinding()] param ( [Parameter(Mandatory = $true, HelpMessage = "Provide the base path for transcripts.")] [ValidateNotNullOrEmpty()] [string]$TranscriptsPath, [Parameter(Mandatory = $true, HelpMessage = "Provide the job name.")] [ValidateNotNullOrEmpty()] [string]$JobName, [Parameter(Mandatory = $true, HelpMessage = "Provide the parent script name.")] [ValidateNotNullOrEmpty()] [string]$ParentScriptName ) Begin { # Log the start of the function Write-EnhancedLog -Message "Starting Get-TranscriptFilePath function..." -Level "NOTICE" Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters # Ensure the destination directory exists if (-not (Test-Path -Path $TranscriptsPath)) { New-Item -ItemType Directory -Path $TranscriptsPath -Force | Out-Null Write-EnhancedLog -Message "Created Transcripts directory at: $TranscriptsPath" -Level "INFO" } } Process { try { # Get the current username or fallback to "UnknownUser" $username = if ($env:USERNAME) { $env:USERNAME } else { "UnknownUser" } Write-EnhancedLog -Message "Current username: $username" -Level "INFO" # Log the provided parent script name Write-EnhancedLog -Message "Parent script name: $ParentScriptName" -Level "INFO" # Check if running as SYSTEM $isSystem = Test-RunningAsSystem Write-EnhancedLog -Message "Is running as SYSTEM: $isSystem" -Level "INFO" # Get the current date for folder structure $currentDate = Get-Date -Format "yyyy-MM-dd" Write-EnhancedLog -Message "Current date for transcript folder: $currentDate" -Level "INFO" # Construct the hostname and timestamp for the log file name $hostname = $env:COMPUTERNAME $timestamp = Get-Date -Format "yyyy-MM-dd-HH-mm-ss" $logFolderPath = Join-Path -Path $TranscriptsPath -ChildPath "$currentDate\$ParentScriptName" # Ensure the log directory exists if (-not (Test-Path -Path $logFolderPath)) { New-Item -Path $logFolderPath -ItemType Directory -Force | Out-Null Write-EnhancedLog -Message "Created directory for transcript logs: $logFolderPath" -Level "INFO" } # Generate log file path based on context (SYSTEM or user) $logFilePath = if ($isSystem) { "$logFolderPath\$hostname-$JobName-SYSTEM-$ParentScriptName-transcript-$timestamp.log" } else { "$logFolderPath\$hostname-$JobName-$username-$ParentScriptName-transcript-$timestamp.log" } Write-EnhancedLog -Message "Constructed log file path: $logFilePath" -Level "INFO" # Sanitize and validate the log file path $logFilePath = Sanitize-LogFilePath -LogFilePath $logFilePath Validate-LogFilePath -LogFilePath $logFilePath Write-EnhancedLog -Message "Log file path sanitized and validated: $logFilePath" -Level "INFO" # Return the constructed file path return $logFilePath } catch { Write-EnhancedLog -Message "An error occurred in Get-TranscriptFilePath: $($_.Exception.Message)" -Level "ERROR" Handle-Error -ErrorRecord $_ throw } } End { Write-EnhancedLog -Message "Exiting Get-TranscriptFilePath function" -Level "NOTICE" } } #EndRegion '.\Public\Get-TranscriptFilePath.ps1' 112 #Region '.\Public\Handle-PSFLogging.ps1' -1 function Copy-PSFLogs { [CmdletBinding()] param ( [string]$SourcePath, [string]$DestinationPath ) Begin { Write-EnhancedLog -Message "Starting Copy-PSFLogs function..." -Level "NOTICE" Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters } Process { if (Test-Path -Path $SourcePath) { try { Copy-Item -Path "$SourcePath*" -Destination $DestinationPath -Recurse -Force -ErrorAction Stop Write-EnhancedLog -Message "Log files successfully copied from $SourcePath to $DestinationPath" -Level "INFO" } catch { Write-EnhancedLog -Message "Failed to copy logs from $SourcePath. Error: $_" -Level "ERROR" } } else { Write-EnhancedLog -Message "Log path not found: $SourcePath" -Level "WARNING" } } End { Write-EnhancedLog -Message "Exiting Copy-PSFLogs function" -Level "NOTICE" } } function Remove-PSFLogs { [CmdletBinding()] param ( [string]$SourcePath ) Begin { Write-EnhancedLog -Message "Starting Remove-PSFLogs function..." -Level "NOTICE" Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters } Process { if (Test-Path -Path $SourcePath) { try { Remove-Item -Path "$SourcePath*" -Recurse -Force -ErrorAction Stop Write-EnhancedLog -Message "Logs successfully removed from $SourcePath" -Level "INFO" } catch { Write-EnhancedLog -Message "Failed to remove logs from $SourcePath. Error: $_" -Level "ERROR" } } else { Write-EnhancedLog -Message "Log path not found: $SourcePath" -Level "WARNING" } } End { Write-EnhancedLog -Message "Exiting Remove-PSFLogs function" -Level "NOTICE" } } function Handle-PSFLogging { [CmdletBinding()] param ( [string]$SystemSourcePathWindowsPS = "C:\Windows\System32\config\systemprofile\AppData\Roaming\WindowsPowerShell\PSFramework\Logs\", [string]$SystemSourcePathPS = "C:\Windows\System32\config\systemprofile\AppData\Roaming\PowerShell\PSFramework\Logs\", [string]$UserSourcePathWindowsPS = "$env:USERPROFILE\AppData\Roaming\WindowsPowerShell\PSFramework\Logs\", [string]$UserSourcePathPS = "$env:USERPROFILE\AppData\Roaming\PowerShell\PSFramework\Logs\", [string]$PSFPath = "C:\Logs\PSF", [string]$ParentScriptName, [string]$JobName, [bool]$SkipSYSTEMLogCopy = $false, [bool]$SkipSYSTEMLogRemoval = $false ) Begin { Write-EnhancedLog -Message "Starting Handle-PSFLogging function..." -Level "NOTICE" Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters # Get the current username and script name $username = if ($env:USERNAME) { $env:USERNAME } else { "UnknownUser" } $scriptName = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.ScriptName) if (-not $scriptName) { $scriptName = "UnknownScript" } # Get the current date for folder creation $currentDate = Get-Date -Format "yyyy-MM-dd" $logFolderPath = "$PSFPath\$currentDate\$scriptName" # Ensure the destination directory exists if (-not (Test-Path -Path $logFolderPath)) { New-Item -ItemType Directory -Path $logFolderPath -Force Write-EnhancedLog -Message "Created destination directory at $logFolderPath" -Level "INFO" } } Process { # Copy logs from both SYSTEM profile paths if (-not $SkipSYSTEMLogCopy) { Copy-PSFLogs -SourcePath $SystemSourcePathWindowsPS -DestinationPath $logFolderPath Write-EnhancedLog -Message "Copied SYSTEM logs from $SystemSourcePathWindowsPS to $logFolderPath." -Level "INFO" Copy-PSFLogs -SourcePath $SystemSourcePathPS -DestinationPath $logFolderPath Write-EnhancedLog -Message "Copied SYSTEM logs from $SystemSourcePathPS to $logFolderPath." -Level "INFO" } else { Write-EnhancedLog -Message "Skipping SYSTEM log copy as per the provided parameter." -Level "INFO" } # Copy logs from the user's profile paths for both PowerShell versions Copy-PSFLogs -SourcePath $UserSourcePathWindowsPS -DestinationPath $logFolderPath Copy-PSFLogs -SourcePath $UserSourcePathPS -DestinationPath $logFolderPath # Verify that the files have been copied if (Test-Path -Path $logFolderPath) { Write-EnhancedLog -Message "Logs successfully processed to $logFolderPath" -Level "INFO" } else { Write-EnhancedLog -Message "Failed to process log files." -Level "ERROR" } # Remove logs from the SYSTEM profile paths if (-not $SkipSYSTEMLogRemoval) { # Remove logs from the SYSTEM profile paths Remove-PSFLogs -SourcePath $SystemSourcePathWindowsPS Write-EnhancedLog -Message "Removed SYSTEM logs from $SystemSourcePathWindowsPS." -Level "INFO" Remove-PSFLogs -SourcePath $SystemSourcePathPS Write-EnhancedLog -Message "Removed SYSTEM logs from $SystemSourcePathPS." -Level "INFO" } else { Write-EnhancedLog -Message "Skipping SYSTEM log removal as per the provided parameter." -Level "INFO" } # Remove logs from the User profile paths Remove-PSFLogs -SourcePath $UserSourcePathWindowsPS Remove-PSFLogs -SourcePath $UserSourcePathPS # Rename SYSTEM logs in PSF to append SYSTEM to easily identify these files $RenamePSFLogFilesParams = @{ LogDirectoryPath = $logFolderPath ParentScriptName = $ParentScriptName JobName = $jobName } Rename-PSFLogFilesWithUsername @RenamePSFLogFilesParams } End { Write-EnhancedLog -Message "Exiting Handle-PSFLogging function" -Level "NOTICE" } } # $HandlePSFLoggingParams = @{ # SystemSourcePathWindowsPS = "C:\Windows\System32\config\systemprofile\AppData\Roaming\WindowsPowerShell\PSFramework\Logs\" # SystemSourcePathPS = "C:\Windows\System32\config\systemprofile\AppData\Roaming\PowerShell\PSFramework\Logs\" # UserSourcePathWindowsPS = "$env:USERPROFILE\AppData\Roaming\WindowsPowerShell\PSFramework\Logs\" # UserSourcePathPS = "$env:USERPROFILE\AppData\Roaming\PowerShell\PSFramework\Logs\" # PSFPath = "C:\Logs\PSF" # } # Handle-PSFLogging @HandlePSFLoggingParams #EndRegion '.\Public\Handle-PSFLogging.ps1' 163 #Region '.\Public\Initialize-ScriptAndLogging.ps1' -1 $LoggingDeploymentName = $config.LoggingDeploymentName function Initialize-ScriptAndLogging { $isWindowsOS = $false if ($PSVersionTable.PSVersion.Major -ge 6) { $isWindowsOS = $isWindowsOS -or ($PSVersionTable.Platform -eq 'Win32NT') } else { $isWindowsOS = $isWindowsOS -or ($env:OS -eq 'Windows_NT') } $deploymentName = "$LoggingDeploymentName" # Replace this with your actual deployment name $baseScriptPath = if ($isWindowsOS) { "C:\code" } else { "/home/code" } $scriptPath_1001 = Join-Path -Path $baseScriptPath -ChildPath $deploymentName $computerName = if ($isWindowsOS) { $env:COMPUTERNAME } else { (hostname) } try { if (-not (Test-Path -Path $scriptPath_1001)) { New-Item -ItemType Directory -Path $scriptPath_1001 -Force | Out-Null Write-Host "Created directory: $scriptPath_1001" -ForegroundColor Green } $Filename = "$LoggingDeploymentName" $logDir = Join-Path -Path $scriptPath_1001 -ChildPath "exports/Logs/$computerName" $logPath = Join-Path -Path $logDir -ChildPath "$(Get-Date -Format 'yyyy-MM-dd-HH-mm-ss')" if (-not (Test-Path $logPath)) { Write-Host "Did not find log directory at $logPath" -ForegroundColor Yellow Write-Host "Creating log directory at $logPath" -ForegroundColor Yellow New-Item -ItemType Directory -Path $logPath -Force -ErrorAction Stop | Out-Null Write-Host "Created log directory at $logPath" -ForegroundColor Green } $logFile = Join-Path -Path $logPath -ChildPath "$Filename-Transcript.log" Start-Transcript -Path $logFile -ErrorAction Stop | Out-Null return @{ ScriptPath = $scriptPath_1001 Filename = $Filename LogPath = $logPath LogFile = $logFile } } catch { Write-Host "An error occurred while initializing script and logging: $_" -ForegroundColor Red } } # Example usage of Initialize-ScriptAndLogging # try { # $initResult = Initialize-ScriptAndLogging # Write-Host "Initialization successful. Log file path: $($initResult.LogFile)" -ForegroundColor Green # } catch { # Write-Host "Initialization failed: $_" -ForegroundColor Red # } #EndRegion '.\Public\Initialize-ScriptAndLogging.ps1' 55 #Region '.\Public\Log-And-Execute-Step.ps1' -1 function Log-And-Execute-Step { [CmdletBinding()] param () Begin { Write-EnhancedLog -Message "Starting Log-And-Execute-Step function" -Level "INFO" } Process { try { $global:currentStep++ $totalSteps = $global:steps.Count $step = $global:steps[$global:currentStep - 1] Write-EnhancedLog -Message "Step [$global:currentStep/$totalSteps]: $($step.Description)" -Level "INFO" & $step.Action } catch { Write-EnhancedLog -Message "Error in step: $($step.Description) - $($_.Exception.Message)" -Level "ERROR" Handle-Error -ErrorRecord $_ } } End { Write-EnhancedLog -Message "Exiting Log-And-Execute-Step function" -Level "INFO" } } # Example usage # Log-And-Execute-Step #EndRegion '.\Public\Log-And-Execute-Step.ps1' 30 #Region '.\Public\Remove-LogsFolder.ps1' -1 function Remove-LogsFolder { <# .SYNOPSIS Removes the logs folder located at C:\Logs and validates the removal. .DESCRIPTION The Remove-LogsFolder function removes the logs folder located at C:\Logs using the Remove-EnhancedItem function. It validates the existence of the folder before and after removal to ensure successful deletion. .PARAMETER LogFolderPath The path of the logs folder to be removed. Default is C:\Logs. .PARAMETER MaxRetries The maximum number of retries to attempt for removal. Default is 5. .PARAMETER RetryInterval The interval in seconds between retries. Default is 10 seconds. .EXAMPLE Remove-LogsFolder Removes the logs folder at C:\Logs. #> [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [string]$LogFolderPath = "C:\Logs", [Parameter(Mandatory = $false)] [int]$MaxRetries = 5, [Parameter(Mandatory = $false)] [int]$RetryInterval = 10 ) Begin { Write-EnhancedLog -Message "Starting Remove-LogsFolder function" -Level "Notice" Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters } Process { try { # Validate before removal $validationResultsBefore = Validate-PathExistsWithLogging -Paths $LogFolderPath if ($validationResultsBefore.TotalValidatedFiles -gt 0) { Write-EnhancedLog -Message "Attempting to remove logs folder: $LogFolderPath" -Level "INFO" $removeParams = @{ Path = $LogFolderPath ForceKillProcesses = $true MaxRetries = $MaxRetries RetryInterval = $RetryInterval } Remove-EnhancedItem @removeParams # Validate after removal $validationResultsAfter = Validate-PathExistsWithLogging -Paths $LogFolderPath if ($validationResultsAfter.TotalValidatedFiles -gt 0) { Write-EnhancedLog -Message "Logs folder $LogFolderPath still exists after attempting to remove. Manual intervention may be required." -Level "ERROR" } else { Write-EnhancedLog -Message "Logs folder $LogFolderPath successfully removed." -Level "CRITICAL" } } else { Write-EnhancedLog -Message "Logs folder $LogFolderPath does not exist. No action taken." -Level "WARNING" } } catch { Write-EnhancedLog -Message "Error during removal of logs folder at path: $LogFolderPath. Error: $($_.Exception.Message)" -Level "ERROR" Handle-Error -ErrorRecord $_ } } End { Write-EnhancedLog -Message "Exiting Remove-LogsFolder function" -Level "Notice" } } # Example usage # Remove-LogsFolder #EndRegion '.\Public\Remove-LogsFolder.ps1' 83 #Region '.\Public\Rename-PSFLogFilesWithUsername.ps1' -1 function Rename-PSFLogFilesWithUsername { [CmdletBinding()] param ( [Parameter(Mandatory = $true, Position = 0)] [string]$LogDirectoryPath, [Parameter(Mandatory = $true, Position = 1)] [string]$ParentScriptName, [Parameter(Mandatory = $true, Position = 2)] [string]$JobName ) Begin { Write-EnhancedLog -Message "Starting Rename-PSFLogFilesWithUsername function..." -Level "NOTICE" Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters } Process { Write-EnhancedLog -Message "Starting the renaming process for log files in directory: $LogDirectoryPath" -Level "INFO" # Get all log files in the specified directory $logFiles = Get-ChildItem -Path $LogDirectoryPath -Filter "*.log" if ($logFiles.Count -eq 0) { Write-EnhancedLog -Message "No log files found in the directory." -Level "WARNING" return } Write-EnhancedLog -Message "Found $($logFiles.Count) log files to process." -Level "INFO" foreach ($logFile in $logFiles) { try { Write-EnhancedLog -Message "Processing file: $($logFile.FullName)" -Level "INFO" # Import the log file as CSV $logEntries = Import-Csv -Path $logFile.FullName # Extract the username, timestamp, and computer name from the first entry in the CSV $firstEntry = $logEntries | Select-Object -First 1 $username = $firstEntry.Username $timestamp = $firstEntry.Timestamp $computerName = $firstEntry.ComputerName if (-not $username) { Write-EnhancedLog -Message "No username found in $($logFile.Name). Skipping file." -Level "WARNING" continue } if (-not $timestamp) { $timestamp = "NoTimestamp" Write-EnhancedLog -Message "No timestamp found in $($logFile.Name). Defaulting to 'NoTimestamp'." -Level "WARNING" } if (-not $computerName) { $computerName = "UnknownComputer" Write-EnhancedLog -Message "No computer name found in $($logFile.Name). Defaulting to 'UnknownComputer'." -Level "WARNING" } Write-EnhancedLog -Message "Username found in file: $username" -Level "INFO" Write-EnhancedLog -Message "Timestamp found in file: $timestamp" -Level "INFO" Write-EnhancedLog -Message "Computer name found in file: $computerName" -Level "INFO" # Remove the domain part if present in the username if ($username -match '^[^\\]+\\(.+)$') { $username = $matches[1] } Write-EnhancedLog -Message "Processed username: $username" -Level "INFO" # Sanitize the username, timestamp, and computer name $safeUsername = $username -replace '[\\/:*?"<>|]', '_' $safeTimestamp = $timestamp -replace '[\\/:*?"<>|]', '_' $safeComputerName = $computerName -replace '[\\/:*?"<>|]', '_' Write-EnhancedLog -Message "Sanitized username: $safeUsername" -Level "INFO" Write-EnhancedLog -Message "Sanitized timestamp: $safeTimestamp" -Level "INFO" Write-EnhancedLog -Message "Sanitized computer name: $safeComputerName" -Level "INFO" # Generate a unique identifier $uniqueId = [guid]::NewGuid().ToString() # Construct the new file name with the GUID $fileExtension = [System.IO.Path]::GetExtension($logFile.FullName) $newFileName = "$JobName-$safeUsername-$safeComputerName-$ParentScriptName-$safeTimestamp-$uniqueId$fileExtension" $newFilePath = [System.IO.Path]::Combine($logFile.DirectoryName, $newFileName) Write-EnhancedLog -Message "Attempting to rename to: $newFilePath" -Level "INFO" # Rename the file Rename-Item -Path $logFile.FullName -NewName $newFileName -Force Write-EnhancedLog -Message "Successfully renamed $($logFile.FullName) to $newFileName" -Level "INFO" } catch { Write-EnhancedLog -Message "Failed to process $($logFile.FullName): $_" -Level "ERROR" Write-EnhancedLog -Message "Possible cause: The file name or path may contain invalid characters or the file might be in use." -Level "ERROR" } } Write-EnhancedLog -Message "Finished processing all log files." -Level "INFO" } End { Write-EnhancedLog -Message "Exiting Rename-PSFLogFilesWithUsername function" -Level "NOTICE" } } #EndRegion '.\Public\Rename-PSFLogFilesWithUsername.ps1' 106 #Region '.\Public\Sanitize-LogFilePath.ps1' -1 function Sanitize-LogFilePath { [CmdletBinding()] param ( [string]$LogFilePath ) try { Write-EnhancedLog -Message "Starting Sanitize-LogFilePath function..." -Level "NOTICE" Write-EnhancedLog -Message "Original LogFilePath: $LogFilePath" -Level "INFO" # Trim leading and trailing whitespace $LogFilePath = $LogFilePath.Trim() Write-EnhancedLog -Message "LogFilePath after trim: $LogFilePath" -Level "INFO" # Replace multiple spaces with a single space $LogFilePath = $LogFilePath -replace '\s+', ' ' Write-EnhancedLog -Message "LogFilePath after removing multiple spaces: $LogFilePath" -Level "INFO" # Replace illegal characters (preserve drive letter and colon) if ($LogFilePath -match '^([a-zA-Z]):\\') { $drive = $matches[1] $LogFilePath = $LogFilePath -replace '[<>:"|?*]', '_' $LogFilePath = "$drive`:$($LogFilePath.Substring(2))" } else { # Handle cases where the path doesn't start with a drive letter $LogFilePath = $LogFilePath -replace '[<>:"|?*]', '_' } Write-EnhancedLog -Message "LogFilePath after replacing invalid characters: $LogFilePath" -Level "INFO" # Replace multiple backslashes with a single backslash $LogFilePath = [System.Text.RegularExpressions.Regex]::Replace($LogFilePath, '\\+', '\') Write-EnhancedLog -Message "LogFilePath after replacing multiple slashes: $LogFilePath" -Level "INFO" # Ensure the path is still rooted if (-not [System.IO.Path]::IsPathRooted($LogFilePath)) { throw "The LogFilePath is not rooted: $LogFilePath" } Write-EnhancedLog -Message "Sanitized LogFilePath: $LogFilePath" -Level "INFO" Write-EnhancedLog -Message "Exiting Sanitize-LogFilePath function" -Level "NOTICE" return $LogFilePath } catch { Write-EnhancedLog -Message "An error occurred in Sanitize-LogFilePath: $_" -Level "ERROR" Handle-Error -ErrorRecord $_ throw $_ # Re-throw the error after logging it } } #EndRegion '.\Public\Sanitize-LogFilePath.ps1' 50 #Region '.\Public\Validate-LogFilePath.ps1' -1 function Validate-LogFilePath { [CmdletBinding()] param ( [string]$LogFilePath ) try { Write-EnhancedLog -Message "Starting Validate-LogFilePath function..." -Level "NOTICE" Write-EnhancedLog -Message "Validating LogFilePath: $LogFilePath" -Level "INFO" # Check for invalid characters in the file path if ($LogFilePath -match "[<>""|?*]") { Write-EnhancedLog -Message "Warning: The LogFilePath contains invalid characters." -Level "WARNING" } # Check for double backslashes which may indicate an error in path generation if ($LogFilePath -match "\\\\") { Write-EnhancedLog -Message "Warning: The LogFilePath contains double backslashes." -Level "WARNING" } Write-EnhancedLog -Message "Validation complete for LogFilePath: $LogFilePath" -Level "INFO" Write-EnhancedLog -Message "Exiting Validate-LogFilePath function" -Level "NOTICE" } catch { Write-EnhancedLog -Message "An error occurred in Validate-LogFilePath: $_" -Level "ERROR" Handle-Error -ErrorRecord $_ throw $_ # Re-throw the error after logging it } } #EndRegion '.\Public\Validate-LogFilePath.ps1' 30 #Region '.\Public\Write-LogMessage.ps1' -1 function Generate-UniqueBase { <# .SYNOPSIS Generates a unique base identifier using the current timestamp, process ID, and a random number. .DESCRIPTION The Generate-UniqueBase function creates a unique identifier composed of parts of the current timestamp, the process ID, and a random number. This ensures uniqueness for various applications that require a simple, unique identifier. .PARAMETER TimestampFormat Specifies the format of the timestamp to use in the identifier. .PARAMETER ProcessIdLength Specifies the number of digits to use from the process ID. .PARAMETER RandomPartMin Specifies the minimum value for the random number part. .PARAMETER RandomPartMax Specifies the maximum value for the random number part. .EXAMPLE Generate-UniqueBase -TimestampFormat "yyMMddHHmm" -ProcessIdLength 4 -RandomPartMin 10 -RandomPartMax 99 Generates a unique identifier using the specified timestamp format, process ID length, and random number range. #> [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [string]$TimestampFormat = "yyMMddHHmm", [Parameter(Mandatory = $false)] [int]$ProcessIdLength = 4, [Parameter(Mandatory = $false)] [int]$RandomPartMin = 10, [Parameter(Mandatory = $false)] [int]$RandomPartMax = 99 ) Begin { Write-EnhancedLog -Message "Starting Generate-UniqueBase function" -Level "NOTICE" Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters } Process { try { # Generate the components $timestamp = (Get-Date).ToString($TimestampFormat) $processid = $PID.ToString("D$ProcessIdLength") $randomPart = (Get-Random -Minimum $RandomPartMin -Maximum $RandomPartMax).ToString("D2") Write-EnhancedLog -Message "Generated timestamp: '$timestamp', Process ID: '$processid', Random Part: '$randomPart'" -Level "INFO" # Adjust the components to ensure the length is 8 characters Write-EnhancedLog -Message "Original lengths -> Timestamp: $($timestamp.Length), Process ID: $($processid.Length), Random Part: $($randomPart.Length)" -Level "INFO" $uniqueBase = $timestamp.Substring($timestamp.Length - 4, 4) + $processid.Substring(0, 2) + $randomPart Write-EnhancedLog -Message "Generated Unique Base (before validation): '$uniqueBase', Length: $($uniqueBase.Length)" -Level "INFO" # Validate that the unique base is exactly 8 characters long if ($uniqueBase.Length -ne 8) { $errorMessage = "The generated unique base '$uniqueBase' is not 8 characters long. It is $($uniqueBase.Length) characters. Halting script execution." Write-EnhancedLog -Message $errorMessage -Level "CRITICAL" -ForegroundColor ([ConsoleColor]::Red) throw $errorMessage } Write-EnhancedLog -Message "Unique base successfully generated: $uniqueBase" -Level "INFO" return $uniqueBase } catch { Write-EnhancedLog -Message "An error occurred in Generate-UniqueBase function: $($_.Exception.Message)" -Level "ERROR" Handle-Error -ErrorRecord $_ throw $_ } } End { Write-EnhancedLog -Message "Exiting Generate-UniqueBase function" -Level "NOTICE" } } function Validate-EventLog { <# .SYNOPSIS Validates whether an event log and its associated source exist and are correctly configured. #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string]$LogName, [Parameter(Mandatory = $true)] [string]$SourceName, [Parameter(Mandatory = $false)] [switch]$PostValidation ) Begin { Write-EnhancedLog -Message "Starting Validate-EventLog function" -Level "NOTICE" Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters } Process { try { # Validate if the log exists Write-EnhancedLog -Message "Validating if event log '$LogName' exists." -Level "INFO" $logExists = @(Get-WinEvent -ListLog * | Where-Object { $_.LogName -eq $LogName }).Count -gt 0 Write-EnhancedLog -Message "Event log '$LogName' existence: $logExists" -Level "INFO" # Validate if the source exists Write-EnhancedLog -Message "Validating if event source '$SourceName' exists." -Level "INFO" $sourceExists = [System.Diagnostics.EventLog]::SourceExists($SourceName) Write-EnhancedLog -Message "Event source '$SourceName' existence: $sourceExists" -Level "INFO" $sourceLogName = if ($sourceExists) { [System.Diagnostics.EventLog]::LogNameFromSourceName($SourceName, ".") } else { Write-EnhancedLog -Message "Event source '$SourceName' is not associated with any log." -Level "WARNING" $null } Write-EnhancedLog -Message "Event source '$SourceName' is associated with log: $sourceLogName" -Level "INFO" # Return the validation results as a hashtable $result = @{ LogExists = $logExists SourceExists = $sourceExists SourceLogName = $sourceLogName } # Ensure the result is correctly returned as a hashtable if ($result -is [System.Collections.Hashtable]) { Write-EnhancedLog -Message "Returning validation result as a hashtable: LogExists = $($result['LogExists']), SourceExists = $($result['SourceExists']), SourceLogName = $($result['SourceLogName'])" -Level "INFO" } else { Write-EnhancedLog -Message "Unexpected data type for validation result. Expected a hashtable." -Level "ERROR" throw "Unexpected data type for validation result." } return $result } catch { Write-EnhancedLog -Message "An error occurred in Validate-EventLog function: $($_.Exception.Message)" -Level "ERROR" Handle-Error -ErrorRecord $_ throw $_ } } End { Write-EnhancedLog -Message "Exiting Validate-EventLog function" -Level "NOTICE" } } function Manage-EventLogSource { <# .SYNOPSIS Manages the event log source, ensuring it is correctly associated with the specified event log. .DESCRIPTION The Manage-EventLogSource function checks the current state of the event log and source. It handles scenarios where the log or source might not exist, or where the source is associated with a different log, and ensures that the event log and source are properly created or reassigned. .PARAMETER LogName The name of the event log. .PARAMETER SourceName The name of the event source. .PARAMETER ValidationResult A hashtable containing the results of the pre-validation process, including whether the log and source exist and their associations. .EXAMPLE Manage-EventLogSource -LogName "MyLog" -SourceName "MySource" -ValidationResult $validationResult Ensures that the event log "MyLog" is correctly associated with the source "MySource." #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string]$LogName, [Parameter(Mandatory = $true)] [string]$SourceName, [Parameter(Mandatory = $true)] [hashtable]$ValidationResult ) Begin { Write-EnhancedLog -Message "Starting Manage-EventLogSource function" -Level "NOTICE" Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters } Process { try { if (-not $ValidationResult.LogExists -and -not $ValidationResult.SourceExists) { # Neither the log nor the source exist, create both Write-EnhancedLog -Message "Neither log nor source exists. Creating both." -Level "INFO" New-EventLog -LogName $LogName -Source $SourceName Write-EnhancedLog -Message "Created event log '$LogName' and source '$SourceName'." -Level "INFO" } elseif ($ValidationResult.LogExists -and $ValidationResult.SourceExists -and $ValidationResult.SourceLogName -ne $LogName) { # Both log and source exist but the source is associated with a different log Write-EnhancedLog -Message "Source exists but is associated with a different log. Recreating source." -Level "WARNING" Remove-EventLog -Source $SourceName New-EventLog -LogName $LogName -Source $SourceName Write-EnhancedLog -Message "Source '$SourceName' was associated with a different log ('$ValidationResult.SourceLogName'). Recreated event log '$LogName' with source '$SourceName'." -Level "WARNING" } elseif (-not $ValidationResult.LogExists -and $ValidationResult.SourceExists) { # Source exists but is associated with a different log, so remove the source and create the correct log and source Write-EnhancedLog -Message "Log does not exist, but source exists. Removing source and creating log." -Level "WARNING" Remove-EventLog -Source $SourceName New-EventLog -LogName $LogName -Source $SourceName Write-EnhancedLog -Message "Event source '$SourceName' was associated with a different log. Recreated event log '$LogName' with source '$SourceName'." -Level "WARNING" } elseif ($ValidationResult.LogExists -and -not $ValidationResult.SourceExists) { # Log exists but the source does not, so create the source Write-EnhancedLog -Message "Log exists, but source does not. Creating source." -Level "INFO" New-EventLog -LogName $LogName -Source $SourceName Write-EnhancedLog -Message "Added source '$SourceName' to existing event log '$LogName'." -Level "INFO" } else { # Both log and source exist and are correctly associated Write-EnhancedLog -Message "Event log '$LogName' and source '$SourceName' already exist and are correctly associated." -Level "INFO" } $DBG } catch { Write-EnhancedLog -Message "An error occurred in Manage-EventLogSource function: $($_.Exception.Message)" -Level "ERROR" Handle-Error -ErrorRecord $_ throw $_ } } End { Write-EnhancedLog -Message "Exiting Manage-EventLogSource function" -Level "NOTICE" } } function Sanitize-ParentScriptName { <# .SYNOPSIS Sanitizes the parent script name by removing spaces and any special characters. .DESCRIPTION The Sanitize-ParentScriptName function ensures that the parent script name is sanitized by removing spaces, special characters, and ensuring it conforms to naming conventions suitable for log names. .PARAMETER ParentScriptName The parent script name to sanitize. .EXAMPLE $sanitizedParentScriptName = Sanitize-ParentScriptName -ParentScriptName "My Script Name 2024" #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string]$ParentScriptName ) Begin { Write-EnhancedLog -Message "Starting Sanitize-ParentScriptName function" -Level "NOTICE" Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters } Process { try { # Remove any spaces and special characters Write-EnhancedLog -Message "Sanitizing the parent script name: '$ParentScriptName'." -Level "INFO" $sanitizedParentScriptName = $ParentScriptName -replace '\s+', '' -replace '[^a-zA-Z0-9]', '' Write-EnhancedLog -Message "Sanitized parent script name: '$sanitizedParentScriptName'." -Level "INFO" return $sanitizedParentScriptName } catch { Write-EnhancedLog -Message "An error occurred in Sanitize-ParentScriptName function: $($_.Exception.Message)" -Level "ERROR" Handle-Error -ErrorRecord $_ throw $_ } } End { Write-EnhancedLog -Message "Exiting Sanitize-ParentScriptName function" -Level "NOTICE" } } function Sanitize-LogName { <# .SYNOPSIS Sanitizes the log name to ensure it is a valid string. .DESCRIPTION The Sanitize-LogName function checks if the log name is a valid string. If the log name is not a string, the function attempts to convert it. If it contains invalid characters or if the conversion is not possible, an error is thrown. .PARAMETER LogName The log name to sanitize. .EXAMPLE $sanitizedLogName = Sanitize-LogName -LogName $logName Sanitizes the provided log name and returns a clean string. #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [AllowNull()] [AllowEmptyString()] [System.Object]$LogName ) Begin { Write-EnhancedLog -Message "Starting Sanitize-LogName function" -Level "NOTICE" Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters } Process { try { # Ensure the LogName is a single string, not an array or hashtable if ($LogName -is [System.Collections.IEnumerable] -and $LogName -notlike [string]) { Write-EnhancedLog -Message "Log name is an array or collection. Attempting to extract the first valid string." -Level "WARNING" $LogName = $LogName | Where-Object { $_ -is [string] } | Select-Object -First 1 } # If LogName is still not a string, throw an error if ($LogName -isnot [string]) { Write-EnhancedLog -Message "Log name is not a valid string. Actual type: $($LogName.GetType().Name)" -Level "ERROR" throw "Log name must be a string. Received type: $($LogName.GetType().Name)" } # Sanitize the log name by trimming and removing any unnecessary characters $sanitizedLogName = $LogName.Trim() -replace '[^\w-]', '' Write-EnhancedLog -Message "Sanitized log name: '$sanitizedLogName'" -Level "INFO" return $sanitizedLogName } catch { Write-EnhancedLog -Message "An error occurred in Sanitize-LogName function: $($_.Exception.Message)" -Level "ERROR" Handle-Error -ErrorRecord $_ throw $_ } } End { Write-EnhancedLog -Message "Exiting Sanitize-LogName function" -Level "NOTICE" } } function Sanitize-SourceName { <# .SYNOPSIS Sanitizes the source name by removing spaces and special characters. .DESCRIPTION The Sanitize-SourceName function ensures that the source name is sanitized by removing spaces, special characters, and ensuring it conforms to naming conventions suitable for log sources. .PARAMETER SourceName The source name to sanitize. .EXAMPLE $sanitizedSourceName = Sanitize-SourceName -SourceName "My Source Name 2024" #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string]$SourceName ) Begin { Write-EnhancedLog -Message "Starting Sanitize-SourceName function" -Level "NOTICE" Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters } Process { try { # Remove any spaces and special characters Write-EnhancedLog -Message "Sanitizing the source name: '$SourceName'." -Level "INFO" $sanitizedSourceName = $SourceName -replace '\s+', '' -replace '[^a-zA-Z0-9]', '' Write-EnhancedLog -Message "Sanitized source name: '$sanitizedSourceName'." -Level "INFO" return $sanitizedSourceName } catch { Write-EnhancedLog -Message "An error occurred in Sanitize-SourceName function: $($_.Exception.Message)" -Level "ERROR" Handle-Error -ErrorRecord $_ throw $_ } } End { Write-EnhancedLog -Message "Exiting Sanitize-SourceName function" -Level "NOTICE" } } function Manage-EventLogs { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string]$ParentScriptName ) Begin { Write-EnhancedLog -Message "Starting Manage-EventLogs function" -Level "NOTICE" Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters } Process { try { # Step 1: Sanitize the parent script name Write-EnhancedLog -Message "Sanitizing the parent script name." -Level "INFO" $sanitizedParentScriptName = Sanitize-ParentScriptName -ParentScriptName $ParentScriptName Write-EnhancedLog -Message "Sanitized parent script name: '$sanitizedParentScriptName'." -Level "INFO" # Step 2: Generate the unique base for the log name Write-EnhancedLog -Message "Generating unique base for log name." -Level "INFO" $uniqueBase = Generate-UniqueBase Write-EnhancedLog -Message "Unique base generated: $uniqueBase" -Level "INFO" # Step 3: Combine the unique base with the sanitized parent script name $logName = "$uniqueBase-$sanitizedParentScriptName" Write-EnhancedLog -Message "Constructed log name: $logName" -Level "INFO" # Step 4: Sanitize the log name Write-EnhancedLog -Message "Sanitizing the log name." -Level "INFO" $global:sanitizedlogName = Sanitize-LogName -LogName $logName Write-EnhancedLog -Message "Sanitized log name: '$global:sanitizedlogName'." -Level "INFO" # Step 5: Retrieve existing logs matching the sanitized parent script name Write-EnhancedLog -Message "Retrieving existing logs that match the sanitized parent script name: '$sanitizedParentScriptName'." -Level "INFO" $logs = @() try { $logs = @(Get-WinEvent -ListLog * | Where-Object { $_.LogName -like "*$sanitizedParentScriptName*" }) Write-EnhancedLog -Message "Retrieved $($logs.Count) logs that match the sanitized parent script name." -Level "INFO" } catch { Write-EnhancedLog -Message "Failed to retrieve logs due to an error: $($_.Exception.Message)" -Level "ERROR" throw $_ } # Step 6: Check if an existing log should be used if ($logs.Count -gt 0) { $existingLog = $logs | Select-Object -First 1 if ($existingLog) { $logName = $existingLog.LogName Write-EnhancedLog -Message "Existing log found and will be used: '$logName'." -Level "INFO" } } else { Write-EnhancedLog -Message "No existing log found for sanitized parent script name: '$sanitizedParentScriptName'. Creating a new log: '$logName'." -Level "INFO" } # Return the sanitized and validated log name return $logName } catch { Write-EnhancedLog -Message "An error occurred in Manage-EventLogs function: $($_.Exception.Message)" -Level "ERROR" Handle-Error -ErrorRecord $_ throw $_ } } End { Write-EnhancedLog -Message "Exiting Manage-EventLogs function" -Level "NOTICE" } } function Construct-SourceName { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string]$LogName, [Parameter(Mandatory = $true)] [string]$CallerFunction ) Begin { Write-EnhancedLog -Message "Starting Construct-SourceName function" -Level "NOTICE" Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters } Process { try { # Step 1: Construct the initial source name Write-EnhancedLog -Message "Constructing source name using LogName '$LogName' and CallerFunction '$CallerFunction'." -Level "INFO" $sourceName = "$LogName-$CallerFunction" Write-EnhancedLog -Message "Constructed source name: '$sourceName'" -Level "INFO" # Step 2: Sanitize the source name Write-EnhancedLog -Message "Sanitizing the constructed source name." -Level "INFO" $sanitizedSourceName = Sanitize-SourceName -SourceName $sourceName Write-EnhancedLog -Message "Sanitized source name: '$sanitizedSourceName'." -Level "INFO" return $sanitizedSourceName } catch { Write-EnhancedLog -Message "An error occurred in Construct-SourceName function: $($_.Exception.Message)" -Level "ERROR" Handle-Error -ErrorRecord $_ throw $_ } } End { Write-EnhancedLog -Message "Exiting Construct-SourceName function" -Level "NOTICE" } } function Initialize-EventLog { <# .SYNOPSIS Initializes the event log by managing logs, validating before and after operations, and managing sources. .DESCRIPTION The Initialize-EventLog function handles the entire process of setting up an event log. It manages the creation or retrieval of logs, validates them before and after operations, and ensures the correct association of event sources. .PARAMETER SourceName The name of the event source to be used. .PARAMETER ParentScriptName The name of the parent script to be used as part of the event log name. .EXAMPLE Initialize-EventLog -SourceName "MySource" -ParentScriptName "MyScript" Initializes an event log with the specified source and parent script name. #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string]$SourceName, [Parameter(Mandatory = $true)] [string]$ParentScriptName ) Begin { Write-EnhancedLog -Message "Starting Initialize-EventLog function" -Level "WARNING" Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters } Process { try { # Step 1: Manage Logs Write-EnhancedLog -Message "Managing event logs." -Level "INFO" try { Write-EnhancedLog -Message "Calling Manage-EventLogs function with ParentScriptName: '$ParentScriptName'." -Level "DEBUG" $global:sanitizedlogName = Manage-EventLogs -ParentScriptName $ParentScriptName Write-EnhancedLog -Message "Manage-EventLogs function returned log name: '$global:sanitizedlogName'." -Level "INFO" } catch { Write-EnhancedLog -Message "An error occurred while managing event logs: $($_.Exception.Message)" -Level "ERROR" throw $_ } $DBG # Step 2: Sanitize the Log Name try { Write-EnhancedLog -Message "Sanitizing the log name returned by Manage-EventLogs." -Level "INFO" $global:sanitizedlogName = Sanitize-LogName -LogName $global:sanitizedlogName Write-EnhancedLog -Message "Sanitized log name: '$global:sanitizedlogName'." -Level "INFO" } catch { Write-EnhancedLog -Message "An error occurred while sanitizing the log name: $($_.Exception.Message)" -Level "ERROR" throw $_ } # Step 3: Construct the source name using the sanitized log name and the caller function try { Write-EnhancedLog -Message "Constructing the source name using the sanitized log name and caller function." -Level "INFO" $global:sanitizedsourceName = Construct-SourceName -LogName $global:sanitizedlogName -CallerFunction $SourceName Write-EnhancedLog -Message "Constructed source name: '$global:sanitizedsourceName'." -Level "INFO" } catch { Write-EnhancedLog -Message "An error occurred while constructing the source name: $($_.Exception.Message)" -Level "ERROR" throw $_ } $DBG # Step 4: Pre-Validation Write-EnhancedLog -Message "Performing pre-validation for log '$global:sanitizedlogName' and source '$global:sanitizedsourceName'." -Level "INFO" try { $validationResult = Validate-EventLog -LogName $global:sanitizedlogName -SourceName $global:sanitizedsourceName Write-EnhancedLog -Message "Pre-validation completed: Log exists = $($validationResult.LogExists), Source exists = $($validationResult.SourceExists)." -Level "INFO" } catch { Write-EnhancedLog -Message "An error occurred during pre-validation: $($_.Exception.Message)" -Level "ERROR" throw $_ } $DBG # Step 5: Manage Sources Write-EnhancedLog -Message "Managing event log sources for log '$global:sanitizedlogName' and source '$global:sanitizedsourceName'." -Level "INFO" try { Manage-EventLogSource -LogName $global:sanitizedlogName -SourceName $global:sanitizedsourceName -ValidationResult $validationResult Write-EnhancedLog -Message "Event log sources managed successfully." -Level "INFO" } catch { Write-EnhancedLog -Message "An error occurred while managing event log sources: $($_.Exception.Message)" -Level "ERROR" throw $_ } $DBG # Step 6: Post-Validation (Optional) Write-EnhancedLog -Message "Performing post-validation for log '$global:sanitizedlogName' and source '$global:sanitizedsourceName'." -Level "INFO" try { Validate-EventLog -LogName $global:sanitizedlogName -SourceName $global:sanitizedsourceName -PostValidation Write-EnhancedLog -Message "Post-validation completed successfully." -Level "INFO" } catch { Write-EnhancedLog -Message "An error occurred during post-validation: $($_.Exception.Message)" -Level "ERROR" throw $_ } } catch { Write-EnhancedLog -Message "An error occurred in Initialize-EventLog function: $($_.Exception.Message)" -Level "ERROR" Handle-Error -ErrorRecord $_ throw $_ } $DBG } End { Write-EnhancedLog -Message "Preparing to exit Initialize-EventLog function. Returning log name: '$global:sanitizedlogName'." -Level "INFO" # Ensure that only the log name string is returned try { if (-not $global:sanitizedlogName) { throw "Log name is null or empty. Cannot return a valid log name." } # Log the type of logName to ensure it's a string if ($global:sanitizedlogName -isnot [string]) { Write-EnhancedLog -Message "Log name is not a string. Actual type: $($global:sanitizedlogName.GetType().Name)" -Level "ERROR" throw "Unexpected type for log name. Expected string." } Write-EnhancedLog -Message "Log name '$global:sanitizedlogName' is valid and will be returned." -Level "INFO" } catch { Write-EnhancedLog -Message "An error occurred while finalizing the log name: $($_.Exception.Message)" -Level "ERROR" throw $_ } $DBG Write-EnhancedLog -Message "Exiting Initialize-EventLog function" -Level "WARNING" return $global:sanitizedlogName } } function Write-LogMessage { <# .SYNOPSIS Writes a log message to the event log with a specified level and event ID. .DESCRIPTION The Write-LogMessage function writes a message to the event log. It maps custom levels to event log entry types and event IDs, determines the calling function, and ensures the event log and source are properly initialized. .PARAMETER Message The message to write to the event log. .PARAMETER Level The level of the message, which determines the event log entry type. Defaults to 'INFO'. .PARAMETER EventID The event ID for the log entry. Defaults to 1000. .EXAMPLE Write-LogMessage -Message "This is a test log message" -Level "ERROR" -EventID 3001 Writes an error message to the event log with event ID 3001. #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string]$Message, [Parameter(Mandatory = $false)] [string]$Level = 'INFO', [Parameter(Mandatory = $false)] [int]$EventID = 1000 ) Begin { Write-EnhancedLog -Message "Starting Write-LogMessage function" -Level "NOTICE" Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters } Process { try { # Get the PowerShell call stack to determine the actual calling function Write-EnhancedLog -Message "Retrieving the PowerShell call stack to determine the caller function." -Level "DEBUG" $callStack = Get-PSCallStack $callerFunction = if ($callStack.Count -ge 2) { $callStack[1].Command } else { '<Unknown>' } Write-EnhancedLog -Message "Caller function identified as '$callerFunction'." -Level "INFO" # Map custom levels to event log entry types and event IDs Write-EnhancedLog -Message "Mapping log level '$Level' to entry type and event ID." -Level "INFO" $entryType, $mappedEventID = switch ($Level.ToUpper()) { 'DEBUG' { 'Information', 1001 } 'INFO' { 'Information', 1002 } 'NOTICE' { 'Information', 1003 } 'WARNING' { 'Warning', 2001 } 'ERROR' { 'Error', 3001 } 'CRITICAL' { 'Error', 3002 } 'IMPORTANT' { 'Information', 1004 } 'OUTPUT' { 'Information', 1005 } 'SIGNIFICANT' { 'Information', 1006 } 'VERYVERBOSE' { 'Information', 1007 } 'VERBOSE' { 'Information', 1008 } 'SOMEWHATVERBOSE' { 'Information', 1009 } 'SYSTEM' { 'Information', 1010 } 'INTERNALCOMMENT' { 'Information', 1011 } default { 'Information', 9999 } } Write-EnhancedLog -Message "Log level '$Level' mapped to entry type '$entryType' and event ID '$mappedEventID'." -Level "INFO" # Use provided EventID if specified, otherwise use mapped EventID $eventID = if ($PSCmdlet.MyInvocation.BoundParameters.ContainsKey('EventID')) { $EventID } else { $mappedEventID } Write-EnhancedLog -Message "Using Event ID: $eventID" -Level "INFO" # Ensure the event log and source are initialized Write-EnhancedLog -Message "Initializing event log for source '$callerFunction'." -Level "INFO" $DBG try { Write-EnhancedLog -Message "Calling Initialize-EventLog function with SourceName: '$callerFunction'." -Level "INFO" $global:sanitizedlogName = Initialize-EventLog -SourceName $callerFunction -ParentScriptName $ParentScriptName Write-EnhancedLog -Message "Event log initialized with name '$global:sanitizedlogName'." -Level "INFO" # Sanitize the log name Write-EnhancedLog -Message "Sanitizing the log name for source '$callerFunction'." -Level "INFO" $global:sanitizedlogName = Sanitize-LogName -LogName $global:sanitizedlogName Write-EnhancedLog -Message "Sanitized log name: '$global:sanitizedlogName'." -Level "INFO" $DBG } catch { Write-EnhancedLog -Message "Failed to initialize or sanitize event log for source '$callerFunction': $($_.Exception.Message)" -Level "ERROR" Handle-Error -ErrorRecord $_ throw $_ } $DBG # Validate the log name before writing to the event log if (-not $global:sanitizedlogName -or $global:sanitizedlogName -is [System.Collections.Hashtable]) { Write-EnhancedLog -Message "The log name '$global:sanitizedlogName' is invalid or is a hashtable. Halting operation." -Level "CRITICAL" throw "Invalid log name: '$global:sanitizedlogName'" } $DBG # Write the message to the specified event log try { Write-EnhancedLog -Message "Validating if the event log source '$global:sanitizedsourceName' exists in log '$global:sanitizedlogName'." -Level "INFO" # Check if the source exists and count occurrences try { # Check if the source exists and count occurrences Write-EnhancedLog -Message "Checking if event log source '$global:sanitizedsourceName' exists in log '$global:sanitizedlogName'." -Level "INFO" $sourceExists = [System.Diagnostics.EventLog]::SourceExists("$global:sanitizedsourceName") if ($sourceExists) { Write-EnhancedLog -Message "Event log source '$global:sanitizedsourceName' exists. Retrieving log name associated with the source." -Level "INFO" try { $sourceLogName = [System.Diagnostics.EventLog]::LogNameFromSourceName("$global:sanitizedsourceName", ".") Write-EnhancedLog -Message "Associated log name retrieved: '$sourceLogName'." -Level "INFO" try { # Count occurrences of the source in the log $sourceCount = @(Get-EventLog -LogName $sourceLogName -Source "$global:sanitizedsourceName").Count if ($sourceCount -eq 0) { Write-EnhancedLog -Message "No previous events found for source '$global:sanitizedsourceName' in log '$sourceLogName'. This is the first event being logged for this source." -Level "INFO" } else { Write-EnhancedLog -Message "Number of occurrences for source '$global:sanitizedsourceName' in log '$sourceLogName': $sourceCount." -Level "INFO" } } catch { Write-EnhancedLog -Message "Failed to count occurrences of source '$global:sanitizedsourceName' in log '$sourceLogName': $($_.Exception.Message)" -Level "ERROR" # Additional enhanced error handling for specific scenarios if ($_.Exception.Message -match "No matches found") { Write-EnhancedLog -Message "This is expected behavior if the log source '$global:sanitizedsourceName' is being used for the first time. No prior events are logged under this source." -Level "NOTICE" } else { throw $_ # Re-throw the error for further handling upstream if it's not an expected case. } } } catch { Write-EnhancedLog -Message "Failed to retrieve the log name associated with the source '$global:sanitizedsourceName': $($_.Exception.Message)" -Level "ERROR" throw $_ } } else { Write-EnhancedLog -Message "Event log source '$global:sanitizedsourceName' does not exist in log '$global:sanitizedlogName'. Proceeding to create it." -Level "WARNING" } } catch { Write-EnhancedLog -Message "An error occurred while checking or counting occurrences of the event log source '$global:sanitizedsourceName': $($_.Exception.Message)" -Level "ERROR" Handle-Error -ErrorRecord $_ throw $_ } $DBG Write-EnhancedLog -Message "Writing log message to event log '$global:sanitizedlogName' for source '$global:sanitizedsourceName'." -Level "INFO" Write-EventLog -LogName $global:sanitizedlogName -Source "$global:sanitizedsourceName" -EntryType $entryType -EventId $eventID -Message $Message Write-Host "Logged message to '$global:sanitizedlogName' from source '$global:sanitizedsourceName'" } catch { Write-EnhancedLog -Message "Failed to write log message to event log '$global:sanitizedlogName' for source '$global:sanitizedsourceName': $($_.Exception.Message)" -Level "ERROR" throw $_ } $DBG } catch { Write-EnhancedLog -Message "An error occurred in Write-LogMessage function: $($_.Exception.Message)" -Level "ERROR" Handle-Error -ErrorRecord $_ throw $_ } } End { Write-EnhancedLog -Message "Exiting Write-LogMessage function" -Level "NOTICE" } } #EndRegion '.\Public\Write-LogMessage.ps1' 851 |