Skip to content

Import-DbaSpConfigure - Leave the connection of the caller alone and make the migration work again - #10575

Open
andreasjordan wants to merge 3 commits into
developmentfrom
fix-import-dbaspconfigure-connection-ownership
Open

Import-DbaSpConfigure - Leave the connection of the caller alone and make the migration work again#10575
andreasjordan wants to merge 3 commits into
developmentfrom
fix-import-dbaspconfigure-connection-ownership

Conversation

@andreasjordan

@andreasjordan andreasjordan commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator

Type of Change

Purpose

Two things, in the same command, because splitting them would have left one half merged and the other not.

1. The command closed connections it did not own. Sixth command of the inventory in #10554:

if ($PSBoundParameters.Path) {
    $server.ConnectionContext.Disconnect()
} else {
    $sourceserver.ConnectionContext.Disconnect()
    $destserver.ConnectionContext.Disconnect()
}

Verified with Connect-DbaInstance -NonPooledConnection and a temp table as the session marker: the file import and the server copy each closed the connection of the caller and took the session with it.

2. The migration between two instances was broken on SQL Server 2022 and newer, even copying an instance onto itself. Reproduced on Developer Edition 16.0.4255.1:

Alter failed.
  Changes to server configuration option 'suppress recovery model errors'
  are not supported in this edition of SQL Server.

Approach

Connection ownership

All three connect calls request IsNewConnectionReference, and the end block only closes what the command opened itself. The variables default to $false, so a path that never connects cannot close anything.

The migration

Four separate defects, all of them in the copy loop:

  • Every option was assigned, even when the value did not change. Assigning marks the property as changed, and Configuration.Alter() then sends the whole configuration in one batch - which fails as a whole as soon as one option is not supported by the edition. Only options that really differ are touched now, and each is altered on its own, so a failure stays with that option instead of poisoning everything after it. The property is refreshed after a failure, because a pending change survives a failed Alter() and fails again with the next one.
  • :182 altered the source where it meant the destination, so show advanced options never reached the destination.
  • The failure of the batch Alter() was swallowed as $needsrestart = $true. Whether a restart is needed is now read from IsDynamic of the option that actually changed, and a failure is reported as the warning it always claimed to be.
  • show advanced options was left at 0 on both instances instead of being put back the way it was found - Export-DbaSpConfigure does restore it. It is restored in a finally, so an option that cannot be set does not leave it switched on either.

The file import

show advanced options was set on the Configuration collection without ever calling Alter(), so it never reached the instance - the file written by Export-DbaSpConfigure sets it itself. What the two lines did do was leave a pending change on the server object of the caller, which their next Alter() would have applied:

  before: cfg=1 run=1
  after : cfg=0 run=1        <-- a later Alter() by the caller would switch it off

Both lines are gone.

While in there

The two Stop-Function calls of the copy path passed an undefined $server as their target, and the .OUTPUTS block described a boolean this command has never returned.

Commands to test

A real migration, SQL 2025 to SQL 2022, which threw before this change:

=== copy SQL03\SQL2025 to SQL03\SQL2022 ===
  command completed
  warnings: Some configuration options will be updated once SQL Server is restarted.
  src IsOpen: True   dst IsOpen: True
  src session survived: True
  dst session survived: True

=== what changed on the destination ===
  scan for startup procs: was 1 now 0 (source has 0)

=== show advanced options on both ===
  source      : 0 -> 0
  destination : 1 -> 1

Exactly the one option the two instances disagreed about, the restart warning because scan for startup procs is not dynamic, and show advanced options left as it was on both sides.

Tests

tests\Import-DbaSpConfigure.Tests.ps1 had no integration tests at all. Four contexts were added, all on InstanceSingle:

  • the file import leaves the connection open and the session intact, and warns about the restart
  • the file import leaves no pending change on the server object of the caller
  • the copy applies the option the two sides disagree about, leaves show advanced options as it found it, does not warn for a dynamic option, and leaves both connections and both sessions alone
  • the command still connects by name and closes that connection

The copy test needs two server objects that disagree without needing two instances: the source is connected while the option still holds the value to be copied, the instance is then changed, and only then is the destination connected. SMO reads the configuration once per server object, so each keeps the value it saw. Nothing but that one option can change, and it is restored afterwards.

14 tests, all passing on SQL 2019 and on SQL 2022. Reverting the command shows what they are worth:

Instance Tests failing without the fix
SQL 2019 7 of 14
SQL 2022 10 of 14

🤖 Generated with Claude Code

andreasjordan and others added 3 commits August 14, 2026 10:59
The command closed every connection it used in its end block, no matter who
opened it. On a session-scoped connection that takes the session of the caller
with it. Both connections are now reported by Connect-DbaInstance through
IsNewConnectionReference, and only a connection opened here is closed.

While in there: the file import set show advanced options on the Configuration
collection without ever calling Alter(), so it never reached the instance but
left a pending change on the server object of the caller. The file written by
Export-DbaSpConfigure sets the option itself, so the two lines are gone.

(do Import-DbaSpConfigure)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three things the CI run found that the lab instance could not:

- The file import warns once per line the edition does not allow to be set,
  so the warning about the restart is the last one, not the only one.
- Every AfterAll has to remove EnableException again. The hashtable is shared
  by the whole file, so the next context inherited it and the command threw
  where the test expected a warning.
- The copy fails on SQL Server 2022 and newer before it is finished, because
  the command assigns every property of the destination even when the value
  does not change and Configuration.Alter() then rejects an option of that
  edition. That is a separate defect. The connections of the caller have to
  survive it either way, so the test catches the failure and asserts that.

The last context now imports the file by instance name instead of copying by
instance name, which is the path where the command owns the connection.

(do Import-DbaSpConfigure)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… again

The migration was broken on SQL Server 2022 and newer, even when copying an
instance onto itself:

    Alter failed.
      Changes to server configuration option 'suppress recovery model errors'
      are not supported in this edition of SQL Server.

Four things behind that, all fixed here:

- Every option of the destination was assigned, even when the value was the
  one already set. That marks the property as changed, and Alter() then sends
  the whole configuration in one batch, which fails as a whole as soon as one
  option is not supported by the edition. Only options that really differ are
  touched now, and each one is altered on its own so a failure stays with it.
- The Alter that was meant to switch 'show advanced options' on at the
  destination was run against the source, so the option never got there.
- The failure of the batch Alter was swallowed as "needs restart". Whether a
  restart is needed is now read from IsDynamic of the option that changed, and
  a failure is reported as the warning it always claimed to be.
- 'show advanced options' was left at 0 on both instances instead of being put
  back the way it was found. It is restored in a finally block, so an option
  that cannot be set does not leave it switched on either.

Also, the two Stop-Function calls of the copy path passed an undefined $server
as their target, and the .OUTPUTS block described a boolean the command has
never returned.

(do Import-DbaSpConfigure)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@andreasjordan andreasjordan changed the title Import-DbaSpConfigure - Leave the connection of the caller alone Import-DbaSpConfigure - Leave the connection of the caller alone and make the migration work again Aug 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Commands disconnect SQL Server connections they do not own

1 participant