I maintain a little module of CLI tools for my team at work and have a behavior i can’t account for.
I keep separate .ps1 files for all function definitions, and then “build” them in to a monolithic .psm1 file before publishing the module.
I say this because when I’m writing and testing the function, it’s a single file and I redefine the function in my terminal (VS Code) whenever there is a change and test.
I’ve been doing error handling like this quite a bit:
try {
#some code
} catch {
Write-Warning $Error[0]
}
When I do this during development I get what I wanted:
However, the same circumstances leading to the error behave differently once it’s in the module.
When this has happened in the past I know i’ve gone back and tried changing $Error[0]
to $Error[0].Message
or something like that, and in testing it works, but then in the module it might occur again. Most of the time the errors are few and far between so it’s hard to test.
But this recent function, Get-LockEvent, will always generate an error if the target computer doesn’t have any Event ID 4800 or 4801 in the Security log.
My question is: what’s happening with the automatic $Error variable when my code is executing from my module instead of a single function definition?
Here is the full function definition i’m working with:
function Get-LockEvent {
<#
.SYNOPSIS
Retrieve logs from Windows Events pertaining to computer lock and unlock.
.DESCRIPTION
Easier to remember wrapper for Get-WinEvent and a filterhashtable for getting event ID 4800 and 4801 from the Windows Security log.
.PARAMETER ComputerName
Can specify a remote computer to pull logs from
.PARAMETER OutputType
By default EventLogRecord objects are returned from Get-WinEvent. You can specify PSObject for output type and the event records will be converted to XML, and then turned in
to Powershell objects from there for easier reading/exporting/filtering.
.PARAMETER TimeFrame
By default it will filter for events occuring today. You can specify "All", "Today" or "LastHour" and Get-WinEvent will filter accordingly.
#>
[cmdletbinding()]
Param (
[Parameter(Mandatory=$false,Position=0)]
[String]$ComputerName,
[Parameter(Mandatory=$false,Position=1)]
[ValidateSet("Default","PSObject")]
[String]$OutputType = "Default",
[Parameter(Mandatory=$false,Position=2)]
[ValidateSet("All","Today","LastHour")]
[String]$TimeFrame = "Today"
)
$StartTime = switch ($TimeFrame) {
"Today" { Get-Date $((Get-Date).ToShortDateString()) }
"All" { $false }
"LastHour" { (Get-Date).AddHours(-1) }
}
$EventHT = @{
FilterHashTable = @{
LogName = "Security"
Id = 4800,4801
}
ErrorAction = "Stop"
}
if ($StartTime) {
$EventHT.FilterHashTable.Add("StartTime",$StartTime)
}
if ($ComputerName) {
$EventHT.Add("ComputerName",$ComputerName)
}
try {
switch ($OutputType) {
"Default" {
Get-WinEvent @EventHT
}
"PSObject" {
$Events = Get-WinEvent @EventHT
Foreach ($Event in $Events) {
$XmlEvent = [xml]$Event.ToXml()
$EventType = switch ($XmlEvent.Event.System.EventID) {
"4800" {"Locked"}
"4801" {"Unlocked"}
}
[PSCustomObject]@{
PSTypeName = "LockEvent"
DateTime = Get-Date $XmlEvent.event.System.timecreated.systemtime -Format 'MM/dd/yy HH:mm:ss'
EventID = $XmlEvent.Event.System.EventID
EventType = $EventType
TargetUserSID = $XmlEvent.Event.EventData.Data.'#text'[0]
TargetUserName = $XmlEvent.Event.EventData.Data.'#text'[1]
TargetDomainName = $XmlEvent.Event.EventData.Data.'#text'[2]
SessionId = $XmlEvent.Event.EventData.Data.'#text'[4]
}
}
}
}
} catch {
Write-Warning $Error[0]
}
}