add: Logging functions for future use

This commit is contained in:
DrIOS
2024-05-30 16:19:05 -05:00
parent 3f4166e98a
commit 8301b8cdbe
4 changed files with 288 additions and 0 deletions

View File

@@ -0,0 +1,22 @@
function Test-IsAdmin {
<#
.SYNOPSIS
Checks if the current user is an administrator on the machine.
.DESCRIPTION
This private function returns a Boolean value indicating whether
the current user has administrator privileges on the machine.
It does this by creating a new WindowsPrincipal object, passing
in a WindowsIdentity object representing the current user, and
then checking if that principal is in the Administrator role.
.INPUTS
None.
.OUTPUTS
Boolean. Returns True if the current user is an administrator, and False otherwise.
.EXAMPLE
PS C:\> Test-IsAdmin
True
#>
# Create a new WindowsPrincipal object for the current user and check if it is in the Administrator role
(New-Object Security.Principal.WindowsPrincipal ([Security.Principal.WindowsIdentity]::GetCurrent())).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
}

View File

@@ -0,0 +1,212 @@
function Write-AuditLog {
<#
.SYNOPSIS
Writes log messages to the console and updates the script-wide log variable.
.DESCRIPTION
The Write-AuditLog function writes log messages to the console based on the severity (Verbose, Warning, or Error) and updates
the script-wide log variable ($script:LogString) with the log entry. You can use the Start, End, and EndFunction switches to
manage the lifecycle of the logging.
.INPUTS
System.String
You can pipe a string to the Write-AuditLog function as the Message parameter.
You can also pipe an object with a Severity property as the Severity parameter.
.OUTPUTS
None
The Write-AuditLog function doesn't output any objects to the pipeline. It writes messages to the console and updates the
script-wide log variable ($script:LogString).
.PARAMETER BeginFunction
Sets the message to "Begin [FunctionName] function log.", where FunctionName is the name of the calling function, and adds it to the log variable.
.PARAMETER Message
The message string to log.
.PARAMETER Severity
The severity of the log message. Accepted values are 'Information', 'Warning', and 'Error'. Defaults to 'Information'.
.PARAMETER Start
Initializes the script-wide log variable and sets the message to "Begin [FunctionName] Log.", where FunctionName is the name of the calling function.
.PARAMETER End
Sets the message to "End Log" and exports the log to a CSV file if the OutputPath parameter is provided.
.PARAMETER EndFunction
Sets the message to "End [FunctionName] log.", where FunctionName is the name of the calling function, and adds it to the log variable.
.PARAMETER OutputPath
The file path for exporting the log to a CSV file when using the End switch.
.EXAMPLE
Write-AuditLog -Message "This is a test message."
Writes a test message with the default severity (Information) to the console and adds it to the log variable.
.EXAMPLE
Write-AuditLog -Message "This is a warning message." -Severity "Warning"
Writes a warning message to the console and adds it to the log variable.
.EXAMPLE
Write-AuditLog -Start
Initializes the log variable and sets the message to "Begin [FunctionName] Log.", where FunctionName is the name of the calling function.
.EXAMPLE
Write-AuditLog -BeginFunction
Sets the message to "Begin [FunctionName] function log.", where FunctionName is the name of the calling function, and adds it to the log variable.
.EXAMPLE
Write-AuditLog -EndFunction
Sets the message to "End [FunctionName] log.", where FunctionName is the name of the calling function, and adds it to the log variable.
.EXAMPLE
Write-AuditLog -End -OutputPath "C:\Logs\auditlog.csv"
Sets the message to "End Log", adds it to the log variable, and exports the log to a CSV file.
.NOTES
Author: DrIOSx
#>
[CmdletBinding(DefaultParameterSetName = 'Default')]
param(
###
[Parameter(
Mandatory = $false,
HelpMessage = 'Input a Message string.',
Position = 0,
ParameterSetName = 'Default',
ValueFromPipeline = $true
)]
[ValidateNotNullOrEmpty()]
[string]$Message,
###
[Parameter(
Mandatory = $false,
HelpMessage = 'Information, Warning or Error.',
Position = 1,
ParameterSetName = 'Default',
ValueFromPipelineByPropertyName = $true
)]
[ValidateNotNullOrEmpty()]
[ValidateSet('Information', 'Warning', 'Error')]
[string]$Severity = 'Information',
###
[Parameter(
Mandatory = $false,
ParameterSetName = 'End'
)]
[switch]$End,
###
[Parameter(
Mandatory = $false,
ParameterSetName = 'BeginFunction'
)]
[switch]$BeginFunction,
[Parameter(
Mandatory = $false,
ParameterSetName = 'EndFunction'
)]
[switch]$EndFunction,
###
[Parameter(
Mandatory = $false,
ParameterSetName = 'Start'
)]
[switch]$Start,
###
[Parameter(
Mandatory = $false,
ParameterSetName = 'End'
)]
[string]$OutputPath
)
begin {
$ErrorActionPreference = "SilentlyContinue"
# Define variables to hold information about the command that was invoked.
$ModuleName = $Script:MyInvocation.MyCommand.Name -replace '\..*'
$callStack = Get-PSCallStack
if ($callStack.Count -gt 1) {
$FuncName = $callStack[1].Command
} else {
$FuncName = "DirectCall" # Or any other default name you prefer
}
#Write-Verbose "Funcname Name is $FuncName!" -Verbose
$ModuleVer = $MyInvocation.MyCommand.Version.ToString()
# Set the error action preference to continue.
$ErrorActionPreference = "Continue"
}
process {
try {
if (-not $Start -and -not (Test-Path variable:script:LogString)) {
throw "The logging variable is not initialized. Please call Write-AuditLog with the -Start switch or ensure $script:LogString is set."
}
$Function = $($FuncName + '.v' + $ModuleVer)
if ($Start) {
$script:LogString = @()
$Message = '+++ Begin Log | ' + $Function + ' |'
}
elseif ($BeginFunction) {
$Message = '>>> Begin Function Log | ' + $Function + ' |'
}
$logEntry = [pscustomobject]@{
Time = ((Get-Date).ToString('yyyy-MM-dd hh:mmTss'))
Module = $ModuleName
PSVersion = ($PSVersionTable.PSVersion).ToString()
PSEdition = ($PSVersionTable.PSEdition).ToString()
IsAdmin = $(Test-IsAdmin)
User = "$Env:USERDOMAIN\$Env:USERNAME"
HostName = $Env:COMPUTERNAME
InvokedBy = $Function
Severity = $Severity
Message = $Message
RunID = -1
}
if ($BeginFunction) {
$maxRunID = ($script:LogString | Where-Object { $_.InvokedBy -eq $Function } | Measure-Object -Property RunID -Maximum).Maximum
if ($null -eq $maxRunID) { $maxRunID = -1 }
$logEntry.RunID = $maxRunID + 1
}
else {
$lastRunID = ($script:LogString | Where-Object { $_.InvokedBy -eq $Function } | Select-Object -Last 1).RunID
if ($null -eq $lastRunID) { $lastRunID = 0 }
$logEntry.RunID = $lastRunID
}
if ($EndFunction) {
$FunctionStart = "$((($script:LogString | Where-Object {$_.InvokedBy -eq $Function -and $_.RunId -eq $lastRunID } | Sort-Object Time)[0]).Time)"
$startTime = ([DateTime]::ParseExact("$FunctionStart", 'yyyy-MM-dd hh:mmTss', $null))
$endTime = Get-Date
$timeTaken = $endTime - $startTime
$Message = '<<< End Function Log | ' + $Function + ' | Runtime: ' + "$($timeTaken.Minutes) min $($timeTaken.Seconds) sec"
$logEntry.Message = $Message
}
elseif ($End) {
$startTime = ([DateTime]::ParseExact($($script:LogString[0].Time), 'yyyy-MM-dd hh:mmTss', $null))
$endTime = Get-Date
$timeTaken = $endTime - $startTime
$Message = '--- End Log | ' + $Function + ' | Runtime: ' + "$($timeTaken.Minutes) min $($timeTaken.Seconds) sec"
$logEntry.Message = $Message
}
$script:LogString += $logEntry
switch ($Severity) {
'Warning' {
Write-Warning ('[WARNING] ! ' + $Message)
$UserInput = Read-Host "Warning encountered! Do you want to continue? (Y/N)"
if ($UserInput -eq 'N') {
throw "Script execution stopped by user."
}
}
'Error' { Write-Error ('[ERROR] X - ' + $FuncName + ' ' + $Message) -ErrorAction Continue }
'Verbose' { Write-Verbose ('[VERBOSE] ~ ' + $Message) }
Default { Write-Information ('[INFO] * ' + $Message) -InformationAction Continue}
}
}
catch {
throw "Write-AuditLog encountered an error (process block): $($_)"
}
}
end {
try {
if ($End) {
if (-not [string]::IsNullOrEmpty($OutputPath)) {
$script:LogString | Export-Csv -Path $OutputPath -NoTypeInformation
Write-Verbose "LogPath: $(Split-Path -Path $OutputPath -Parent)"
}
else {
throw "OutputPath is not specified for End action."
}
}
}
catch {
throw "Error in Write-AuditLog (end block): $($_.Exception.Message)"
}
}
}

View File

@@ -0,0 +1,27 @@
$ProjectPath = "$PSScriptRoot\..\..\.." | Convert-Path
$ProjectName = ((Get-ChildItem -Path $ProjectPath\*\*.psd1).Where{
($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and
$(try { Test-ModuleManifest $_.FullName -ErrorAction Stop } catch { $false } )
}).BaseName
Import-Module $ProjectName
InModuleScope $ProjectName {
Describe Get-PrivateFunction {
Context 'Default' {
BeforeEach {
$return = Get-PrivateFunction -PrivateData 'string'
}
It 'Returns a single object' {
($return | Measure-Object).Count | Should -Be 1
}
It 'Returns a string based on the parameter PrivateData' {
$return | Should -Be 'string'
}
}
}
}

View File

@@ -0,0 +1,27 @@
$ProjectPath = "$PSScriptRoot\..\..\.." | Convert-Path
$ProjectName = ((Get-ChildItem -Path $ProjectPath\*\*.psd1).Where{
($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and
$(try { Test-ModuleManifest $_.FullName -ErrorAction Stop } catch { $false } )
}).BaseName
Import-Module $ProjectName
InModuleScope $ProjectName {
Describe Get-PrivateFunction {
Context 'Default' {
BeforeEach {
$return = Get-PrivateFunction -PrivateData 'string'
}
It 'Returns a single object' {
($return | Measure-Object).Count | Should -Be 1
}
It 'Returns a string based on the parameter PrivateData' {
$return | Should -Be 'string'
}
}
}
}