Created
December 2, 2025 21:32
-
-
Save SweetAsNZ/ed1cb515c055a1c682c2ba7fad953815 to your computer and use it in GitHub Desktop.
Gets a list of deduplicated IP's or Ports that hit the Computer using the output of Get-NetworkConnectionLog.ps1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| function Get-NetworkConnectionLogDedupedData { | |
| <# | |
| .SYNOPSIS | |
| Get deduplicated network connection log data with flexible property selection and time filtering. | |
| .DESCRIPTION | |
| Parses network connection log CSV files and returns unique combinations of selected properties. | |
| Supports time-based filtering (last N minutes or hours) and flexible property selection for deduplication. | |
| .PARAMETER FilePath | |
| Path to the network connection log CSV file. If not specified, searches for the most recent log file | |
| in the default location: $ENV:USERPROFILE\Documents\WindowsPowerShell\SCRIPTS\Network\Get-NetworkConnectionLog\ | |
| .PARAMETER Protocol | |
| Include Protocol in deduplication. | |
| .PARAMETER LocalAddress | |
| Include LocalAddress in deduplication. | |
| .PARAMETER LocalPort | |
| Include LocalPort in deduplication. | |
| .PARAMETER RemoteAddress | |
| Include RemoteAddress in deduplication. | |
| .PARAMETER RemotePort | |
| Include RemotePort in deduplication. | |
| .PARAMETER State | |
| Include State in deduplication. | |
| .PARAMETER OwningProcess | |
| Include OwningProcess in deduplication. | |
| .PARAMETER IgnoreIPv6 | |
| Exclude IPv6 addresses from results. Default is True (IPv6 addresses are excluded). | |
| .PARAMETER ForLastMinutes | |
| Filter log entries from the last N minutes. Takes precedence over ForLastHours. | |
| .PARAMETER ForLastHours | |
| Filter log entries from the last N hours. | |
| .PARAMETER IncludeTimestamp | |
| Include the earliest timestamp for each unique combination. | |
| .PARAMETER OutputToCSV | |
| Export results to CSV file. Creates filename based on original file with _Deduped_<Properties> inserted before timestamp. | |
| .PARAMETER ResolveIPsInDNS | |
| Attempt to resolve IP addresses to DNS A Records. Adds ResolvedLocalAddress and ResolvedRemoteAddress columns to results. | |
| .EXAMPLE | |
| Get-NetworkConnectionLogDedupedData -RemoteAddress -OutputToCSV -ResolveIPsInDNS | |
| Returns unique RemoteAddress, exports to CSV, and resolves IPs to DNS names. | |
| .EXAMPLE | |
| Get-NetworkConnectionLogDedupedData -State -Protocol -ForLastHours 2 | |
| Returns unique State and Protocol combinations from the last 2 hours. | |
| .EXAMPLE | |
| Get-NetworkConnectionLogDedupedData -RemoteAddress -IncludeTimestamp | |
| Returns unique RemoteAddress values with the earliest timestamp for each. | |
| .EXAMPLE | |
| Get-NetworkConnectionLogDedupedData -FilePath "C:\Logs\connections.csv" -LocalPort -State | |
| Returns unique LocalPort and State combinations from the specified file. | |
| .EXAMPLE | |
| Get-NetworkConnectionLogDedupedData -ForLastHours 1 | |
| Returns all unique property combinations (default: all properties) from the last hour. | |
| .EXAMPLE | |
| Get-NetworkConnectionLogDedupedData -RemoteAddress -IgnoreIPv6:$false | |
| Returns unique RemoteAddress values including IPv6 addresses. | |
| .EXAMPLE | |
| Get-NetworkConnectionLogDedupedData -RemoteAddress -RemotePort -State -OutputToCSV | |
| Exports unique combinations to CSV with filename like: Get-NetworkConnectionLog_Deduped_RemoteAddress-RemotePort-State_AKL0EX804_20251201_160132.csv | |
| .EXAMPLE | |
| Get-NetworkConnectionLogDedupedData -RemoteAddress -ResolveIPsInDNS | |
| Returns unique RemoteAddress values with DNS names resolved for each IP address. | |
| .NOTES | |
| Author: Tim West | |
| Company: Sweet As Chocolate Ltd | |
| Created: 2025-12-03 | |
| Updated: 2025-12-03 | |
| Status: Production | |
| Version: 1.0.2 | |
| Use with the output of Get-NetworkConnectionLog.ps1 | |
| .CHANGELOG | |
| 2025-12-03: Initial version - flexible property deduplication with time filtering | |
| 2025-12-03: Split Properties parameter into individual switch parameters for easier usage | |
| 2025-12-03: Added IgnoreIPv6 parameter (default: $true) to filter out IPv6 addresses | |
| 2025-12-03: Added OutputToCSV parameter to export results with descriptive filename | |
| 2025-12-03: Added ResolveIPsInDNS parameter to resolve IP addresses to DNS A Records | |
| #> | |
| [CmdletBinding()] | |
| param( | |
| [Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true,HelpMessage="Defaults to latest log file in \Network\Get-NetworkConnectionLog")] | |
| [string]$FilePath, | |
| [Parameter()] | |
| [switch]$Protocol, | |
| [Parameter()] | |
| [switch]$LocalAddress, | |
| [Parameter()] | |
| [switch]$LocalPort, | |
| [Parameter()] | |
| [switch]$RemoteAddress, | |
| [Parameter()] | |
| [switch]$RemotePort, | |
| [Parameter()] | |
| [switch]$State, | |
| [Parameter()] | |
| [switch]$OwningProcess, | |
| [Parameter()] | |
| [bool]$IgnoreIPv6 = $true, | |
| [Parameter()] | |
| [int]$ForLastMinutes, | |
| [Parameter()] | |
| [int]$ForLastHours, | |
| [Parameter()] | |
| [switch]$IncludeTimestamp, | |
| [Parameter()] | |
| [switch]$OutputToCSV, | |
| [Parameter()] | |
| [switch]$ResolveIPsInDNS | |
| ) | |
| begin { | |
| Write-Verbose "Starting Get-NetworkConnectionLogDedupedData" | |
| # Determine file path | |
| if (-not $FilePath) { | |
| $DefaultPath = "$ENV:USERPROFILE\Documents\WindowsPowerShell\SCRIPTS\Network\Get-NetworkConnectionLog" | |
| if (Test-Path $DefaultPath) { | |
| $LatestFile = Get-ChildItem -Path $DefaultPath -Filter "*.csv" -ErrorAction SilentlyContinue | | |
| Sort-Object LastWriteTime -Descending | Select-Object -First 1 | |
| if ($LatestFile) { | |
| $FilePath = $LatestFile.FullName | |
| Write-Verbose "Using latest file: $FilePath" | |
| } else { | |
| Write-Warning "No CSV files found in $DefaultPath" | |
| return | |
| } | |
| } else { | |
| Write-Warning "Default path not found: $DefaultPath" | |
| return | |
| } | |
| } | |
| # Validate file exists | |
| if (-not (Test-Path $FilePath)) { | |
| Write-Warning "File not found: $FilePath" | |
| return | |
| } | |
| Write-Verbose "Processing file: $FilePath" | |
| } | |
| process { | |
| try { | |
| # Import CSV data | |
| $Data = Import-Csv -Path $FilePath -ErrorAction Stop | |
| Write-Verbose "Loaded $($Data.Count) records" | |
| # Calculate time cutoff if specified | |
| $Cutoff = $null | |
| if ($ForLastMinutes) { | |
| $Cutoff = (Get-Date).AddMinutes(-$ForLastMinutes) | |
| Write-Verbose "Filtering for last $ForLastMinutes minutes (after $Cutoff)" | |
| } elseif ($ForLastHours) { | |
| $Cutoff = (Get-Date).AddHours(-$ForLastHours) | |
| Write-Verbose "Filtering for last $ForLastHours hours (after $Cutoff)" | |
| } | |
| # Filter by time if cutoff specified | |
| if ($Cutoff) { | |
| $Data = $Data | Where-Object { | |
| try { | |
| $Timestamp = [DateTime]::Parse($_.Timestamp) | |
| $Timestamp -ge $Cutoff | |
| } catch { | |
| $false | |
| } | |
| } | |
| Write-Verbose "After time filter: $($Data.Count) records" | |
| } | |
| # Filter out IPv6 addresses if requested | |
| if ($IgnoreIPv6) { | |
| $Data = $Data | Where-Object { | |
| $_.LocalAddress -notmatch ':' -and $_.RemoteAddress -notmatch ':' | |
| } | |
| Write-Verbose "After IPv6 filter: $($Data.Count) records" | |
| } | |
| # Filter out blank IP addresses and localhost (127.0.0.1) | |
| $Data = $Data | Where-Object { | |
| -not [string]::IsNullOrWhiteSpace($_.LocalAddress) -and | |
| -not [string]::IsNullOrWhiteSpace($_.RemoteAddress) -and | |
| $_.LocalAddress -ne '127.0.0.1' -and | |
| $_.RemoteAddress -ne '127.0.0.1' | |
| } | |
| Write-Verbose "After localhost/blank filter: $($Data.Count) records" | |
| # Build property list for selection based on switches | |
| $SelectProperties = @() | |
| if ($IncludeTimestamp) { | |
| $SelectProperties += 'Timestamp' | |
| } | |
| # Add properties based on switch parameters | |
| $PropertiesToInclude = @() | |
| if ($Protocol) { $PropertiesToInclude += 'Protocol' } | |
| if ($LocalAddress) { $PropertiesToInclude += 'LocalAddress' } | |
| if ($LocalPort) { $PropertiesToInclude += 'LocalPort' } | |
| if ($RemoteAddress) { $PropertiesToInclude += 'RemoteAddress' } | |
| if ($RemotePort) { $PropertiesToInclude += 'RemotePort' } | |
| if ($State) { $PropertiesToInclude += 'State' } | |
| if ($OwningProcess) { $PropertiesToInclude += 'OwningProcess' } | |
| # If no properties specified, include all | |
| if ($PropertiesToInclude.Count -eq 0) { | |
| $PropertiesToInclude = @('Protocol', 'LocalAddress', 'LocalPort', 'RemoteAddress', 'RemotePort', 'State', 'OwningProcess') | |
| Write-Verbose "No properties specified, using all properties" | |
| } | |
| $SelectProperties += $PropertiesToInclude | |
| # Select and deduplicate | |
| $Results = $Data | | |
| Select-Object -Property $SelectProperties | | |
| Sort-Object -Property $SelectProperties -Unique | |
| Write-Verbose "Deduplicated to $($Results.Count) unique combinations" | |
| # Resolve IP addresses to DNS names if requested | |
| if ($ResolveIPsInDNS -and $Results) { | |
| Write-Verbose "Resolving IP addresses to DNS names..." | |
| $Results = $Results | ForEach-Object { | |
| $Record = $_ | |
| $ResolvedLocal = $null | |
| $ResolvedRemote = $null | |
| # Resolve LocalAddress if present and not empty | |
| if ($Record.LocalAddress -and $Record.LocalAddress -ne '') { | |
| try { | |
| $DnsResult = Resolve-DnsName -Name $Record.LocalAddress -ErrorAction SilentlyContinue -Type PTR | |
| if ($DnsResult -and $DnsResult.NameHost) { | |
| $ResolvedLocal = $DnsResult.NameHost | |
| } | |
| } catch { | |
| $ResolvedLocal = "Unable to resolve" | |
| } | |
| } | |
| # Resolve RemoteAddress if present and not empty | |
| if ($Record.RemoteAddress -and $Record.RemoteAddress -ne '') { | |
| try { | |
| $DnsResult = Resolve-DnsName -Name $Record.RemoteAddress -ErrorAction SilentlyContinue -Type PTR | |
| if ($DnsResult -and $DnsResult.NameHost) { | |
| $ResolvedRemote = $DnsResult.NameHost | |
| } | |
| } catch { | |
| $ResolvedRemote = "Unable to resolve" | |
| } | |
| } | |
| # Add resolved properties to record | |
| $Record | Add-Member -MemberType NoteProperty -Name ResolvedLocalAddress -Value $ResolvedLocal -Force | |
| $Record | Add-Member -MemberType NoteProperty -Name ResolvedRemoteAddress -Value $ResolvedRemote -Force | |
| $Record | |
| } | |
| Write-Verbose "DNS resolution completed" | |
| } | |
| # Export to CSV if requested | |
| if ($OutputToCSV -and $Results) { | |
| # Build property string for filename | |
| $PropertyString = $PropertiesToInclude -join '-' | |
| # Parse original filename to insert deduped marker | |
| $OriginalFileName = [System.IO.Path]::GetFileNameWithoutExtension($FilePath) | |
| $FileDirectory = [System.IO.Path]::GetDirectoryName($FilePath) | |
| # Create new filename: OriginalName_Deduped_Properties_Timestamp.csv | |
| $NewFileName = "{0}_Deduped_{1}.csv" -f $OriginalFileName, $PropertyString | |
| $OutputPath = Join-Path -Path $FileDirectory -ChildPath $NewFileName | |
| # Export to CSV | |
| $Results | Export-Csv -Path $OutputPath -NoTypeInformation -ErrorAction Stop | |
| Write-Host "\nExported $($Results.Count) unique combination(s) to:" -ForegroundColor Green | |
| Write-Host $OutputPath -ForegroundColor White | |
| } | |
| # Output results | |
| if ($Results) { | |
| Write-Host "`nFound $($Results.Count) unique combination(s):" -ForegroundColor Green | |
| $Results | Format-Table -AutoSize | |
| # Return objects for pipeline | |
| $Results | |
| } else { | |
| Write-Host "No records found matching criteria" -ForegroundColor Yellow | |
| } | |
| } catch { | |
| $Err = $PSItem.Exception.Message | |
| Write-Warning "Error processing file: $Err" | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment