$FilterHashTable = @{
LogName = 'Application'
ID = 1534
StartTime = (Get-Date).AddHours(-1)
EndTime = Get-Date
}
$Events = Get-WinEvent -FilterHashtable $FilterHashTable | ForEach-Object {
$Values = $_.Properties | ForEach-Object { $_.Value }
# return a new object with the required information
[PSCustomObject]@{
Time = $_.TimeCreated
# index 0 contains the name of the update
Event = $Values[0]
Component = $Values[1]
Error = $Values[2]
User = $_.UserId.Value
}
}
$Events | Format-Table -AutoSize
The trick is related to how you can read hidden properties from Event, but for this post, we're carrying only about the use of ForEach-Object and how it's used in this case. It basically uses Get-WinEvent to get events from event log and as they show up it passes it thru pipeline creating PSCustomObject, finally saving it in $Events variable. Seems easy enough. But when I've run this on my own functions I was getting strange results.
$FilterHashTable = @{
LogName = 'Security'
ID = 4625
}
Write-Color 'Get-WinEvent', ' - ', ' ForEach' -Color Yellow, White, Green
$WinEvent = Get-WinEvent -FilterHashtable $FilterHashTable -MaxEvents 3 -ComputerName 'AD1.AD.EVOTEC.XYZ'
foreach ($Event in $WinEvent) {
$Event.TimeCreated.Count
$Event.ProviderName.Count
}
Write-Color 'Get-WinEvent', ' - ', ' ForEach-Object' -Color Yellow, White, Green
Get-WinEvent -FilterHashtable $FilterHashTable -MaxEvents 3 -ComputerName 'AD1.AD.EVOTEC.XYZ' | ForEach-Object {
$_.TimeCreated.Count
$_.ProviderName.Count
}
The code above is a simplified version for testing behavior of ForEach-Object and ForEach. I asked for three events from Event Log and I'm simply checking Count for two variables.
$MyData = @{
LogName = 'Security'
ID = 4625
Machine = 'AD1.ad.evotec.xyz'
MaxEvents = 3
}
Write-Color 'Get-Events', ' - ', ' ForEach' -Color Yellow, White, Green
$Events = Get-Events @MyData
foreach ($Event in $Events) {
$Event.TimeCreated.Count
$Event.ProviderName.Count
}
Write-Color 'Get-Events', ' - ', ' ForEach-Object' -Color Yellow, White, Green
Get-Events @MyData | ForEach-Object {
$_.TimeCreated.Count
$_.ProviderName.Count
}
For a moment I thought that maybe my PowerShell foo is failing me and I just don't know how to use ForEach properly but it seems it's related to comma operator that I was so happy to brag about earlier on. To confirm my suspicion about comma I've decided to use my old example.
function Show-ThirdExample {
param(
[string[]] $Test
)
[Array] $Output = foreach ($my in $Test) {
$my
}
# I want to do something with value before returning
if ($Output -is [array]) {
Write-Color 'Array' -Color Green
}
# Actually returning
, $Output
}
function Show-ThirdExample {
param(
[string[]] $Test
)
[Array] $Output = foreach ($my in $Test) {
$my
}
# I want to do something with value before returning
if ($Output -is [array]) {
Write-Color 'Array' -Color Green
}
# Actually returning
, $Output
}
Write-Color 'Example - ForEach (two elements)' -Color Cyan
$Value1 = Show-ThirdExample -Test 'one', 'two'
foreach ($Value in $Value1) {
$Value.Count
}
Write-Color 'Example - Foreach (one element)' -Color Cyan
$Value2 = Show-ThirdExample -Test 'one'
foreach ($Value in $Value2) {
$Value.Count
}
Write-Color 'Example - ForEach Object' -Color Cyan
Show-ThirdExample -Test 'one', 'two' | ForEach-Object {
$_.Count
}
Testing my idea proves that for some reason (unknown to me) using a comma to preserve Array is making ForEach-Object unusable. ForEach works just fine. While I can continue using it that way, most likely someone will use ForEach-Object and will get wrong results. So how do I fix it?
function Show-ThirdExample {
param(
[string[]] $Test
)
[Array] $Output = foreach ($my in $Test) {
$my
}
# I want to do something with value before returning
if ($Output -is [array]) {
Write-Color 'Array' -Color Green
}
# Actually returning
@($Output)
}
Write-Color 'Example - ForEach (two elements)' -Color Cyan
$Value1 = Show-ThirdExample -Test 'one', 'two'
foreach ($Value in $Value1) {
$Value.Count
}
Write-Color 'Example - Foreach (one element)' -Color Cyan
$Value2 = Show-ThirdExample -Test 'one'
foreach ($Value in $Value2) {
$Value.Count
}
Write-Color 'Example - ForEach Object' -Color Cyan
Show-ThirdExample -Test 'one', 'two' | ForEach-Object {
$_.Count
}
As you can see above, this gives proper result when it comes to ForEach-Object vs. ForEach. But if we will check what is returned it isn't Array for one element anymore.