I would like to have a list of recent firewall changes (as is often made by installing software), and see the details of the firewall change that was made.
I managed to get a list within a certain time frame, from EventLogs, where the message
contains the interesting info, but I am not sure how to best get that detail without resorting to long RegEx.
Get-WinEvent -ErrorAction SilentlyContinue -FilterHashtable @{logname="Microsoft-Windows-Windows Firewall With Advanced Security/Firewall"; id=2097; StartTime=(Get-Date).AddHours(-2); EndTime=Get-Date}
ProviderName: Microsoft-Windows-Windows Firewall With Advanced Security
TimeCreated Id LevelDisplayName Message
----------- -- ---------------- -------
2024-11-22 18:18:24 2097 Information A rule has been added to the Windows Defender Firewall exception list.…
2024-11-22 18:18:24 2097 Information A rule has been added to the Windows Defender Firewall exception list.…
2024-11-22 18:18:21 2097 Information A rule has been added to the Windows Defender Firewall exception list.…
2024-11-22 18:18:21 2097 Information A rule has been added to the Windows Defender Firewall exception list.…
Piping the previous command to Format-List
(fl
) gives the details:
TimeCreated : 2024-11-22 18:18:24
ProviderName : Microsoft-Windows-Windows Firewall With Advanced Security
Id : 2097
Message : A rule has been added to the Windows Defender Firewall exception list.
Added Rule:
Rule ID: UDP Query User{D0EBCDC7-8C31-463A-ADB9-A72A2FE0EFA9}C:\bin\arduino\arduino ide.exe
Rule Name: Arduino IDE
Origin: Local
Active: Yes
Direction: Inbound
Profiles: Public
Action: Block
Application Path: C:\bin\arduino\arduino ide.exe
Service Name:
Protocol: UDP
Security Options: None
Edge Traversal: None
Modifying User: S-1-5-80-3088073201-1464728630-1879813800-1107566885-823218052
Modifying Application: C:\Windows\System32\svchost.exe
PolicyAppId:
Error Code: 0
How can I selectively get the table with these columns?
TimeCreated, Protocol, Direction, 'Rule Name', 'Application Path'
Extra Credit would be to add the port number.
UPDATE:
The final function:
function get_firewall_changes {
$nArgs = $($args.Count)
if ( $nArgs -eq 0 ) {
$tHours = -1 # [hours] 1 hour
} else {
$tHours = -[int] $args[0]
}
$selector = [System.Diagnostics.Eventing.Reader.EventLogPropertySelector]::new(
[string[]]@(
"Event/EventData/Data[@Name='RuleName']"
"Event/EventData/Data[@Name='ApplicationPath']"))
$REX = '(?s)Direction:\s*(?<dir>[^\n]+).+Protocol:\s*(?<prot>[^\n]+)'
Get-WinEvent -ErrorAction SilentlyContinue -FilterHashtable @{logname="Microsoft-Windows-Windows Firewall With Advanced Security/Firewall"; id=2097; StartTime=(Get-Date).AddHours($tHours); EndTime=Get-Date} | ForEach-Object {
$RuleName, $ApplicationPath = $_.GetPropertyValues($selector)
$Direction, $Protocol = [regex]::Match( $_.Message, $REX).Groups['dir', 'prot'].Value
if ($RuleName.Length -gt 47 ) { $RuleName = ($RuleName[0..47] -join '') + '...'; } # Truncate long names to 50 characters
[pscustomobject]@{
TimeCreated = $_.TimeCreated
Protocol = $Protocol
Direction = $Direction
RuleName = $RuleName
ApplicationPath = $ApplicationPath
}
} | Format-Table -Autosize -Wrap
}
You can use the same logic as the one shown in this answer however this comes with a caveat, the obtained Protocol
and Direction
will be uint
and ushort
and there isn't an available API that handles this translation from those values to human readable data, i.e. 6
-> Inbound
, 1
-> TCP
, etc. The event message is created by the EvtFormatMessage
function.
If that doesn't matter for you then use this approach:
$selector = [System.Diagnostics.Eventing.Reader.EventLogPropertySelector]::new(
[string[]]@(
"Event/EventData/Data[@Name='Protocol']"
"Event/EventData/Data[@Name='Direction']"
"Event/EventData/Data[@Name='RuleName']"
"Event/EventData/Data[@Name='ApplicationPath']"))
Get-WinEvent -FilterHashtable ..... | ForEach-Object {
$Protocol, $Direction, $RuleName, $ApplicationPath = $_.GetPropertyValues($selector)
[pscustomobject]@{
TimeCreated = $_.TimeCreated
Protocol = $Protocol
Direction = $Direction
RuleName = $RuleName
ApplicationPath = $ApplicationPath
}
}
Otherwise, you can extract those values from the .Message
via regex, this approach seem to work:
$selector = [System.Diagnostics.Eventing.Reader.EventLogPropertySelector]::new(
[string[]]@(
"Event/EventData/Data[@Name='RuleName']"
"Event/EventData/Data[@Name='ApplicationPath']"))
Get-WinEvent -FilterHashtable ..... | ForEach-Object {
$RuleName, $ApplicationPath = $_.GetPropertyValues($selector)
$Direction, $Protocol = [regex]::Match(
$_.Message,
'(?s)Direction:\s*(?<dir>[^\n]+).+Protocol:\s*(?<prot>[^\n]+)').
Groups['dir', 'prot'].Value
[pscustomobject]@{
TimeCreated = $_.TimeCreated
Protocol = $Protocol
Direction = $Direction
RuleName = $RuleName
ApplicationPath = $ApplicationPath
}
}