diff --git a/public/Install-DbaMaintenanceSolution.ps1 b/public/Install-DbaMaintenanceSolution.ps1 index b47c7942331..52d33ab9a65 100644 --- a/public/Install-DbaMaintenanceSolution.ps1 +++ b/public/Install-DbaMaintenanceSolution.ps1 @@ -456,8 +456,17 @@ function Install-DbaMaintenanceSolution { } foreach ($instance in $SqlInstance) { + # Connect-DbaInstance tells us whether it opened a connection for us. We must only close what we opened + # ourselves, because closing a connection of the caller takes their session with it. See #10554. + $isNewConnection = $false + $splatConnect = @{ + SqlInstance = $instance + SqlCredential = $SqlCredential + NonPooledConnection = $true + IsNewConnectionReference = [ref]$isNewConnection + } try { - $server = Connect-DbaInstance -SqlInstance $instance -SqlCredential $SqlCredential -NonPooledConnection + $server = Connect-DbaInstance @splatConnect } catch { Stop-Function -Message "Error occurred while establishing connection to $instance" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue } @@ -558,7 +567,9 @@ function Install-DbaMaintenanceSolution { if ($Pscmdlet.ShouldProcess($instance, "Dropping all objects created by Ola's Maintenance Solution")) { Write-ProgressHelper -ExcludePercent -Message "Dropping objects created by Ola's Maintenance Solution" - $null = $db.Invoke($cleanupQuery) + # Invoke-DbaQuery names the database instead of running on the connection of the caller, which + # $db.Invoke() would leave in that database. This is how the installation below runs as well. + $null = Invoke-DbaQuery -SqlInstance $server -Database $Database -Query $cleanupQuery -EnableException } # Remove Ola's Jobs @@ -891,8 +902,10 @@ function Install-DbaMaintenanceSolution { } } - # Close non-pooled connection as this is not done automatically. If it is a reused Server SMO, connection will be opened again automatically on next request. - $null = $server | Disconnect-DbaInstance + if ($isNewConnection) { + # Close non-pooled connection as this is not done automatically. + $null = $server | Disconnect-DbaInstance + } } Write-ProgressHelper -ExcludePercent -Message "Installation complete" diff --git a/tests/Install-DbaMaintenanceSolution.Tests.ps1 b/tests/Install-DbaMaintenanceSolution.Tests.ps1 index 5c1c470f19f..a80d3955ec8 100644 --- a/tests/Install-DbaMaintenanceSolution.Tests.ps1 +++ b/tests/Install-DbaMaintenanceSolution.Tests.ps1 @@ -193,6 +193,43 @@ Describe $CommandName -Tag IntegrationTests { } } + Context "The connection of the caller is left alone (#10554)" { + BeforeAll { + $PSDefaultParameterValues["*-Dba*:EnableException"] = $true + + $callerServer = Connect-DbaInstance -SqlInstance $TestConfig.InstanceMulti1 -NonPooledConnection + $null = $callerServer.ConnectionContext.ExecuteNonQuery("CREATE TABLE #dbatoolsci_marker (id INT)") + + # ReplaceExisting takes the code path that drops the existing objects, jobs are left out so that no + # SQL Agent command touches the connection - those are separate commands, see #10555. + $splatInstall = @{ + SqlInstance = $callerServer + Database = "tempdb" + BackupLocation = "NUL" + ReplaceExisting = $true + } + $null = Install-DbaMaintenanceSolution @splatInstall + + $PSDefaultParameterValues.Remove("*-Dba*:EnableException") + } + + AfterAll { + $PSDefaultParameterValues["*-Dba*:EnableException"] = $true + + $null = $callerServer | Disconnect-DbaInstance + + $PSDefaultParameterValues.Remove("*-Dba*:EnableException") + } + + It "leaves the connection open, so the session survives" { + { $callerServer.ConnectionContext.ExecuteScalar("SELECT COUNT(*) FROM #dbatoolsci_marker") } | Should -Not -Throw + } + + It "leaves the connection in the database it was on" { + $callerServer.ConnectionContext.ExecuteScalar("SELECT DB_NAME()") | Should -Be "master" + } + } + Context "Additional backup parameters all enabled" { AfterEach { Invoke-DbaQuery -SqlInstance $TestConfig.InstanceMulti2 -Query $jobStep.Command -NoExec -EnableException