From 31efbbd4cfc4d9665ae259d12433801f3ca80e92 Mon Sep 17 00:00:00 2001 From: Andreas Jordan Date: Fri, 14 Aug 2026 14:18:27 +0200 Subject: [PATCH] Registered server commands - Leave the connection of the caller alone Seven sites in the registered server commands closed a connection they did not open, two of them Get- commands. Reading registered servers ended the session of the connection that was handed in - temp tables, SET options, session context - and the connection was silently reopened afterwards, so nothing looked wrong. They share one mechanism, so this is one fix and not seven: removing the disconnect in Add-DbaRegServerGroup alone would change nothing, because it calls Get-DbaRegServerGroup, which disconnects as well. Get-DbaRegServerStore is where the connection is obtained, so it is where the answer is known. It asks Connect-DbaInstance through IsNewConnectionReference and records the result as IsNewConnection on the store, which is safe because the store is built fresh per call and never handed back to Connect-DbaInstance. Disconnect-Regserver becomes the single place that acts on it: it walks up from any object of the registered server tree to the store and closes the connection only when the store says we opened it. The six sites that closed the connection by hand now go through it as well. Measured on SQL Server 2019 with a temp table as the marker, session survived: before after False True Get-DbaRegServerGroup False True Get-DbaRegServer False True Add-DbaRegServerGroup False True Move-DbaRegServerGroup False True Move-DbaRegServer False True Remove-DbaRegServer False True Remove-DbaRegServerGroup (do *RegServer*) Co-Authored-By: Claude Opus 5 (1M context) --- private/functions/Disconnect-Regserver.ps1 | 24 +++++++++--- public/Add-DbaRegServerGroup.ps1 | 4 +- public/Get-DbaRegServer.ps1 | 2 +- public/Get-DbaRegServerGroup.ps1 | 4 +- public/Get-DbaRegServerStore.ps1 | 18 +++++++-- public/Move-DbaRegServer.ps1 | 2 +- public/Move-DbaRegServerGroup.ps1 | 2 +- public/Remove-DbaRegServerGroup.ps1 | 2 +- tests/Add-DbaRegServerGroup.Tests.ps1 | 35 +++++++++++++++++ tests/Get-DbaRegServer.Tests.ps1 | 40 +++++++++++++++++++ tests/Get-DbaRegServerGroup.Tests.ps1 | 34 ++++++++++++++++ tests/Get-DbaRegServerStore.Tests.ps1 | 33 ++++++++++++++++ tests/Move-DbaRegServer.Tests.ps1 | 45 ++++++++++++++++++++++ tests/Move-DbaRegServerGroup.Tests.ps1 | 44 +++++++++++++++++++++ tests/Remove-DbaRegServer.Tests.ps1 | 42 ++++++++++++++++++++ tests/Remove-DbaRegServerGroup.Tests.ps1 | 41 ++++++++++++++++++++ 16 files changed, 354 insertions(+), 18 deletions(-) diff --git a/private/functions/Disconnect-Regserver.ps1 b/private/functions/Disconnect-Regserver.ps1 index ef973fe88c16..476aed76ce83 100644 --- a/private/functions/Disconnect-Regserver.ps1 +++ b/private/functions/Disconnect-Regserver.ps1 @@ -1,8 +1,22 @@ function Disconnect-Regserver ($Server) { + <# + .SYNOPSIS + Internal function. Closes the connection behind a registered server object, but only if we opened it. + + .DESCRIPTION + Takes any object of the registered server tree - a store, a group or a registered server - and walks up + to the RegisteredServersStore, which is the object that holds the connection. + + The connection is only closed when Get-DbaRegServerStore opened it itself, which it records as + IsNewConnection on the store. A connection that was handed in belongs to the caller, and closing it takes + their session, their temp tables and their database context with it. See #10572. + #> $i = 0 - do { $server = $server.Parent } - until ($null -ne $server.ServerConnection -or $i++ -gt 20) - if ($server.ServerConnection) { - $server.ServerConnection.Disconnect() + while ($null -ne $Server -and $null -eq $Server.ServerConnection -and $i++ -le 20) { + $Server = $Server.Parent } -} \ No newline at end of file + + if ($Server.ServerConnection -and $Server.IsNewConnection) { + $Server.ServerConnection.Disconnect() + } +} diff --git a/public/Add-DbaRegServerGroup.ps1 b/public/Add-DbaRegServerGroup.ps1 index d9b9d28377d8..3058b51257c9 100644 --- a/public/Add-DbaRegServerGroup.ps1 +++ b/public/Add-DbaRegServerGroup.ps1 @@ -151,9 +151,7 @@ function Add-DbaRegServerGroup { $newgroup.Alter() Get-DbaRegServerGroup -SqlInstance $currentInstance -Group (Get-RegServerGroupReverseParse -object $newgroup) - if ($currentInstance.ConnectionContext) { - $currentInstance.ConnectionContext.Disconnect() - } + Disconnect-RegServer -Server $newgroup } catch { Stop-Function -Message "Failed to add $reggroup" -ErrorRecord $_ -Continue } diff --git a/public/Get-DbaRegServer.ps1 b/public/Get-DbaRegServer.ps1 index 56b3ea11770d..379f9a6ed740 100644 --- a/public/Get-DbaRegServer.ps1 +++ b/public/Get-DbaRegServer.ps1 @@ -219,7 +219,7 @@ function Get-DbaRegServer { } } else { $servers += ($serverstore.DatabaseEngineServerGroup.GetDescendantRegisteredServers()) - $serverstore.ServerConnection.Disconnect() + Disconnect-RegServer -Server $serverstore } # save the $serverstore for later usage diff --git a/public/Get-DbaRegServerGroup.ps1 b/public/Get-DbaRegServerGroup.ps1 index 4141f450e091..8460bc1d9bbf 100644 --- a/public/Get-DbaRegServerGroup.ps1 +++ b/public/Get-DbaRegServerGroup.ps1 @@ -177,9 +177,7 @@ function Get-DbaRegServerGroup { $groups = $serverstore.DatabaseEngineServerGroup.GetDescendantRegisteredServers().Parent | Where-Object Id -In $Id } } - if ($serverstore.ServerConnection) { - $serverstore.ServerConnection.Disconnect() - } + Disconnect-RegServer -Server $serverstore foreach ($groupobject in $groups) { Add-Member -Force -InputObject $groupobject -MemberType NoteProperty -Name ComputerName -Value $serverstore.ComputerName diff --git a/public/Get-DbaRegServerStore.ps1 b/public/Get-DbaRegServerStore.ps1 index 942b1c087eed..347d22331250 100644 --- a/public/Get-DbaRegServerStore.ps1 +++ b/public/Get-DbaRegServerStore.ps1 @@ -48,7 +48,9 @@ function Get-DbaRegServerStore { - RegisteredServers: Collection of registered servers at the root level Properties excluded from default display (internal/technical properties): - - ServerConnection, DomainInstanceName, DomainName, Urn, Properties, Metadata, Parent, ConnectionContext, PropertyMetadataChanged, PropertyChanged, ParentServer + - ServerConnection, DomainInstanceName, DomainName, Urn, Properties, Metadata, Parent, ConnectionContext, PropertyMetadataChanged, PropertyChanged, ParentServer, IsNewConnection + + IsNewConnection records whether the connection behind the store was opened here or handed in by the caller. The registered server commands use it to decide whether they may close the connection when they are done. All SMO RegisteredServersStore properties are accessible using Select-Object *, including the excluded properties if needed for advanced operations. @@ -72,8 +74,17 @@ function Get-DbaRegServerStore { ) process { foreach ($instance in $SqlInstance) { + # Connect-DbaInstance tells us whether it opened a connection for us. The whole registered server + # family closes the connection through Disconnect-RegServer, which reads the answer back off the + # store, so that only a connection this module opened is ever closed. See #10572. + $isNewConnection = $false + $splatConnect = @{ + SqlInstance = $instance + SqlCredential = $SqlCredential + IsNewConnectionReference = [ref]$isNewConnection + } try { - $server = Connect-DbaInstance -SqlInstance $instance -SqlCredential $SqlCredential + $server = Connect-DbaInstance @splatConnect } catch { Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue } @@ -88,7 +99,8 @@ function Get-DbaRegServerStore { Add-Member -Force -InputObject $store -MemberType NoteProperty -Name InstanceName -value $server.ServiceName Add-Member -Force -InputObject $store -MemberType NoteProperty -Name SqlInstance -value $server.DomainInstanceName Add-Member -Force -InputObject $store -MemberType NoteProperty -Name ParentServer -value $server - Select-DefaultView -InputObject $store -ExcludeProperty ServerConnection, DomainInstanceName, DomainName, Urn, Properties, Metadata, Parent, ConnectionContext, PropertyMetadataChanged, PropertyChanged, ParentServer + Add-Member -Force -InputObject $store -MemberType NoteProperty -Name IsNewConnection -value $isNewConnection + Select-DefaultView -InputObject $store -ExcludeProperty ServerConnection, DomainInstanceName, DomainName, Urn, Properties, Metadata, Parent, ConnectionContext, PropertyMetadataChanged, PropertyChanged, ParentServer, IsNewConnection } # Magic courtesy of Mathias Jessen and David Shifflet diff --git a/public/Move-DbaRegServer.ps1 b/public/Move-DbaRegServer.ps1 index 5d3ca9c0188d..ab31df4a41c5 100644 --- a/public/Move-DbaRegServer.ps1 +++ b/public/Move-DbaRegServer.ps1 @@ -126,7 +126,7 @@ function Move-DbaRegServer { try { $null = $parentserver.ServerConnection.ExecuteNonQuery($regserver.ScriptMove($movetogroup).GetScript()) Get-DbaRegServer -SqlInstance $server -Name $regserver.Name -ServerName $regserver.ServerName - $parentserver.ServerConnection.Disconnect() + Disconnect-RegServer -Server $parentserver } catch { Stop-Function -Message "Failed to move $($regserver.Name) to $Group on $($regserver.SqlInstance)" -ErrorRecord $_ -Continue } diff --git a/public/Move-DbaRegServerGroup.ps1 b/public/Move-DbaRegServerGroup.ps1 index 4131314aafd3..b625b5a9ae8b 100644 --- a/public/Move-DbaRegServerGroup.ps1 +++ b/public/Move-DbaRegServerGroup.ps1 @@ -130,7 +130,7 @@ function Move-DbaRegServerGroup { Write-Message -Level Verbose -Message "Executing $($regservergroup.ScriptMove($groupobject).GetScript())" $null = $parentserver.ServerConnection.ExecuteNonQuery($regservergroup.ScriptMove($groupobject).GetScript()) Get-DbaRegServerGroup -SqlInstance $server -Group $newname - $parentserver.ServerConnection.Disconnect() + Disconnect-RegServer -Server $parentserver } catch { Stop-Function -Message "Failed to move $($regserver.Name) to $NewGroup on $($regserver.SqlInstance)" -ErrorRecord $_ -Continue } diff --git a/public/Remove-DbaRegServerGroup.ps1 b/public/Remove-DbaRegServerGroup.ps1 index 33a5259e1b08..5ff41783ba7a 100644 --- a/public/Remove-DbaRegServerGroup.ps1 +++ b/public/Remove-DbaRegServerGroup.ps1 @@ -115,7 +115,7 @@ function Remove-DbaRegServerGroup { # try to avoid 'Collection was modified after the enumerator was instantiated' issue if ($regservergroup.ID) { $null = $parentserver.ServerConnection.ExecuteNonQuery($regservergroup.ScriptDrop().GetScript()) - $parentserver.ServerConnection.Disconnect() + Disconnect-RegServer -Server $parentserver } else { $regservergroup.Drop() } diff --git a/tests/Add-DbaRegServerGroup.Tests.ps1 b/tests/Add-DbaRegServerGroup.Tests.ps1 index 1324e94e3ecb..194bcefd8e6e 100644 --- a/tests/Add-DbaRegServerGroup.Tests.ps1 +++ b/tests/Add-DbaRegServerGroup.Tests.ps1 @@ -108,4 +108,39 @@ Describe $CommandName -Tag IntegrationTests { $results.SqlInstance | Should -Not -BeNullOrEmpty } } + + Context "The connection of the caller is left alone (#10572)" { + BeforeAll { + # We want to run all commands in the BeforeAll block with EnableException to ensure that the test fails if the setup fails. + $PSDefaultParameterValues["*-Dba*:EnableException"] = $true + + # Only a non-pooled connection can show this. SMO silently reopens a pooled connection, so the test + # would pass even with the disconnect this is about. + $callerServer = Connect-DbaInstance -SqlInstance $TestConfig.InstanceSingle -NonPooledConnection + $null = $callerServer.ConnectionContext.ExecuteNonQuery("CREATE TABLE #dbatoolsci_marker (id INT)") + + $callerGroupName = "dbatoolsci-caller-group" + $callerResult = Add-DbaRegServerGroup -SqlInstance $callerServer -Name $callerGroupName + + # We want to run all commands outside of the BeforeAll block without EnableException to be able to test for specific warnings. + $PSDefaultParameterValues.Remove("*-Dba*:EnableException") + } + + AfterAll { + # We want to run all commands in the AfterAll block with EnableException to ensure that the test fails if the cleanup fails. + $PSDefaultParameterValues["*-Dba*:EnableException"] = $true + + $null = $callerServer | Disconnect-DbaInstance + + $PSDefaultParameterValues.Remove("*-Dba*:EnableException") + } + + It "still adds the group" { + $callerResult.Name | Should -Be $callerGroupName + } + + It "leaves the connection open, so the session survives" { + { $callerServer.ConnectionContext.ExecuteScalar("SELECT COUNT(*) FROM #dbatoolsci_marker") } | Should -Not -Throw + } + } } \ No newline at end of file diff --git a/tests/Get-DbaRegServer.Tests.ps1 b/tests/Get-DbaRegServer.Tests.ps1 index f9e50b8986a5..688ad4970fa2 100644 --- a/tests/Get-DbaRegServer.Tests.ps1 +++ b/tests/Get-DbaRegServer.Tests.ps1 @@ -197,4 +197,44 @@ Describe $CommandName -Tag IntegrationTests { # Property Comparisons will come later when we have the commands } + + Context "The connection of the caller is left alone (#10572)" { + BeforeAll { + # We want to run all commands in the BeforeAll block with EnableException to ensure that the test fails if the setup fails. + $PSDefaultParameterValues["*-Dba*:EnableException"] = $true + + # This context brings its own registered server, because the one of the context above is cleaned up + # by the time this runs. + $callerRegSrvName = "dbatoolsci-caller-server" + $null = Add-DbaRegServer -SqlInstance $TestConfig.InstanceSingle -ServerName $callerRegSrvName -Name $callerRegSrvName + + # Only a non-pooled connection can show this. SMO silently reopens a pooled connection, so the test + # would pass even with the disconnect this is about. + $callerServer = Connect-DbaInstance -SqlInstance $TestConfig.InstanceSingle -NonPooledConnection + $null = $callerServer.ConnectionContext.ExecuteNonQuery("CREATE TABLE #dbatoolsci_marker (id INT)") + + $callerResult = Get-DbaRegServer -SqlInstance $callerServer + + # We want to run all commands outside of the BeforeAll block without EnableException to be able to test for specific warnings. + $PSDefaultParameterValues.Remove("*-Dba*:EnableException") + } + + AfterAll { + # We want to run all commands in the AfterAll block with EnableException to ensure that the test fails if the cleanup fails. + $PSDefaultParameterValues["*-Dba*:EnableException"] = $true + + $null = $callerServer | Disconnect-DbaInstance + Get-DbaRegServer -SqlInstance $TestConfig.InstanceSingle -Name $callerRegSrvName | Remove-DbaRegServer -ErrorAction SilentlyContinue + + $PSDefaultParameterValues.Remove("*-Dba*:EnableException") + } + + It "still returns the registered servers" { + $callerResult.Name | Should -Contain $callerRegSrvName + } + + It "leaves the connection open, so the session survives" { + { $callerServer.ConnectionContext.ExecuteScalar("SELECT COUNT(*) FROM #dbatoolsci_marker") } | Should -Not -Throw + } + } } \ No newline at end of file diff --git a/tests/Get-DbaRegServerGroup.Tests.ps1 b/tests/Get-DbaRegServerGroup.Tests.ps1 index f87887951dd2..0f75fc0c26ad 100644 --- a/tests/Get-DbaRegServerGroup.Tests.ps1 +++ b/tests/Get-DbaRegServerGroup.Tests.ps1 @@ -132,4 +132,38 @@ Describe $CommandName -Tag IntegrationTests { # Property Comparisons will come later when we have the commands } + + Context "The connection of the caller is left alone (#10572)" { + BeforeAll { + # We want to run all commands in the BeforeAll block with EnableException to ensure that the test fails if the setup fails. + $PSDefaultParameterValues["*-Dba*:EnableException"] = $true + + # Only a non-pooled connection can show this. SMO silently reopens a pooled connection, so the test + # would pass even with the disconnect this is about. + $callerServer = Connect-DbaInstance -SqlInstance $TestConfig.InstanceSingle -NonPooledConnection + $null = $callerServer.ConnectionContext.ExecuteNonQuery("CREATE TABLE #dbatoolsci_marker (id INT)") + + $callerResult = Get-DbaRegServerGroup -SqlInstance $callerServer -Id 1 + + # We want to run all commands outside of the BeforeAll block without EnableException to be able to test for specific warnings. + $PSDefaultParameterValues.Remove("*-Dba*:EnableException") + } + + AfterAll { + # We want to run all commands in the AfterAll block with EnableException to ensure that the test fails if the cleanup fails. + $PSDefaultParameterValues["*-Dba*:EnableException"] = $true + + $null = $callerServer | Disconnect-DbaInstance + + $PSDefaultParameterValues.Remove("*-Dba*:EnableException") + } + + It "still returns the group" { + $callerResult.Name | Should -Be "DatabaseEngineServerGroup" + } + + It "leaves the connection open, so the session survives" { + { $callerServer.ConnectionContext.ExecuteScalar("SELECT COUNT(*) FROM #dbatoolsci_marker") } | Should -Not -Throw + } + } } \ No newline at end of file diff --git a/tests/Get-DbaRegServerStore.Tests.ps1 b/tests/Get-DbaRegServerStore.Tests.ps1 index 1d5a34c0a522..5d53ddf3ce0b 100644 --- a/tests/Get-DbaRegServerStore.Tests.ps1 +++ b/tests/Get-DbaRegServerStore.Tests.ps1 @@ -28,4 +28,37 @@ Describe $CommandName -Tag IntegrationTests { $results.DisplayName | Should -Be "Central Management Servers" } } + + Context "The store records who owns the connection (#10572)" { + BeforeAll { + # We want to run all commands in the BeforeAll block with EnableException to ensure that the test fails if the setup fails. + $PSDefaultParameterValues["*-Dba*:EnableException"] = $true + + # This is what the whole registered server family reads to decide whether it may close the + # connection when it is done. + $callerServer = Connect-DbaInstance -SqlInstance $TestConfig.InstanceSingle -NonPooledConnection + $storeFromName = Get-DbaRegServerStore -SqlInstance $TestConfig.InstanceSingle + $storeFromServer = Get-DbaRegServerStore -SqlInstance $callerServer + + # We want to run all commands outside of the BeforeAll block without EnableException to be able to test for specific warnings. + $PSDefaultParameterValues.Remove("*-Dba*:EnableException") + } + + AfterAll { + # We want to run all commands in the AfterAll block with EnableException to ensure that the test fails if the cleanup fails. + $PSDefaultParameterValues["*-Dba*:EnableException"] = $true + + $null = $callerServer | Disconnect-DbaInstance + + $PSDefaultParameterValues.Remove("*-Dba*:EnableException") + } + + It "reports a connection it opened itself as its own" { + $storeFromName.IsNewConnection | Should -BeTrue + } + + It "reports a connection of the caller as not its own" { + $storeFromServer.IsNewConnection | Should -BeFalse + } + } } \ No newline at end of file diff --git a/tests/Move-DbaRegServer.Tests.ps1 b/tests/Move-DbaRegServer.Tests.ps1 index 2adea6f3055c..53debe160cd4 100644 --- a/tests/Move-DbaRegServer.Tests.ps1 +++ b/tests/Move-DbaRegServer.Tests.ps1 @@ -89,4 +89,49 @@ Describe $CommandName -Tag IntegrationTests { $results = Get-DbaRegServer -SqlInstance $TestConfig.InstanceSingle -Group $testGroupHR | Move-DbaRegServer -Group $testGroupFinance $results.Count | Should -Be 2 } + + Context "The connection of the caller is left alone (#10572)" { + BeforeAll { + # We want to run all commands in the BeforeAll block with EnableException to ensure that the test fails if the setup fails. + $PSDefaultParameterValues["*-Dba*:EnableException"] = $true + + $callerRegSrvName = "dbatoolsci-caller-server" + $callerTargetName = "dbatoolsci-caller-target" + $null = Add-DbaRegServerGroup -SqlInstance $TestConfig.InstanceSingle -Name $callerTargetName + $null = Add-DbaRegServer -SqlInstance $TestConfig.InstanceSingle -ServerName $callerRegSrvName -Name $callerRegSrvName + + # Only a non-pooled connection can show this. SMO silently reopens a pooled connection, so the test + # would pass even with the disconnect this is about. + $callerServer = Connect-DbaInstance -SqlInstance $TestConfig.InstanceSingle -NonPooledConnection + + # The marker is created after the lookup on purpose, because Get-DbaRegServer closes the connection + # too. This test has to measure the command under test, not its input. + $callerInputObject = Get-DbaRegServer -SqlInstance $callerServer -Name $callerRegSrvName + $null = $callerServer.ConnectionContext.ExecuteNonQuery("CREATE TABLE #dbatoolsci_marker (id INT)") + + $callerResult = Move-DbaRegServer -InputObject $callerInputObject -Group $callerTargetName + + # We want to run all commands outside of the BeforeAll block without EnableException to be able to test for specific warnings. + $PSDefaultParameterValues.Remove("*-Dba*:EnableException") + } + + AfterAll { + # We want to run all commands in the AfterAll block with EnableException to ensure that the test fails if the cleanup fails. + $PSDefaultParameterValues["*-Dba*:EnableException"] = $true + + $null = $callerServer | Disconnect-DbaInstance + Get-DbaRegServer -SqlInstance $TestConfig.InstanceSingle -Name $callerRegSrvName | Remove-DbaRegServer -ErrorAction SilentlyContinue + Get-DbaRegServerGroup -SqlInstance $TestConfig.InstanceSingle -Group $callerTargetName | Remove-DbaRegServerGroup -ErrorAction SilentlyContinue + + $PSDefaultParameterValues.Remove("*-Dba*:EnableException") + } + + It "still moves the registered server" { + $callerResult.Parent.Name | Should -Be $callerTargetName + } + + It "leaves the connection open, so the session survives" { + { $callerServer.ConnectionContext.ExecuteScalar("SELECT COUNT(*) FROM #dbatoolsci_marker") } | Should -Not -Throw + } + } } \ No newline at end of file diff --git a/tests/Move-DbaRegServerGroup.Tests.ps1 b/tests/Move-DbaRegServerGroup.Tests.ps1 index 844dca085abd..315fea87e074 100644 --- a/tests/Move-DbaRegServerGroup.Tests.ps1 +++ b/tests/Move-DbaRegServerGroup.Tests.ps1 @@ -67,4 +67,48 @@ Describe $CommandName -Tag IntegrationTests { $results.Parent.Name | Should -Be "DatabaseEngineServerGroup" } } + + Context "The connection of the caller is left alone (#10572)" { + BeforeAll { + # We want to run all commands in the BeforeAll block with EnableException to ensure that the test fails if the setup fails. + $PSDefaultParameterValues["*-Dba*:EnableException"] = $true + + $callerGroupName = "dbatoolsci-caller-group" + $callerTargetName = "dbatoolsci-caller-target" + $null = Add-DbaRegServerGroup -SqlInstance $TestConfig.InstanceSingle -Name $callerGroupName + $null = Add-DbaRegServerGroup -SqlInstance $TestConfig.InstanceSingle -Name $callerTargetName + + # Only a non-pooled connection can show this. SMO silently reopens a pooled connection, so the test + # would pass even with the disconnect this is about. + $callerServer = Connect-DbaInstance -SqlInstance $TestConfig.InstanceSingle -NonPooledConnection + + # The marker is created after the lookup on purpose, because Get-DbaRegServerGroup closes the + # connection too. This test has to measure the command under test, not its input. + $callerInputObject = Get-DbaRegServerGroup -SqlInstance $callerServer -Group $callerGroupName + $null = $callerServer.ConnectionContext.ExecuteNonQuery("CREATE TABLE #dbatoolsci_marker (id INT)") + + $callerResult = Move-DbaRegServerGroup -InputObject $callerInputObject -NewGroup $callerTargetName + + # We want to run all commands outside of the BeforeAll block without EnableException to be able to test for specific warnings. + $PSDefaultParameterValues.Remove("*-Dba*:EnableException") + } + + AfterAll { + # We want to run all commands in the AfterAll block with EnableException to ensure that the test fails if the cleanup fails. + $PSDefaultParameterValues["*-Dba*:EnableException"] = $true + + $null = $callerServer | Disconnect-DbaInstance + Get-DbaRegServerGroup -SqlInstance $TestConfig.InstanceSingle -Group $callerTargetName | Remove-DbaRegServerGroup -ErrorAction SilentlyContinue + + $PSDefaultParameterValues.Remove("*-Dba*:EnableException") + } + + It "still moves the group" { + $callerResult.Parent.Name | Should -Be $callerTargetName + } + + It "leaves the connection open, so the session survives" { + { $callerServer.ConnectionContext.ExecuteScalar("SELECT COUNT(*) FROM #dbatoolsci_marker") } | Should -Not -Throw + } + } } \ No newline at end of file diff --git a/tests/Remove-DbaRegServer.Tests.ps1 b/tests/Remove-DbaRegServer.Tests.ps1 index ff8ebc75e8bb..398ca488acd9 100644 --- a/tests/Remove-DbaRegServer.Tests.ps1 +++ b/tests/Remove-DbaRegServer.Tests.ps1 @@ -81,4 +81,46 @@ Describe $CommandName -Tag IntegrationTests { $results.Status | Should -Be "Dropped" } } + + Context "The connection of the caller is left alone (#10572)" { + BeforeAll { + # We want to run all commands in the BeforeAll block with EnableException to ensure that the test fails if the setup fails. + $PSDefaultParameterValues["*-Dba*:EnableException"] = $true + + $callerRegSrvName = "dbatoolsci-caller-server" + $null = Add-DbaRegServer -SqlInstance $TestConfig.InstanceSingle -ServerName $callerRegSrvName -Name $callerRegSrvName + + # Only a non-pooled connection can show this. SMO silently reopens a pooled connection, so the test + # would pass even with the disconnect this is about. + $callerServer = Connect-DbaInstance -SqlInstance $TestConfig.InstanceSingle -NonPooledConnection + + # The marker is created after the lookup on purpose, because Get-DbaRegServer closes the connection + # too. This test has to measure the command under test, not its input. + $callerInputObject = Get-DbaRegServer -SqlInstance $callerServer -Name $callerRegSrvName + $null = $callerServer.ConnectionContext.ExecuteNonQuery("CREATE TABLE #dbatoolsci_marker (id INT)") + + $callerResult = Remove-DbaRegServer -InputObject $callerInputObject + + # We want to run all commands outside of the BeforeAll block without EnableException to be able to test for specific warnings. + $PSDefaultParameterValues.Remove("*-Dba*:EnableException") + } + + AfterAll { + # We want to run all commands in the AfterAll block with EnableException to ensure that the test fails if the cleanup fails. + $PSDefaultParameterValues["*-Dba*:EnableException"] = $true + + $null = $callerServer | Disconnect-DbaInstance + Get-DbaRegServer -SqlInstance $TestConfig.InstanceSingle -Name $callerRegSrvName | Remove-DbaRegServer -ErrorAction SilentlyContinue + + $PSDefaultParameterValues.Remove("*-Dba*:EnableException") + } + + It "still drops the registered server" { + $callerResult.Status | Should -Be "Dropped" + } + + It "leaves the connection open, so the session survives" { + { $callerServer.ConnectionContext.ExecuteScalar("SELECT COUNT(*) FROM #dbatoolsci_marker") } | Should -Not -Throw + } + } } \ No newline at end of file diff --git a/tests/Remove-DbaRegServerGroup.Tests.ps1 b/tests/Remove-DbaRegServerGroup.Tests.ps1 index 7b6f8a5ca591..c5e722fd9799 100644 --- a/tests/Remove-DbaRegServerGroup.Tests.ps1 +++ b/tests/Remove-DbaRegServerGroup.Tests.ps1 @@ -69,4 +69,45 @@ Describe $CommandName -Tag IntegrationTests { $results.Name | Should -Be "dbatoolsci-third" } } + + Context "The connection of the caller is left alone (#10572)" { + BeforeAll { + # We want to run all commands in the BeforeAll block with EnableException to ensure that the test fails if the setup fails. + $PSDefaultParameterValues["*-Dba*:EnableException"] = $true + + $callerGroupName = "dbatoolsci-caller-group" + $null = Add-DbaRegServerGroup -SqlInstance $TestConfig.InstanceSingle -Name $callerGroupName + + # Only a non-pooled connection can show this. SMO silently reopens a pooled connection, so the test + # would pass even with the disconnect this is about. + $callerServer = Connect-DbaInstance -SqlInstance $TestConfig.InstanceSingle -NonPooledConnection + + # The marker is created after the lookup on purpose, because Get-DbaRegServerGroup closes the + # connection too. This test has to measure the command under test, not its input. + $callerInputObject = Get-DbaRegServerGroup -SqlInstance $callerServer -Group $callerGroupName + $null = $callerServer.ConnectionContext.ExecuteNonQuery("CREATE TABLE #dbatoolsci_marker (id INT)") + + $callerResult = Remove-DbaRegServerGroup -InputObject $callerInputObject + + # We want to run all commands outside of the BeforeAll block without EnableException to be able to test for specific warnings. + $PSDefaultParameterValues.Remove("*-Dba*:EnableException") + } + + AfterAll { + # We want to run all commands in the AfterAll block with EnableException to ensure that the test fails if the cleanup fails. + $PSDefaultParameterValues["*-Dba*:EnableException"] = $true + + $null = $callerServer | Disconnect-DbaInstance + + $PSDefaultParameterValues.Remove("*-Dba*:EnableException") + } + + It "still drops the group" { + $callerResult.Status | Should -Be "Dropped" + } + + It "leaves the connection open, so the session survives" { + { $callerServer.ConnectionContext.ExecuteScalar("SELECT COUNT(*) FROM #dbatoolsci_marker") } | Should -Not -Throw + } + } } \ No newline at end of file