From f88cf8c6d6ebcc7bf83e65a86aa4b60f2e17d18d Mon Sep 17 00:00:00 2001 From: Rephl3x Date: Wed, 15 Apr 2026 13:42:47 +1200 Subject: [PATCH] Add Binoculars event filters and all-user mode --- Binoculars.ps1 | 341 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 321 insertions(+), 20 deletions(-) diff --git a/Binoculars.ps1 b/Binoculars.ps1 index 8d115e1..285f631 100644 --- a/Binoculars.ps1 +++ b/Binoculars.ps1 @@ -23,6 +23,25 @@ $script:EventCatalog = @{ 4771 = @{ Name = 'Kerberos Pre-Authentication Failure'; Category = 'Kerberos'; DefaultOutcome = 'Failure' } 4776 = @{ Name = 'NTLM Credential Validation'; Category = 'NTLM'; DefaultOutcome = 'Success' } } +$script:EventFilterGroups = [ordered]@{ + 'Successful logon (4624)' = @(4624) + 'Failed logon (4625)' = @(4625) + 'Lockout (4740)' = @(4740) + 'Unlock (4767)' = @(4767) + 'Logoff (4634, 4647)' = @(4634, 4647) + 'Kerberos tickets (4768, 4769, 4770)' = @(4768, 4769, 4770) + 'Kerberos pre-auth failure (4771)' = @(4771) + 'NTLM validation (4776)' = @(4776) +} +$script:EventProfiles = [ordered]@{ + 'All Authentication' = @($script:RelevantEventIds) + 'Lockouts and Failures' = @(4625, 4740, 4767, 4771, 4776) + 'Successful Sign-Ins' = @(4624, 4768, 4769, 4770) + 'Session Trail' = @(4624, 4625, 4634, 4647, 4740, 4767) + 'Kerberos Focus' = @(4768, 4769, 4770, 4771) + 'NTLM Focus' = @(4776) + 'Custom' = @() +} $script:LogonTypeCatalog = @{ 2 = 'Interactive' 3 = 'Network' @@ -114,6 +133,87 @@ function ConvertTo-BinocularsSafeFileName { return $safeValue } +function Get-BinocularsEventIdSetKey { + param( + [AllowEmptyCollection()] + [int[]]$EventIds + ) + + return ((@($EventIds | Sort-Object -Unique)) -join ',') +} + +function Set-BinocularsEventFilterCheckBoxes { + param( + [Parameter(Mandatory)] + [System.Collections.IDictionary]$CheckBoxMap, + [AllowEmptyCollection()] + [int[]]$EventIds + ) + + $selectedIds = @($EventIds | Sort-Object -Unique) + foreach ($entry in $CheckBoxMap.GetEnumerator()) { + $checkBox = $entry.Value + $groupIds = @($checkBox.Tag) + $checkBox.Checked = (@($groupIds | Where-Object { $_ -in $selectedIds }).Count -eq $groupIds.Count) + } +} + +function Get-BinocularsSelectedEventIds { + param( + [Parameter(Mandatory)] + [System.Collections.IDictionary]$CheckBoxMap + ) + + $selectedIds = New-Object System.Collections.Generic.List[int] + foreach ($checkBox in $CheckBoxMap.Values) { + if (-not $checkBox.Checked) { + continue + } + + foreach ($eventId in @($checkBox.Tag)) { + if (-not $selectedIds.Contains([int]$eventId)) { + $selectedIds.Add([int]$eventId) | Out-Null + } + } + } + + return @($selectedIds | Sort-Object) +} + +function Get-BinocularsMatchingProfileName { + param( + [AllowEmptyCollection()] + [int[]]$EventIds + ) + + $targetKey = Get-BinocularsEventIdSetKey -EventIds $EventIds + foreach ($profileName in $script:EventProfiles.Keys) { + if ($profileName -eq 'Custom') { + continue + } + + if ((Get-BinocularsEventIdSetKey -EventIds $script:EventProfiles[$profileName]) -eq $targetKey) { + return $profileName + } + } + + return 'Custom' +} + +function Get-BinocularsEventFilterSummary { + param( + [AllowEmptyCollection()] + [int[]]$EventIds + ) + + $profileName = Get-BinocularsMatchingProfileName -EventIds $EventIds + if ($profileName -ne 'Custom') { + return $profileName + } + + return 'Custom [{0}]' -f ((@($EventIds | Sort-Object -Unique)) -join ',') +} + function Get-BinocularsArchiveRoot { $candidates = @() @@ -381,11 +481,21 @@ function Resolve-BinocularsIdentity { ) $trimmedInput = $UserInput.Trim() + $domain = Get-ADDomain -ErrorAction Stop if ([string]::IsNullOrWhiteSpace($trimmedInput)) { - throw 'Enter a user account to search for.' + return [pscustomobject]@{ + Input = '' + SamAccountName = 'All Users' + UserPrincipalName = '' + Sid = '' + DisplayName = 'All Users' + DomainDnsName = $domain.DNSRoot + DomainNetBIOSName = $domain.NetBIOSName + SearchTerms = (New-Object 'System.Collections.Generic.HashSet[string]' ([System.StringComparer]::OrdinalIgnoreCase)) + SearchAllUsers = $true + } } - $domain = Get-ADDomain -ErrorAction Stop $lookupValues = New-Object System.Collections.Generic.List[string] $lookupValues.Add($trimmedInput) @@ -459,6 +569,7 @@ function Resolve-BinocularsIdentity { DomainDnsName = $domain.DNSRoot DomainNetBIOSName = $domain.NetBIOSName SearchTerms = $searchTerms + SearchAllUsers = $false } } @@ -549,17 +660,32 @@ function Test-BinocularsPrincipalMatch { [Parameter(Mandatory)] [System.Collections.IDictionary]$DataMap, [Parameter(Mandatory)] - [psobject]$Identity + [psobject]$Identity, + [string]$RawMessage ) + if ($Identity.SearchAllUsers) { + return $true + } + $candidateValues = New-Object System.Collections.Generic.HashSet[string] ([System.StringComparer]::OrdinalIgnoreCase) $priorityKeys = @( 'TargetUserName', + 'TargetAccountName', + 'TargetOutboundUserName', + 'TargetSid', + 'TargetUserSid', + 'LogonAccount', + 'MappedAccountName', + 'SamAccountName', + 'MemberName', + 'MemberSid', 'SubjectUserName', + 'SubjectAccountName', 'AccountName', 'UserName', - 'TargetSid', - 'SubjectUserSid' + 'SubjectUserSid', + 'UserSid' ) foreach ($priorityKey in $priorityKeys) { @@ -598,6 +724,55 @@ function Test-BinocularsPrincipalMatch { } } + $looseNeedles = @( + $Identity.SamAccountName, + $Identity.UserPrincipalName, + $Identity.Sid, + "$($Identity.DomainNetBIOSName)\$($Identity.SamAccountName)", + "$($Identity.SamAccountName)@$($Identity.DomainDnsName)" + ) | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | Select-Object -Unique + + $combinedTextParts = New-Object System.Collections.Generic.List[string] + foreach ($value in $candidateValues) { + if (-not [string]::IsNullOrWhiteSpace($value)) { + $combinedTextParts.Add($value) | Out-Null + } + } + foreach ($value in $DataMap.Values) { + if (-not [string]::IsNullOrWhiteSpace([string]$value)) { + $combinedTextParts.Add(([string]$value).Trim().ToLowerInvariant()) | Out-Null + } + } + if (-not [string]::IsNullOrWhiteSpace($RawMessage)) { + $combinedTextParts.Add($RawMessage.Trim().ToLowerInvariant()) | Out-Null + } + + $combinedText = ($combinedTextParts | Select-Object -Unique) -join ' ' + foreach ($needle in $looseNeedles) { + $normalizedNeedle = $needle.Trim().ToLowerInvariant() + if ([string]::IsNullOrWhiteSpace($normalizedNeedle)) { + continue + } + + if ($combinedText -like "*$normalizedNeedle*") { + return $true + } + + if ($normalizedNeedle.Contains('\')) { + $shortNeedle = $normalizedNeedle.Split('\')[-1] + if ($combinedText -like "*$shortNeedle*") { + return $true + } + } + + if ($normalizedNeedle.Contains('@')) { + $samLikeNeedle = $normalizedNeedle.Split('@')[0] + if ($combinedText -like "*$samLikeNeedle*") { + return $true + } + } + } + return $false } @@ -777,7 +952,7 @@ function ConvertTo-BinocularsRecord { ) $eventData = Get-BinocularsEventDataMap -Xml $RawEvent.Xml - if (-not (Test-BinocularsPrincipalMatch -DataMap $eventData -Identity $Identity)) { + if (-not (Test-BinocularsPrincipalMatch -DataMap $eventData -Identity $Identity -RawMessage $RawEvent.Message)) { return $null } @@ -812,6 +987,7 @@ function ConvertTo-BinocularsRecord { RecordId = [long]$RawEvent.RecordId Fingerprint = '{0}|{1}|{2}' -f $domainController, $RawEvent.RecordId, $eventId RawFields = $rawFields + RawMessage = $RawEvent.Message } } @@ -869,6 +1045,7 @@ function Invoke-BinocularsParallelEventQuery { RecordId = [long]$_.RecordId TimeCreated = $_.TimeCreated Xml = $_.ToXml() + Message = $_.Message Error = $null } } @@ -880,6 +1057,7 @@ function Invoke-BinocularsParallelEventQuery { RecordId = $null TimeCreated = $null Xml = $null + Message = $null Error = $_.Exception.Message } } @@ -1002,17 +1180,22 @@ function Invoke-BinocularsSearch { [datetime]$EndTime, [AllowEmptyCollection()] [string[]]$DomainControllers, + [AllowEmptyCollection()] + [int[]]$EventIds = $script:RelevantEventIds, [object]$StatusTextBox ) if (-not $DomainControllers) { throw 'No reachable writable DCs are currently available. Use Refresh DCs to recheck unreachable servers.' } + if (-not $EventIds) { + throw 'Select at least one event type to search.' + } Write-BinocularsStatus -Message "Searching $($DomainControllers.Count) writable DCs in $($Identity.DomainDnsName)." -StatusTextBox $StatusTextBox Write-BinocularsStatus -Message "Query window: $($StartTime.ToString('yyyy-MM-dd HH:mm:ss')) to $($EndTime.ToString('yyyy-MM-dd HH:mm:ss'))." -StatusTextBox $StatusTextBox - $rawQuery = Invoke-BinocularsParallelEventQuery -DomainControllers $domainControllers -StartTime $StartTime -EndTime $EndTime -StatusTextBox $StatusTextBox + $rawQuery = Invoke-BinocularsParallelEventQuery -DomainControllers $domainControllers -StartTime $StartTime -EndTime $EndTime -EventIds $EventIds -StatusTextBox $StatusTextBox $seenFingerprints = New-Object 'System.Collections.Generic.HashSet[string]' ([System.StringComparer]::OrdinalIgnoreCase) $records = New-Object System.Collections.Generic.List[object] @@ -1047,6 +1230,7 @@ function Invoke-BinocularsSearch { Errors = @($rawQuery.Errors) SuccessfulDomainControllers = @($rawQuery.SuccessfulDomainControllers) DomainControllers = @($domainControllers) + EventIds = @($EventIds | Sort-Object -Unique) Identity = $Identity StartTime = $StartTime EndTime = $EndTime @@ -1072,7 +1256,7 @@ function Write-BinocularsArchive { $fileStamp = Get-Date -Format 'yyyyMMdd' $safeUser = ConvertTo-BinocularsSafeFileName -Value ('{0}-{1}' -f $Identity.DomainDnsName, $Identity.SamAccountName) $archivePath = Join-Path $archiveRoot ('Binoculars-{0}-{1}.csv' -f $safeUser, $fileStamp) - $exportRows = $Records | Select-Object TimeCreated, DomainController, EventId, EventName, Category, Outcome, User, UserDomain, SourceHost, SourceIP, LogonType, Status, StatusText, Summary, RecordId, Fingerprint, RawFields + $exportRows = $Records | Select-Object TimeCreated, DomainController, EventId, EventName, Category, Outcome, User, UserDomain, SourceHost, SourceIP, LogonType, Status, StatusText, Summary, RecordId, Fingerprint, RawFields, RawMessage if (Test-Path -Path $archivePath) { $exportRows | Export-Csv -Path $archivePath -NoTypeInformation -Append @@ -1111,6 +1295,10 @@ function Format-BinocularsRecordDetails { '-------' $Record.Summary '' + 'Raw Message' + '-----------' + $Record.RawMessage + '' 'Raw Fields' '----------' (($Record.RawFields -split '; ') -join [Environment]::NewLine) @@ -1330,7 +1518,7 @@ function Export-BinocularsResults { if ($saveDialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) { $Records | - Select-Object TimeCreated, DomainController, EventId, EventName, Category, Outcome, User, UserDomain, SourceHost, SourceIP, LogonType, Status, StatusText, Summary, RecordId, Fingerprint, RawFields | + Select-Object TimeCreated, DomainController, EventId, EventName, Category, Outcome, User, UserDomain, SourceHost, SourceIP, LogonType, Status, StatusText, Summary, RecordId, Fingerprint, RawFields, RawMessage | Export-Csv -Path $saveDialog.FileName -NoTypeInformation return $saveDialog.FileName @@ -1377,7 +1565,7 @@ function Start-BinocularsHeadless { if ($ExportPath) { $result.Records | - Select-Object TimeCreated, DomainController, EventId, EventName, Category, Outcome, User, UserDomain, SourceHost, SourceIP, LogonType, Status, StatusText, Summary, RecordId, Fingerprint, RawFields | + Select-Object TimeCreated, DomainController, EventId, EventName, Category, Outcome, User, UserDomain, SourceHost, SourceIP, LogonType, Status, StatusText, Summary, RecordId, Fingerprint, RawFields, RawMessage | Export-Csv -Path $ExportPath -NoTypeInformation Write-Host "Exported to $ExportPath" } @@ -1400,8 +1588,11 @@ function Show-BinocularsGui { $script:watchMode = $false $script:queryInProgress = $false $script:activeIdentity = $null + $script:activeEventIds = @($script:RelevantEventIds) + $script:activeEventFilterSummary = Get-BinocularsEventFilterSummary -EventIds $script:activeEventIds $script:cancelSearchRequested = $false $script:domainControllerInventory = @() + $script:updatingEventFilters = $false $form = New-Object System.Windows.Forms.Form $form.Text = 'Binoculars' @@ -1412,7 +1603,7 @@ function Show-BinocularsGui { $topPanel = New-Object System.Windows.Forms.Panel $topPanel.Dock = 'Top' - $topPanel.Height = 120 + $topPanel.Height = 170 $titleLabel = New-Object System.Windows.Forms.Label $titleLabel.Text = 'Binoculars - Writable DC Authentication Tracker' @@ -1506,15 +1697,51 @@ function Show-BinocularsGui { $archiveButton.Size = New-Object System.Drawing.Size(100, 28) $topPanel.Controls.Add($archiveButton) + $eventProfileLabel = New-Object System.Windows.Forms.Label + $eventProfileLabel.Text = 'Event Profile' + $eventProfileLabel.Location = New-Object System.Drawing.Point(15, 80) + $eventProfileLabel.AutoSize = $true + $topPanel.Controls.Add($eventProfileLabel) + + $eventProfileComboBox = New-Object System.Windows.Forms.ComboBox + $eventProfileComboBox.Location = New-Object System.Drawing.Point(90, 76) + $eventProfileComboBox.Size = New-Object System.Drawing.Size(185, 23) + $eventProfileComboBox.DropDownStyle = 'DropDownList' + foreach ($profileName in $script:EventProfiles.Keys) { + [void]$eventProfileComboBox.Items.Add($profileName) + } + $topPanel.Controls.Add($eventProfileComboBox) + + $eventFilterFlowPanel = New-Object System.Windows.Forms.FlowLayoutPanel + $eventFilterFlowPanel.Location = New-Object System.Drawing.Point(290, 72) + $eventFilterFlowPanel.Size = New-Object System.Drawing.Size(1170, 36) + $eventFilterFlowPanel.WrapContents = $true + $eventFilterFlowPanel.AutoScroll = $true + $eventFilterFlowPanel.FlowDirection = 'LeftToRight' + $eventFilterFlowPanel.Padding = New-Object System.Windows.Forms.Padding(0) + $eventFilterFlowPanel.Margin = New-Object System.Windows.Forms.Padding(0) + $topPanel.Controls.Add($eventFilterFlowPanel) + + $eventFilterCheckBoxes = [ordered]@{} + foreach ($groupName in $script:EventFilterGroups.Keys) { + $checkBox = New-Object System.Windows.Forms.CheckBox + $checkBox.Text = $groupName + $checkBox.AutoSize = $true + $checkBox.Margin = New-Object System.Windows.Forms.Padding(0, 6, 14, 0) + $checkBox.Tag = @($script:EventFilterGroups[$groupName]) + $eventFilterFlowPanel.Controls.Add($checkBox) + $eventFilterCheckBoxes[$groupName] = $checkBox + } + $summaryLabel = New-Object System.Windows.Forms.Label $summaryLabel.Text = 'No results loaded.' - $summaryLabel.Location = New-Object System.Drawing.Point(15, 76) + $summaryLabel.Location = New-Object System.Drawing.Point(15, 118) $summaryLabel.Size = New-Object System.Drawing.Size(1460, 18) $topPanel.Controls.Add($summaryLabel) $infoLabel = New-Object System.Windows.Forms.Label $infoLabel.Text = 'DCs are strongest for auth, failed auth and lockout events. Logoff visibility is limited to sessions the DC itself records.' - $infoLabel.Location = New-Object System.Drawing.Point(15, 96) + $infoLabel.Location = New-Object System.Drawing.Point(15, 140) $infoLabel.Size = New-Object System.Drawing.Size(1460, 18) $infoLabel.ForeColor = [System.Drawing.Color]::DimGray $topPanel.Controls.Add($infoLabel) @@ -1627,7 +1854,11 @@ function Show-BinocularsGui { $userTextBox.Enabled = -not $script:watchMode -and -not $Busy $lookbackUpDown.Enabled = -not $script:watchMode -and -not $Busy $intervalUpDown.Enabled = -not $script:watchMode -and -not $Busy + $eventProfileComboBox.Enabled = -not $script:watchMode -and -not $Busy $archiveButton.Enabled = -not $Busy + foreach ($filterCheckBox in $eventFilterCheckBoxes.Values) { + $filterCheckBox.Enabled = -not $script:watchMode -and -not $Busy + } if ($Busy) { $form.Cursor = [System.Windows.Forms.Cursors]::WaitCursor @@ -1682,7 +1913,18 @@ function Show-BinocularsGui { $unreachableCount = @($inventory | Where-Object { $_.IsReachable -eq $false }).Count $unknownCount = @($inventory | Where-Object { $null -eq $_.IsReachable }).Count $pdcEntry = @($inventory | Where-Object { $_.IsPdcEmulator } | Select-Object -First 1) - $resolvedUser = if ($script:activeIdentity) { "$($script:activeIdentity.DisplayName) [$($script:activeIdentity.SamAccountName)]" } else { 'No user selected' } + $resolvedUser = if ($script:activeIdentity) { + if ($script:activeIdentity.SearchAllUsers) { + 'All Users' + } + else { + "$($script:activeIdentity.DisplayName) [$($script:activeIdentity.SamAccountName)]" + } + } + else { + 'No user selected' + } + $filterText = if ($script:activeEventFilterSummary) { $script:activeEventFilterSummary } else { 'Default' } $windowText = if ($Result) { '{0} -> {1}' -f $Result.StartTime.ToString('yyyy-MM-dd HH:mm:ss'), $Result.EndTime.ToString('yyyy-MM-dd HH:mm:ss') } @@ -1692,8 +1934,9 @@ function Show-BinocularsGui { $pdcText = if ($pdcEntry) { $pdcEntry[0].HostName } else { 'N/A' } $searchedDcCount = if ($Result) { $Result.DomainControllers.Count } else { $reachableCount } - $summaryLabel.Text = 'User: {0} | Search DCs: {1} | Green: {2} | Red: {3} | Unknown: {4} | PDC: {5} | Events: {6} | Success: {7} | Failure: {8} | Lockout: {9} | Unlock: {10} | Logoff: {11} | Errors: {12} | Window: {13}' -f ` + $summaryLabel.Text = 'User: {0} | Filter: {1} | Search DCs: {2} | Green: {3} | Red: {4} | Unknown: {5} | PDC: {6} | Events: {7} | Success: {8} | Failure: {9} | Lockout: {10} | Unlock: {11} | Logoff: {12} | Errors: {13} | Window: {14}' -f ` $resolvedUser, ` + $filterText, ` $searchedDcCount, ` $reachableCount, ` $unreachableCount, ` @@ -1726,6 +1969,8 @@ function Show-BinocularsGui { $script:lastArchivePath = $null $script:lastPollEnd = $null $script:activeIdentity = $null + $script:activeEventIds = @(Get-BinocularsSelectedEventIds -CheckBoxMap $eventFilterCheckBoxes) + $script:activeEventFilterSummary = Get-BinocularsEventFilterSummary -EventIds $script:activeEventIds $script:cancelSearchRequested = $false Set-BinocularsGridData -Grid $grid -Records @() $detailsTextBox.Clear() @@ -1757,8 +2002,14 @@ function Show-BinocularsGui { else { Resolve-BinocularsIdentity -UserInput $userTextBox.Text } + $selectedEventIds = @(Get-BinocularsSelectedEventIds -CheckBoxMap $eventFilterCheckBoxes) + if (-not $selectedEventIds) { + throw 'Select at least one event type before searching.' + } $script:activeIdentity = $identity + $script:activeEventIds = @($selectedEventIds) + $script:activeEventFilterSummary = Get-BinocularsEventFilterSummary -EventIds $script:activeEventIds $endTime = Get-Date if ($Incremental -and $script:lastPollEnd) { $startTime = $script:lastPollEnd.AddSeconds(-1 * $script:PollOverlapSeconds) @@ -1768,6 +2019,7 @@ function Show-BinocularsGui { } Write-BinocularsStatus -Message "Tracking $($identity.DisplayName) [$($identity.SamAccountName)]" -StatusTextBox $statusTextBox + Write-BinocularsStatus -Message "Event filter: $($script:activeEventFilterSummary)" -StatusTextBox $statusTextBox if (-not (& $refreshDcInventory $false)) { & $updateSummary $null return $false @@ -1787,7 +2039,7 @@ function Show-BinocularsGui { throw 'All writable DCs are currently marked unreachable. Use Refresh DCs to recheck them.' } - $result = Invoke-BinocularsSearch -Identity $identity -StartTime $startTime -EndTime $endTime -DomainControllers $searchableControllers -StatusTextBox $statusTextBox + $result = Invoke-BinocularsSearch -Identity $identity -StartTime $startTime -EndTime $endTime -DomainControllers $searchableControllers -EventIds $selectedEventIds -StatusTextBox $statusTextBox foreach ($successfulDc in $result.SuccessfulDomainControllers) { Set-BinocularsDomainControllerState -Inventory $script:domainControllerInventory -HostName $successfulDc -Reachable $true } @@ -1855,6 +2107,52 @@ function Show-BinocularsGui { } } + $syncEventFilterProfile = { + $selectedIds = @(Get-BinocularsSelectedEventIds -CheckBoxMap $eventFilterCheckBoxes) + $matchedProfile = Get-BinocularsMatchingProfileName -EventIds $selectedIds + + $script:updatingEventFilters = $true + $eventProfileComboBox.SelectedItem = $matchedProfile + $script:updatingEventFilters = $false + + $script:activeEventIds = @($selectedIds) + $script:activeEventFilterSummary = Get-BinocularsEventFilterSummary -EventIds $selectedIds + if (-not $script:queryInProgress) { + & $updateSummary $null + } + } + + $eventProfileComboBox.Add_SelectedIndexChanged({ + if ($script:updatingEventFilters) { + return + } + + $selectedProfile = [string]$eventProfileComboBox.SelectedItem + if ([string]::IsNullOrWhiteSpace($selectedProfile) -or $selectedProfile -eq 'Custom') { + return + } + + $script:updatingEventFilters = $true + Set-BinocularsEventFilterCheckBoxes -CheckBoxMap $eventFilterCheckBoxes -EventIds $script:EventProfiles[$selectedProfile] + $script:updatingEventFilters = $false + + $script:activeEventIds = @($script:EventProfiles[$selectedProfile] | Sort-Object -Unique) + $script:activeEventFilterSummary = $selectedProfile + if (-not $script:queryInProgress) { + & $updateSummary $null + } + }) + + foreach ($filterCheckBox in $eventFilterCheckBoxes.Values) { + $filterCheckBox.Add_CheckedChanged({ + if ($script:updatingEventFilters) { + return + } + + & $syncEventFilterProfile + }) + } + $grid.Add_SelectionChanged({ if ($grid.SelectedRows.Count -eq 0) { return @@ -1980,6 +2278,13 @@ function Show-BinocularsGui { $form.Controls.Add($topPanel) $form.Controls.Add($statusStrip) + $script:updatingEventFilters = $true + Set-BinocularsEventFilterCheckBoxes -CheckBoxMap $eventFilterCheckBoxes -EventIds $script:RelevantEventIds + $eventProfileComboBox.SelectedItem = 'All Authentication' + $script:updatingEventFilters = $false + $script:activeEventIds = @($script:RelevantEventIds) + $script:activeEventFilterSummary = 'All Authentication' + Set-BinocularsGridData -Grid $grid -Records @() try { $script:domainControllerInventory = @(Get-BinocularsDomainControllerInventory) @@ -2005,10 +2310,6 @@ catch { } if ($NoGui) { - if ([string]::IsNullOrWhiteSpace($UserName)) { - throw 'Use -UserName when running Binoculars.ps1 with -NoGui.' - } - Start-BinocularsHeadless -UserName $UserName -LookbackMinutes $LookbackMinutes -ExportPath $ExportPath } else {