SimpleEventLogging.psm1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
<#
.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');