Skip to content

Commit 97775b6

Browse files
Find-DbaInstance - Fix default instance port fallback (review of #10327)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 7ad5506 commit 97775b6

3 files changed

Lines changed: 120 additions & 8 deletions

File tree

docs/trackers/features/commit-bug-review-TRACKER.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ Find real bugs (logic errors, null refs, incorrect behavior) and fix them. Skip
132132
| 13807a2b3 | Test-DbaBuild: Fix bug introduced in last change (#10328) | DONE | Reviewed duplicate ReleaseDate follow-up; no bugs found. |
133133
| 4382a8118 | Test-DbaLinkedServerConnection - Fix test failure when Named Pipes is disabled (#10326) | DONE | Reviewed test-only TCP datasrc change; no bugs found. |
134134
| b09063aa0 | Get-DbaWaitStatistic - Fix bug from recent refactoring (#10323) | DONE | Reviewed commit; later commit 9a9236a13 fixed unsafe wait-type SQL filtering and missing normalization. |
135-
| eddfeeeca | Find-DbaInstance - Fix TcpConnected false for default instances (#10327) | PENDING | |
135+
| eddfeeeca | Find-DbaInstance - Fix TcpConnected false for default instances (#10327) | DONE | Added fallback port scanning for Browser default instances without reusing named-instance ports; added unit regression test. |
136136
| 5f483d42c | Get-DbaPermission - Fix Azure SQL DB compatibility (#10320) | PENDING | |
137137
| a38bc6b35 | Get-DbaDbIdentity, Set-DbaDbIdentity, Invoke-DbaDbDbccUpdateUsage - Normalize table names (#10318) | PENDING | |
138138
| 9899bd274 | Copy-DbaDbTableData - Add -ScriptingOptionsObject parameter (#10317) | PENDING | |

public/Find-DbaInstance.ps1

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,7 @@ function Find-DbaInstance {
300300
$pingReply = $null
301301
$sPNs = @()
302302
$ports = @()
303+
$browserFallbackPorts = @()
303304
$browseResult = $null
304305
$services = @()
305306
#Variable marked as unused by PSScriptAnalyzer
@@ -351,8 +352,19 @@ function Find-DbaInstance {
351352
Write-ProgressHelper -Activity "Processing: $($computer)" -StepNumber ($stepCounter++) -Message "Probing Browser service"
352353
$browseResult = Get-SQLInstanceBrowserUDP -ComputerName $computer -EnableException
353354
Write-Message -Level Verbose -Message "Browser returned $($browseResult.Count) instance(s): $(($browseResult | ForEach-Object { "$($_.InstanceName):$($_.TCPPort)" }) -join ', ')"
354-
# Filter port 0 - Browser returns 0 for instances that don't report a TCP port (default instances)
355-
$ports = $browseResult.TCPPort | Where-Object { $_ -gt 0 } | Test-TcpPort -ComputerName $computer
355+
$portsToScan = @()
356+
$browserReportedPorts = $browseResult.TCPPort | Where-Object { $_ -gt 0 }
357+
if ($browserReportedPorts) {
358+
$portsToScan += $browserReportedPorts
359+
}
360+
if ($browseResult | Where-Object { -not $_.TCPPort }) {
361+
$browserFallbackPorts = $TCPPort | Select-Object -Unique
362+
Write-Message -Level Verbose -Message "Browser has instance(s) without TCPPort, adding fallback ports: $($browserFallbackPorts -join ', ')"
363+
$portsToScan += $browserFallbackPorts
364+
}
365+
if ($portsToScan) {
366+
$ports = $portsToScan | Select-Object -Unique | Test-TcpPort -ComputerName $computer
367+
}
356368
Write-Message -Level Verbose -Message "Port test results from Browser: $(($ports | ForEach-Object { "Port $($_.Port)=$($_.IsOpen)" }) -join ', ')"
357369
} catch {
358370
Write-Message -Level Verbose -Message "Browser scan failed: $_"
@@ -363,8 +375,9 @@ function Find-DbaInstance {
363375
# (e.g. SQL Server 2022+ where Browser is deprecated, or default instances
364376
# which don't report a TCP port via Browser UDP)
365377
if (-not $ports) {
366-
Write-Message -Level Verbose -Message "No port info from Browser, falling back to default ports: $($TCPPort -join ', ')"
367-
$ports = $TCPPort | Test-TcpPort -ComputerName $computer
378+
$browserFallbackPorts = $TCPPort | Select-Object -Unique
379+
Write-Message -Level Verbose -Message "No port info from Browser, falling back to default ports: $($browserFallbackPorts -join ', ')"
380+
$ports = $browserFallbackPorts | Test-TcpPort -ComputerName $computer
368381
Write-Message -Level Verbose -Message "Fallback port test results: $(($ports | ForEach-Object { "Port $($_.Port)=$($_.IsOpen)" }) -join ', ')"
369382
}
370383
} else {
@@ -471,8 +484,9 @@ function Find-DbaInstance {
471484
} else {
472485
# Default instance - Browser doesn't report a specific TCP port,
473486
# check if any of the fallback ports we tested is open
474-
Write-Message -Level Verbose -Message "Browser has no TCPPort (default instance), checking PortsScanned for any open port: $(($object.PortsScanned | ForEach-Object { "Port $($_.Port)=$($_.IsOpen)" }) -join ', ')"
475-
$object.PortsScanned | Where-Object IsOpen | Select-Object -First 1 | ForEach-Object {
487+
$defaultPortResults = $object.PortsScanned | Where-Object { $_.Port -in $browserFallbackPorts }
488+
Write-Message -Level Verbose -Message "Browser has no TCPPort (default instance), checking fallback PortsScanned for any open port: $(($defaultPortResults | ForEach-Object { "Port $($_.Port)=$($_.IsOpen)" }) -join ', ')"
489+
$defaultPortResults | Where-Object IsOpen | Select-Object -First 1 | ForEach-Object {
476490
$object.Port = $_.Port
477491
$object.TcpConnected = $true
478492
Write-Message -Level Verbose -Message "Found open port $($_.Port), TcpConnected set to True"

tests/Find-DbaInstance.Tests.ps1

Lines changed: 99 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#Requires -Module @{ ModuleName="Pester"; ModuleVersion="5.0" }
22
param(
3-
$ModuleName = "dbatools",
3+
$ModuleName = "dbatools",
44
$CommandName = "Find-DbaInstance",
55
$PSDefaultParameterValues = $TestConfig.Defaults
66
)
@@ -25,6 +25,104 @@ Describe $CommandName -Tag UnitTests {
2525
Compare-Object -ReferenceObject $expectedParameters -DifferenceObject $hasParameters | Should -BeNullOrEmpty
2626
}
2727
}
28+
29+
InModuleScope dbatools {
30+
BeforeAll {
31+
function New-MockFindDbaInstanceUdpClient {
32+
param(
33+
[byte[]]$ResponseBytes
34+
)
35+
36+
$udpClient = [PSCustomObject]@{
37+
Client = [PSCustomObject]@{
38+
ReceiveTimeout = 0
39+
Blocking = $false
40+
}
41+
ResponseBytes = $ResponseBytes
42+
}
43+
Add-Member -InputObject $udpClient -MemberType ScriptMethod -Name Connect -Value {
44+
param(
45+
$ComputerName,
46+
$Port
47+
)
48+
} -Force
49+
Add-Member -InputObject $udpClient -MemberType ScriptMethod -Name Send -Value {
50+
param(
51+
[byte[]]$Buffer,
52+
[int]$Count
53+
)
54+
55+
$Count
56+
} -Force
57+
Add-Member -InputObject $udpClient -MemberType ScriptMethod -Name Receive -Value {
58+
param([ref]$RemoteEndPoint)
59+
60+
$this.ResponseBytes
61+
} -Force
62+
Add-Member -InputObject $udpClient -MemberType ScriptMethod -Name Close -Value {
63+
} -Force
64+
$udpClient
65+
}
66+
67+
function New-MockFindDbaInstanceTcpClient {
68+
$tcpClient = [PSCustomObject]@{
69+
Connected = $false
70+
}
71+
Add-Member -InputObject $tcpClient -MemberType ScriptMethod -Name Connect -Value {
72+
param(
73+
$ComputerName,
74+
$Port
75+
)
76+
77+
$script:tcpConnectPorts += $Port
78+
$this.Connected = $Port -in @(1433, 51433)
79+
} -Force
80+
Add-Member -InputObject $tcpClient -MemberType ScriptMethod -Name Dispose -Value {
81+
} -Force
82+
$tcpClient
83+
}
84+
}
85+
86+
Context "Browser scan handling" {
87+
BeforeEach {
88+
$script:tcpConnectPorts = @()
89+
$script:browserResponseBytes = [System.Text.Encoding]::ASCII.GetBytes(
90+
"ServerName;sqlhost;InstanceName;MSSQLSERVER;IsClustered;No;Version;16.0.1000.6;ServerName;sqlhost;InstanceName;DEV;IsClustered;No;Version;16.0.1000.6;tcp;51433"
91+
)
92+
93+
Mock Test-FunctionInterrupt { $false }
94+
function Write-ProgressHelper {
95+
}
96+
function Write-Message {
97+
}
98+
Mock New-Object {
99+
New-MockFindDbaInstanceUdpClient -ResponseBytes $script:browserResponseBytes
100+
} -ParameterFilter {
101+
$TypeName -eq "System.Net.Sockets.UdpClient"
102+
}
103+
Mock New-Object {
104+
New-MockFindDbaInstanceTcpClient
105+
} -ParameterFilter {
106+
$TypeName -eq "Net.Sockets.TcpClient"
107+
}
108+
}
109+
110+
It "scans fallback ports for default instances without reusing named instance ports" {
111+
$results = Find-DbaInstance -ComputerName "sqlhost" -ScanType Browser
112+
$defaultInstance = $results | Where-Object InstanceName -eq "MSSQLSERVER"
113+
$namedInstance = $results | Where-Object InstanceName -eq "DEV"
114+
115+
$defaultInstance | Should -Not -BeNullOrEmpty
116+
$namedInstance | Should -Not -BeNullOrEmpty
117+
$script:tcpConnectPorts | Should -Contain 1433
118+
$script:tcpConnectPorts | Should -Contain 51433
119+
$defaultInstance.Port | Should -Be 1433
120+
$defaultInstance.TcpConnected | Should -Be $true
121+
$namedInstance.Port | Should -Be 51433
122+
$namedInstance.TcpConnected | Should -Be $true
123+
}
124+
}
125+
}
28126
}
29127

30128
Describe $CommandName -Tag IntegrationTests {

0 commit comments

Comments
 (0)