topbanner_forum
  *

avatar image

Welcome, Guest. Please login or register.
Did you miss your activation email?

Login with username, password and session length
  • Saturday December 14, 2024, 11:01 am
  • Proudly celebrating 15+ years online.
  • Donate now to become a lifetime supporting member of the site and get a non-expiring license key for all of our programs.
  • donate

Author Topic: Have Issue with PowerShell and Formatting/Displaying Data from the Event Logs  (Read 5714 times)

Stoic Joker

  • Honorary Member
  • Joined in 2008
  • **
  • Posts: 6,649
    • View Profile
    • Donate to Member
Greetings,
   I'm trying to create a report using the Windows Event Logs that displays the RD Gateway logon history of users with PowerShell, but I'm stuck at how to get the length (or Duration in the code) of their session to show up in a string inside of a switch statement.


Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-TerminalServices-Gateway/Operational'; ID=300,302,303 } | %{

(new-object -Type PSObject -Property @{
TimeCreated = $_.TimeCreated

evId = $_.ID -replace '\s',''

ClientIP = $_.Message -replace '(?smi).*on client computer\s\"+([^\s]+)\",\s+.*','$1'

UserName = $_.Message -replace '(?smi).*The user\s\"+([^\s]+)\",\s+.*','$1'

Duration = $_.Message -replace '(?smi).*\ssession duration was\s+([^\s]+)\s+.*','$1'
})

} | sort UserName, TimeCreated -Descending | Select TimeCreated, ClientIP `
, @{N='Username';E={'{0}' -f $_.UserName}} `
, @{N='evID - User Action Made';E={
switch ($_.evId) {
300 {'300 - Requested Resource Access Authorized'}
302 {'302 - Full Resource Machine Connection'}
303 {"303 - User Disconnected From Resource: 'Duration'"}
default {'This should be unreachable...'}
} Format-Table -AutoSize
}}


No matter what I try, nothing will put the number of seconds number that should be stored in the Duration array into that string for output to the screen. And I need to have it displayed conditionally for only the 303 events - Otherwise it doesn't exist and dumps the whole event message text.

Anybody know the right answer that I can't seem to find?

Thanks in Advance

Stoic Joker

4wd

  • Supporting Member
  • Joined in 2006
  • **
  • Posts: 5,644
    • View Profile
    • Donate to Member
Try string interpolation:
Code: PowerShell [Select]
  1. 303 {"303 - User Disconnected From Resource: $($Duration)"}

Stoic Joker

  • Honorary Member
  • Joined in 2008
  • **
  • Posts: 6,649
    • View Profile
    • Donate to Member
Try string interpolation:
Code: PowerShell [Select]
  1. 303 {"303 - User Disconnected From Resource: $($Duration)"}

I tried that and a few different variations ($_.($Duration, $($_.Duration), and etc.); no luck.
Double quotes come out blank, and single quotes just echo back the code as typed.

The part that's killing me, is I'm not even sure which part is broken; the output string creation, or is the elusive data that I can't get to show-up in it just missing/identified wrong/not supposed to be accessible for there until spring...

Do I need to (somehow) pass the other arrays into the switch statement? Something along the lines of:
Code: PowerShell [Select]
  1. switch ($_.evId.($_.Duration)) {

Or is that even possible/supposed to be needed?

Thank you.


P.S. If it helps/matters - Test code is being run on a Windows Server 2022 machine.


4wd

  • Supporting Member
  • Joined in 2006
  • **
  • Posts: 5,644
    • View Profile
    • Donate to Member
You could also try setting a value regardless of whether there's a match or not:
Duration = if ($_ -match '(?smi).*\ssession duration was\s+([^\s]+)\s+.*') { $matches[1] } else { "Unknown Duration" }

P.S. I can't actually test it as I only have a Win10 machine, wish I could suggest something more.

Shades

  • Member
  • Joined in 2006
  • **
  • Posts: 2,939
    • View Profile
    • Donate to Member
Which version of PowerShell are you using? The one included in Windows itself or the open-source version?

The open-source version is at version 7.x, Powershell included with Windows isn't.

You can run both versions next to each other, that is not a problem on any of the Windows computers in my care (which all run Win 10, Win 11, Server 2019 or Server 2022). The open source version exists, because MS wants PowerShell to be adopted into every operating system, so it is possible to run PowerShell scripts on Linux servers. Not WSL, but real Linux servers.

The open-source version gets more "love" from everyone, incl. Microsoft, so it may be possible that your script works in the open-source version.

Currently I am using the open source version to "play around" with projects that include LLMs to make the computer create all the scripts it needs to accomplish requests I make. Been using the OpenAI LLMs with that, but also with LLMs I run locally. I only mention it, because OS PowerShell works well with this, while the included PowerShell does not.

Stoic Joker

  • Honorary Member
  • Joined in 2008
  • **
  • Posts: 6,649
    • View Profile
    • Donate to Member
You could also try setting a value regardless of whether there's a match or not:
Duration = if ($_ -match '(?smi).*\ssession duration was\s+([^\s]+)\s+.*') { $matches[1] } else { "Unknown Duration" }

P.S. I can't actually test it as I only have a Win10 machine, wish I could suggest something more.

Yeah, I had a feeling that might be a problem...

But! The duration part solved itself later in the code, when I ran it through a formatting routine that returns blank if the input is not a number:
@{Name='Duration  ';Expression={'{0}' -f [timespan]::fromseconds($_.Duration).ToString("d\.hh\:mm\:ss")}}

Then I ran into a variation of the same problem on the next value that your code above worked perfectly on:
Target = if ($_.Message -match '(?smi).*to resource\s\"+([^\s]+).INTERNALDOMAINNAME.com\".\s+.*') { $matches[1] }
else {
$_.Message -replace '(?smi).*network resource:\s\"+([^\s]+).LAP.com\".\s+.*','$1'
}

The output report combines two different event types (connect and disconnect) in the same column, that both needed the same (internal resource machine name) info that was worded differently in their source event messages.

So if anybody else needs it, the now working/finished report code looks like so:
$Start = (Get-Date).AddDays(-14)

$End = Get-Date ## (Get-Date).Date AutoMagically Makes it Midnight of that Date.

Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-TerminalServices-Gateway/Operational';
StartTime=$Start; EndTime=$End; ID=302,303 } | %{

(new-object -Type PSObject -Property @{
TimeStamp = $_.TimeCreated

evId = $_.ID -replace '\s',''

ClientIP = $_.Message -replace '(?smi).*on client computer\s\"+([^\s]+)\",\s+.*','$1'

UserName = $_.Message -replace '(?smi).*The user\s\"+([^\s]+)\",\s+.*','$1'

Duration = $_.Message -replace '(?smi).*\ssession duration was\s+([^\s]+)\s+.*','$1'

Target = if ($_.Message -match '(?smi).*to resource\s\"+([^\s]+).LAP.com\".\s+.*') { $matches[1] }
else {
$_.Message -replace '(?smi).*network resource:\s\"+([^\s]+).INTERNALDOMAINNAME.com\".\s+.*','$1'
}

})

} | sort UserName, TimeStamp -Descending | Select `
 @{Name='Event Date and Time   ';Expression={'{0}' -f $_.TimeStamp}} `
, @{Name='User Location  ';Expression={'{0}' -f $_.ClientIP}} `
, @{Name='Target Machine';Expression={'{0}' -f $_.Target}} `
, @{Name='Username    ';Expression={'{0}' -f $_.UserName}} `
, @{N='User Action Made   ';E={
switch ($_.evId) {
302 {"Connected"}
303 {"Disconnected After:"}
default {'Should be unreachable'}
}}} `
, @{Name='Duration  ';Expression={'{0}' -f [timespan]::fromseconds($_.Duration).ToString("d\.hh\:mm\:ss")}} | Format-Table -AutoSize -Wrap



Thank You!
« Last Edit: January 26, 2024, 12:25 PM by Stoic Joker »

Stoic Joker

  • Honorary Member
  • Joined in 2008
  • **
  • Posts: 6,649
    • View Profile
    • Donate to Member
Which version of PowerShell are you using? The one included in Windows itself or the open-source version?

The open-source version is at version 7.x, Powershell included with Windows isn't.

Normally I'm a what's in the box/living of the land sort of guy. But I may have to explore the OS version.

The open-source version gets more "love" from everyone, incl. Microsoft, so it may be possible that your script works in the open-source version.

No... The prevailing Rule-of-Thumb is: I screwed something up.

Typically because I've usually screwed something up ... As I just don't do this often enough to be/get good at it - Hence, I'm not.

I once spent an entire afternoon working on code with ChatGPT. The reason it took so long is that ChatGPT wasn't astute enough to simply inform me, that I was a Freaking Idiot. Because after some additional tangentially related research I realized that what I was asking it to do was flat-out impossible.


I think I might just be gettin old.