SimpleEventLogging.psm1

<#
.SYNOPSIS
Collection of event logging functions and syntactic helpers.
 
.DESCRIPTION
Uses the legacy API to log Windows events, so is limited to the "Classic" event channels.
Events are not schematized and payload is logged as a single event string value.
 
 
 
Useful for adding quick & dirty logging.
 
.NOTES
Writes event data to screen as well.
If no host console is available, the write-host is disabled.
 
Initialization errors are logged to the Microsoft-Windows-PowerShell/Operational channel using existing eventIDs (4100,4101,4102)
 
#>

function Write-InformationEvent {
<#
.SYNOPSIS
Write-InformationEvent writes an informational event to the local event log and local screen
.DESCRIPTION
Writes a simple text message (not schematized) Informational level event to
the local Application log using the global outputEventSource.
 
If the global variable instanceGuid is populated it will prefix the event
message.
 
.PARAMETER EventID
A mandatory parameter, this is the EventID for the event.
 
.PARAMETER Message
An optional parameter (but really you should put something here) is the
text message to be logged.
.EXAMPLE
Write-InformationEvent 100 "Yoo hoo!"
#>

Param
(
        [Parameter(Mandatory=$true)]
        [UInt16]$EventID,
        [Parameter(Mandatory=$false)]
        [string]$Message
)

    # check if global eventlogging variable is set to true (ok to write to eventlog)
    if($Script:EventLoggingEnabled -eq $true)
    {
        # for script runtime instance logging (doesn't have to be a guid, just something unique per script invokation)
        if ($Script:instanceGuid -ne $null)
        {
            Write-EventLog -LogName $Script:eventLogName -Source $Script:eventLogSource -EntryType Information -EventId $EventID -Message "Instance:$($Script:instanceGuid)`n$($Message)";

        }
        else
        {
            Write-EventLog -LogName $Script:eventLogName -Source $Script:eventLogSource -EntryType Information -EventId $EventID -Message $Message;
        }
    }
    if ($Script:WriteHostEnabled) {
        Write-Host -ForegroundColor Green "$((get-date).ToString("s")):Informational:$($EventID):$($Message)";
    }

} # function Write-InformationEvent

function Write-WarningEvent {
<#
.SYNOPSIS
Write-WarningEvent writes an Warning event to the local event log
.DESCRIPTION
Writes a simple text message (not schematized) Warning level event to
the local Application log using the global outputEventSource.
 
If the global variable instanceGuid is populated it will prefix the event
message.
 
.PARAMETER EventID
A mandatory parameter, this is the EventID for the event.
 
.PARAMETER Message
An optional parameter (but really you should put something here) is the
text message to be logged.
.EXAMPLE
Write-WarningEvent 200 "Uh oh!"
#>

Param(
    [Parameter(Mandatory=$true)]
        [UInt16]$EventID,
        [Parameter(Mandatory=$false)]
        [string]$Message
)
    # check if global eventlogging variable is set to true (ok to write to eventlog)
    if($Script:EventLoggingEnabled -eq $true)
    {
        # for script runtime instance logging (doesn't have to be a guid, just something unique per script invokation)
        if ($Script:instanceGuid -ne $null)
        {
            Write-EventLog -LogName $Script:eventLogName -Source $Script:eventLogSource -EntryType Warning -EventId $EventID -Message "Instance:$($Script:instanceGuid)`n$($Message)";
        }
        else
        {
            Write-EventLog -LogName $Script:eventLogName -Source $Script:eventLogSource -EntryType Warning -EventId $EventID -Message $Message;

        }
    }
    if ($Script:WriteHostEnabled) {
        Write-Host -ForegroundColor Yellow "$((get-date).ToString("s")):Warning:$($EventID):$($Message)";
    }
} # function Write-WarningEvent

function Write-ErrorEvent {
<#
.SYNOPSIS
Write-ErrorEvent writes an Error event to the local event log
.DESCRIPTION
Writes a simple text message (not schematized) Error level event to
the local Application log using the global outputEventSource.
 
If the global variable instanceGuid is populated it will prefix the event
message.
 
.PARAMETER EventID
A mandatory parameter, this is the EventID for the event.
 
.PARAMETER Message
An optional parameter (but really you should put something here) is the
text message to be logged.
.EXAMPLE
Write-ErrorEvent 300 "Better bring a bucket!"
#>

Param(
    [Parameter(Mandatory=$true)]
        [UInt16]$EventID,
        [Parameter(Mandatory=$false)]
        [string]$Message
)
    # check if global eventlogging variable is set to true (ok to write to eventlog)
    if($Script:EventLoggingEnabled -eq $true)
    {
        # for script runtime instance logging (doesn't have to be a guid, just something unique per script invokation)
        if ($Script:instanceGuid -ne $null)
        {
            Write-EventLog -LogName $Script:eventLogName -Source $Script:eventLogSource -EntryType Error -EventId $EventID -Message "Instance:$($Script:instanceGuid)`n$($Message)";
        }
        else
        {
            Write-EventLog -LogName $Script:eventLogName -Source $Script:eventLogSource -EntryType Error -EventId $EventID -Message $Message;
        }
    }
    if ($Script:WriteHostEnabled) {
        Write-Host -ForegroundColor Red "$((get-date).ToString("s")):Error:$($EventID):$($Message)";
    }
} # function Write-ErrorEvent

function Test-eventlogStatus {
<#
.SYNOPSIS
Checks whether Application event logging can be performed under the supplied event source.
.DESCRIPTION
Checks if the supplied event source exists, if so, sets the Event log flag
to true and returns.
 
If the event source does not exist and the script is run with Administrator
privileges, the event source is created. Then the Event log flag is set to true
and the script returns.
 
Otherwise, the Event log flag is set to false, meaning no event log events.
 
All messages are echoed to console as well, regardless of flag if an output host is available.
 
.PARAMETER EventSource
A mandatory parameter, this is the event source name that will be
checked and created if needed. This cannot be null or empty.
 
.PARAMETER CreateInstanceGuid
[switch] if set, events will be logged with a GUID value indicating a specific instance, to help differentiate two instances of the same script running.
 
.EXAMPLE
Test-EventLogStatus -EventSource "ThisIsMyEventSource" -CreateInstanceGuid
 
#>

Param(
    [Parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [string]$EventSource,
        [switch]$CreateInstanceGuid
)
    $Script:eventLogSource = $EventSource;

    [bool]$eventlogSourceExists = $false;
    if($CreateInstanceGuid) {
        $Script:InstanceGuid = ([Guid]::NewGuid()).ToString();
    } else {
        $Script:InstanceGuid = $null;
    }

    try {
    # check if the event source exists (for any event channel - this is a legacy event source, not a modern event provider)
        $eventlogSourceExists = [System.Diagnostics.EventLog]::SourceExists($EventSource);
    } catch {
        ## hijack an existing event and provide warning details there.
        New-WinEvent -ProviderName 'Microsoft-Windows-PowerShell' -id 4100 -Payload("SimpleEventLogging Module: function Test-eventlogStatus","Not enough permissions to check event log sources. Please re-run script with local admin credentials.","Exception Message:$_");
        if ($Script:WriteHostEnabled) {
            Write-Host -ForegroundColor Red "Not enough permissions to check event log sources. Please re-run script with local admin credentials - See Microsoft-Windows-Powershell/Operational channel EventID 4100 for more details..";
        }
    }

    # If the check returned false, time to create the event source.
    if($false -eq $eventlogSourceExists)
    {
        # event source does not exist - need to create it.
        # but first,
        # Administrator level check - cannot create an event source without Admin level permissions
        # this gets the current windows identity/login-session and checks if it is in the built-in role Administrator.
        if ($true -eq ([System.Security.Principal.WindowsPrincipal] [System.Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator))
        {
            try # keep this scoped to just new-eventlog to avoid confusion
            {
                New-EventLog -LogName $Script:eventLogName -Source $EventSource;
                $Script:eventLogSource = $EventSource;
                # sweet! set the logging flag to true and return
                $Script:EventLoggingEnabled = $true;
                if ($Script:WriteHostEnabled) {
                    Write-Host -ForegroundColor Green "Successfully created event source:$($EventSource)";
                }
                New-WinEvent -ProviderName 'Microsoft-Windows-PowerShell' -id 4101 -Payload("SimpleEventLogging Module: function Test-eventlogStatus","Successfully created event source:$($EventSource)","Success Status");
                return;
            }
            catch
            {
                if ($Script:WriteHostEnabled) {
                    # new-eventlog is the culprit above, can't even log about it!
                    Write-Host -ForegroundColor Red "Exception caught attempting to create event source: $EventSource";
                    Write-Host -ForegroundColor Red  "Exception Message:$_";
                }
                New-WinEvent -ProviderName 'Microsoft-Windows-PowerShell' -id 4100 -Payload("SimpleEventLogging Module: function Test-eventlogStatus","Exception caught attempting to create event source:$($EventSource)","Exception Message:$_");
                # <sadPanda> no events for us, something went wrong.
                $Script:EventLoggingEnabled = $false;
                return;
            }
        }
        else
        {
            # source does't exist and not enough permissions to create it.
            # set logging flag to false.
            if ($Script:WriteHostEnabled) {
                Write-Host -ForegroundColor Red "Event Logging is disabled because required event source does not exist and user running script does not have permissions to create it";
            }
            New-WinEvent -ProviderName 'Microsoft-Windows-PowerShell' -id 4102 -Payload("SimpleEventLogging Module: function Test-eventlogStatus","Event Logging is disabled because required event source does not exist and user running script does not have permissions to create it","Warning Status");
            $Script:EventLoggingEnabled = $false;
            return;
        }
    }
    else
    {
        # Woot! Event source exists, set flag and no other changes needed.
        $Script:EventLoggingEnabled = $true;
        if ($Script:WriteHostEnabled) {
            Write-Host -ForegroundColor Green "Event Source:$($EventSource) already exists!";
        }
        New-WinEvent -ProviderName 'Microsoft-Windows-PowerShell' -id 4100 -Payload("SimpleEventLogging Module: function Test-eventlogStatus","Event Source:$($EventSource) already exists!","Success Status");
    }
} # function Test-eventlogStatus

## definitions for the script to function properly with set-strictmode enabled.
[bool]$Script:EventLoggingEnabled = $false;
[bool]$Script:WriteHostEnabled = $false;
$Script:eventLogSource = [String]::Empty;
$Script:eventLogName = "Application";

## Script initialization - test whether Write-Host is possible or not.
try {
    Write-Host -NoNewline "";
    $Script:WriteHostEnabled = $true;
}
catch {
    ## no output host available, log and disable the WriteHost commands.
    New-WinEvent -ProviderName 'Microsoft-Windows-PowerShell' -id 4100 -Payload("SimpleEventLogging Module: function Test-eventlogStatus","Disabling Write-Host output. Exception:$($_)","Warning Status");
    $Script:WriteHostEnabled = $false;
}

Export-ModuleMember -Function @('Write-InformationEvent','Write-WarningEvent','Write-ErrorEvent','Test-EventLogStatus');
Export-ModuleMember -Cmdlet @('Write-InformationEvent','Write-WarningEvent','Write-ErrorEvent','Test-EventLogStatus');