0

I'm trying to group an array of objects, but I have to group based on an enumerated Properties property, and I can't figure it out.

Quick explanation: Windows security log, event id 4624, tracks logons. I want to get the last time each user logged into the same computer in the past 30 days. I can grab this through Get-WinEvent based on past 30 days:

$DateAfter = (Get-Date).AddDays(-29)
$DateBefore = (Get-Date)
$WinEvents = Get-WinEvent -FilterHashtable @{ LogName = 'Security'; Id = 4624; StartTime = $DateAfter; EndTime = $DateBefore }

$WinEvents becomes an array of System.Diagnostics.Eventing.Reader.EventRecord(s).

I want to group by username (e.g. $WinEvents[0].Properties[5].Value) and get the last time created (e.g. $WinEvents[0].TimeCreated)

A similar issue was answered here: Get latest dates from an array per object group in Powershell but I can't figure out how to manipulate the array in the same fashion as I'm getting tripped up with the need to get username through Properties enumeration

I could run this (e.g.):

foreach ($evt in $WinEvents) { $evt.Properties[5].Value, $evt.TimeCreated -join " " }

and I'll get something like this to review:

PC1$ 6/11/2020 11:39:54 AM
jjones 6/11/2020 11:39:23 AM
PC2$ 6/11/2020 11:39:10 AM
bsmith 6/11/2020 11:37:53 AM
SYSTEM 6/11/2020 11:37:40 AM
bsmith 6/11/2020 11:37:23 AM
PC1$ 6/11/2020 11:35:52 AM [...]

I'm stuck fundamentally on how to pull these same username and timestamp values into their own array for the grouping.

Full disclosure: My ultimate goal is to clear out local profiles of folks who haven't logged onto the local machine in 30 days (https://adamtheautomator.com/powershell-delete-user-profile/). Delprof2 works great in many cases, but we're seeing several systems where ntuser.dat and ntuser.ini are not accurate, hence the need to sift through the event logs. I'm using Powershell 7 just for futureproofing.

halfer
  • 19,824
  • 17
  • 99
  • 186
tb1
  • 1,340
  • 12
  • 14

1 Answers1

3

Just like in the answer you linked to, you wanna start by grouping on username:

$groupedByUser = $WinEvents |Group-Object { $_.Properties[5].Value }

Now just loop over the collection of groups, then sort the individual entries in each group by TimeCreated and keep only the latest:

$groupedByUser |ForEach-Object {
  $_.Group |Sort-Object TimeCreated |Select-Object -Last 1
}
Mathias R. Jessen
  • 157,619
  • 12
  • 148
  • 206
  • Thank you so much - I couldn't see what was right in front of me! – tb1 Jun 19 '20 at 18:28
  • One thing that tripped me up - can you clarify why I can do: "$winevents | Select-Object -Property TimeCreated" but this pulls up blanks: "$winevents | Select-Object -Property Property[5].Value" ? – tb1 Jun 19 '20 at 18:38
  • 1
    `Select-Object -Property` accepts either a string or something called a property expression - in the case were you pass a string, that string must resolve to a property on the input item, and the events don't have any properties with the name `Property[5].Value` – Mathias R. Jessen Jun 19 '20 at 18:51
  • 1
    I had a typo, but just to recap, this did not work: "$winevents | Select-Object -Property Properties[5].Value" but based on your response, I confirmed this works: "$winevents | Select-Object -Property {$_.Properties[5].Value}" - thanks again!!! – tb1 Jun 19 '20 at 19:04
  • 1
    Yes, exactly right! `Select-Object` also takes something we usually refer to as _calculated properties_ - a property expression with a name/label: `$winevents |Select-Object @{Name='Username';Expression={$_.Property[5].Value}}` – Mathias R. Jessen Jun 19 '20 at 19:09