Private/Kinds/RemoteDesktopServer.ps1
|
# The RemoteDesktopServer Kind: the servers an application reached by Remote Desktop. # # A Technician who works by Remote Desktop does so from a program: the Remote Desktop # Connection of Windows (mstsc) or a manager of many connections such as Royal TS. Both # draw their sessions with the Remote Desktop component of Windows (MSTscAx), and so both # write into the client's log, Microsoft-Windows-TerminalServices-RDPClient/Operational, # that the RemoteDesktop Kind reads. Which program a connection came from, the log does not # say: the servers in it stand for every Remote Desktop application of the Run, and the # Finding says so. docs/research/remote-desktop-clients.md. # # The Kind judges no connection: the RemoteDesktop Kind does, with its Findings and its # Timed Events. What this one adds is that the App of its Check uses each server, as a # Service on port 3389 - which the Run turns into the Links Uses of the Network Map # (New-CheckAppLink, ADR-0012). Nothing is probed: a Technician's machine may hold dozens # of servers in thirty days, and what was logged of them is on the Timeline. # # What the Gatherer returns: # Days, UnreadableLogs as the RemoteDesktop Kind # Connections the RemoteDesktop Kind's, of the Check before this one where it # ran, else read here # What the Judge observes of how things are connected (ADR-0012): each server reached, a # Service on it. The Run adds which App uses it. $script:RemoteDesktopServerLinks = @('OnServer:Service') # The port a Remote Desktop server is reached on, as the RemoteDesktop Kind's Timed Events # name it, so that what was logged and what is used meet on one Service. $script:RemoteDesktopServerPort = 3389 # How many servers the Finding names, the newest first. $script:RemoteDesktopServerNamed = 5 # What Get-RemoteDesktopServerData hands its Judge. See Private/DataShape.ps1. $script:RemoteDesktopServerDataShape = @{ Days = 'Answered' Connections = 'List' UnreadableLogs = 'List' } function Get-RemoteDesktopServerData { <# .SYNOPSIS The Remote Desktop client's connections: those the RemoteDesktop Check gathered before this one, or read here where it did not run. Judges none of them. .PARAMETER Observed What the Checks before this one gathered. 'RemoteDesktop' is read where it is there, so that the log is read once a Run. #> [CmdletBinding()] [OutputType([psobject])] param([hashtable]$Parameters = @{}, [AllowNull()][hashtable]$Observed = @{}) $read = $null if ($Observed -and $Observed.ContainsKey('RemoteDesktop')) { $read = $Observed['RemoteDesktop'] } if ($null -eq $read) { $read = Get-RemoteDesktopData -Parameters $Parameters } [pscustomobject]@{ PSTypeName = 'Gutcheck.Data.RemoteDesktopServer' Days = Get-DataProperty $read 'Days' Connections = @((Get-DataCollection $read 'Connections') | Where-Object { $_ }) UnreadableLogs = @((Get-DataCollection $read 'UnreadableLogs') | Where-Object { $_ }) } } function Get-RemoteDesktopServerUsed { <# .SYNOPSIS Each server of the connections, the one connected to last first, with the address its connections went to most. Pure. #> [CmdletBinding()] [OutputType([psobject])] param([AllowNull()][AllowEmptyCollection()]$Connection) $connections = @($Connection | Where-Object { $_ }) if (-not $connections.Count) { return } $among = @($connections | ForEach-Object { "$(Get-DataProperty $_ 'Server')" }) $tally = @{} foreach ($t in @(Get-RemoteDesktopServerTally -Connection $connections)) { $tally["$($t.Server)"] = $t } $seen = @{} $newest = @($connections | Sort-Object @{ Expression = { ConvertTo-DataTime (Get-DataProperty $_ 'Start') }; Descending = $true }) foreach ($connection in $newest) { $key = Get-ServerKey -Name (Get-DataProperty $connection 'Server') -Among $among if (-not $key -or $seen.ContainsKey($key)) { continue } $seen[$key] = $true $t = $tally[$key] [pscustomobject]@{ Server = $key Address = "$(@((Get-DataCollection $t 'Addresses') | Where-Object { $_ })[0].Address)" Last = ConvertTo-DataTime (Get-DataProperty $connection 'Start') Total = $t.Total } } } function ConvertTo-RemoteDesktopServerFinding { <# .SYNOPSIS Which servers the log holds, and each as a Service on port 3389 that the App of the Check uses. Pure. #> [CmdletBinding()] [OutputType([psobject])] param([AllowNull()]$Data, [hashtable]$Parameters = @{}) $used = @(Get-RemoteDesktopServerUsed -Connection (Get-DataCollection $Data 'Connections')) $unreadable = @((Get-DataCollection $Data 'UnreadableLogs') | Where-Object { $_ }) $days = Get-NetworkDisruptionDays -Data $Data $check = Get-Text 'Check.RemoteDesktopServer.Servers' if ($used.Count) { $names = @($used | Select-Object -First $script:RemoteDesktopServerNamed | ForEach-Object { $_.Server }) $list = $names -join ', ' if ($used.Count -gt $names.Count) { $list = (Get-Text 'Value.RemoteDesktopServer.AndMore') -f $list, ($used.Count - $names.Count) } # Information and not OK: that the log names no program is what there is to read, # and an OK Finding carries no Meaning. New-Finding -Category Network -Check $check -Severity INFO ` -Value ((Get-Text 'Value.RemoteDesktopServer.Servers') -f $used.Count, $days, $list) ` -Meaning (Get-Text 'Meaning.RemoteDesktopServer.Shared') } elseif (-not $unreadable.Count) { New-Finding -Category Network -Check $check -Severity INFO ` -Value ((Get-Text 'Value.RemoteDesktopServer.None') -f $days) ` -Meaning (Get-Text 'Meaning.RemoteDesktopServer.Shared') } New-UnreadableLogFinding -Data $Data -Parameters $Parameters Select-UniqueLink -Link @($used | ForEach-Object { New-ServiceLink -Server $_.Server -Port $script:RemoteDesktopServerPort -Address $_.Address }) } |