Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 2 additions & 67 deletions private/functions/Get-DecryptedObject.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -132,73 +132,8 @@ function Get-DecryptedObject {
}

Write-Message -Level Verbose -Message "Query password information from the Db."

if ($server.Name -like 'ADMIN:*') {
Write-Message -Level Verbose -Message "We already have a dac, so we use it."
$results = $server.Query($sql)
} else {
$instance = $server.InstanceName
if (-not $server.IsClustered) {
$connString = "Server=ADMIN:127.0.0.1\$instance;Trusted_Connection=True;Pooling=false"
} else {
$dacEnabled = $server.Configuration.RemoteDacConnectionsEnabled.ConfigValue

if ($dacEnabled -eq $false) {
If ($Pscmdlet.ShouldProcess($server.Name, "Enabling remote DAC on clustered instance.")) {
try {
Write-Message -Level Verbose -Message "DAC must be enabled for clusters, even when accessed from active node. Enabling."
$server.Configuration.RemoteDacConnectionsEnabled.ConfigValue = $true
$server.Configuration.Alter()
} catch {
Stop-Function -Message "Failure enabling remote DAC on clustered instance $sourceName" -Target $sourceName -ErrorRecord $_
return
}
}
}

$connString = "Server=ADMIN:$sourceName;Trusted_Connection=True;Pooling=false;"
}

try {
$results = Invoke-Command2 -Raw -Credential $Credential -ComputerName $fullComputerName -ArgumentList $connString, $sql {
try {
$connString = $args[0]
$sql = $args[1]
$conn = New-Object System.Data.SqlClient.SQLConnection($connString)
$cmd = New-Object System.Data.SqlClient.SqlCommand($sql, $conn)
$dt = New-Object System.Data.DataTable
$conn.open()
$dt.Load($cmd.ExecuteReader())
$conn.Close()
$conn.Dispose()
return $dt
} catch {
$exception = $_
try {
$conn.Close()
$conn.Dispose()
} catch {
$null = 1
}
throw $exception
}
}
} catch {
Stop-Function -Message "Can't establish local DAC connection on $sourceName." -Target $server -ErrorRecord $_
}

if ($server.IsClustered -and $dacEnabled -eq $false) {
If ($Pscmdlet.ShouldProcess($server.Name, "Disabling remote DAC on clustered instance.")) {
try {
Write-Message -Level Verbose -Message "Setting remote DAC config back to 0."
$server.Configuration.RemoteDacConnectionsEnabled.ConfigValue = $false
$server.Configuration.Alter()
} catch {
Stop-Function -Message "Failure disabling remote DAC on clustered instance $sourceName" -Target $server -ErrorRecord $_
}
}
}
}
Write-Message -Level Debug -Message $sql
$results = $server.Query($sql)

Write-Message -Level Verbose -Message "Go through each row in results"
foreach ($result in $results) {
Expand Down
33 changes: 29 additions & 4 deletions public/Export-DbaInstance.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -217,17 +217,39 @@ function Export-DbaInstance {
foreach ($instance in $SqlInstance) {
$stepCounter = 0
try {
$server = Connect-DbaInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 10
# Do we need a dedicated admin connection for password retrieval?
# If not both are excluded, we do
$dacNeeded = $Exclude -notcontains 'Credentials' -or $Exclude -notcontains 'LinkedServers'
# If passwords are excluded, we don't need a DAC
if ($ExcludePassword) { $dacNeeded = $false }

# Do we have a dedicated admin connection already?
$dacConnected = $instance.Type -eq 'Server' -and $instance.InputObject.Name -match '^ADMIN:'

$dacOpened = $false
if ($dacNeeded) {
if ($dacConnected) {
Write-Message -Level Verbose -Message "Reusing dedicated admin connection for password retrieval."
$server = $instance.InputObject
} else {
Write-Message -Level Verbose -Message "Opening dedicated admin connection for password retrieval."
$server = Connect-DbaInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 10 -DedicatedAdminConnection -WarningAction SilentlyContinue
$dacOpened = $true
}
} else {
Write-Message -Level Verbose -Message "Opening or reusing normal connection because passwords are excluded."
$server = Connect-DbaInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 10
}
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
}

if ($Force) {
# when the caller requests to overwrite existing scripts we won't add the dynamic timestamp to the folder name, so that a pre-existing location can be overwritten.
$exportPath = Join-DbaPath -Path $Path -Child "$($server.name.replace('\', '$'))"
$exportPath = Join-DbaPath -Path $Path -Child "$($server.DomainInstanceName.replace('\', '$'))"
} else {
$timeNow = (Get-Date -UFormat (Get-DbatoolsConfigValue -FullName 'formatting.uformat'))
$exportPath = Join-DbaPath -Path $Path -Child "$($server.name.replace('\', '$'))-$timeNow"
$exportPath = Join-DbaPath -Path $Path -Child "$($server.DomainInstanceName.replace('\', '$'))-$timeNow"
}

# Ensure the export dir exists.
Expand Down Expand Up @@ -264,7 +286,7 @@ function Export-DbaInstance {
if ($Exclude -notcontains 'Credentials') {
Write-Message -Level Verbose -Message "Exporting SQL credentials"
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Exporting SQL credentials"
$null = Export-DbaCredential -SqlInstance $server -FilePath "$exportPath\credentials.sql" -ExcludePassword:$ExcludePassword
$null = Export-DbaCredential -SqlInstance $server -Credential $Credential -FilePath "$exportPath\credentials.sql" -ExcludePassword:$ExcludePassword
Get-ChildItem -ErrorAction Ignore -Path "$exportPath\credentials.sql"
}

Expand Down Expand Up @@ -447,6 +469,9 @@ function Export-DbaInstance {
Get-ChildItem -ErrorAction Ignore -Path "$exportPath\oledbprovider.sql"
}

if ($dacOpened) {
$null = $server | Disconnect-DbaInstance -WhatIf:$false
}

Write-Progress -Activity "Performing Instance Export for $instance" -Completed
}
Expand Down
67 changes: 63 additions & 4 deletions public/Start-DbaMigration.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ function Start-DbaMigration {
Specifies credentials to connect to the destination SQL Server instance(s). Use when the current Windows account lacks sufficient permissions.
Accepts PowerShell credential objects created with Get-Credential for SQL Authentication or alternative Windows accounts.

.PARAMETER Credential
Login to the target OS using alternative credentials. Accepts credential objects (Get-Credential)

Only used when passwords are being exported, as it requires access to the Windows OS via PowerShell remoting to decrypt the passwords.

.PARAMETER BackupRestore
Uses backup and restore method to migrate databases instead of detach/attach. Creates copy-only backups to preserve existing backup chains.
Requires either -SharedPath for new backups or -UseLastBackup to restore from existing backup files.
Expand Down Expand Up @@ -152,6 +157,10 @@ function Start-DbaMigration {
Required when migrating databases with encrypted objects or certificates that need master key protection.
Must be provided as a SecureString object for security.

.PARAMETER ExcludePassword
Copies credentials, linked servers, and other objects without the actual password values.
Use this in security-conscious environments where password decryption is restricted or when passwords should be manually reset after migration.

.PARAMETER Force
Overwrites existing objects on the destination server without prompting for confirmation.
For databases: drops existing databases with matching names before restoring.
Expand Down Expand Up @@ -242,6 +251,7 @@ function Start-DbaMigration {
param (
[DbaInstanceParameter]$Source,
[DbaInstanceParameter[]]$Destination,
[PSCredential]$Credential,
[switch]$DetachAttach,
[switch]$Reattach,
[switch]$BackupRestore,
Expand All @@ -264,6 +274,7 @@ function Start-DbaMigration {
[switch]$KeepCDC,
[switch]$KeepReplication,
[switch]$Continue,
[switch]$ExcludePassword,
[switch]$Force,
[string]$AzureCredential,
[Security.SecureString]$MasterKeyPassword,
Expand Down Expand Up @@ -348,7 +359,40 @@ function Start-DbaMigration {
}

try {
$sourceserver = Connect-DbaInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential
# Do we need a dedicated admin connection to the source for password retrieval?
# If not all of the three are excluded, we do
$dacNeeded = $Exclude -notcontains 'Credentials' -or $Exclude -notcontains 'DatabaseMail' -or $Exclude -notcontains 'LinkedServers'
# If passwords are excluded, we don't need a DAC
if ($ExcludePassword) { $dacNeeded = $false }

# Do we have a dedicated admin connection already?
$dacConnected = $Source.Type -eq 'Server' -and $Source.InputObject.Name -match '^ADMIN:'

$dacOpened = $false
if ($dacNeeded) {
if ($dacConnected) {
Write-Message -Level Verbose -Message "Reusing dedicated admin connection for password retrieval."
$sourceServerDac = $Source.InputObject
# Reconnect without DAC for Copy-DbaDatabase
Write-Message -Level Verbose -Message "Opening additional normal connection for all commands that don't require DAC."
$sourceServer = Connect-DbaInstance -SqlInstance $Source.FullName -SqlCredential $SourceSqlCredential
} else {
Write-Message -Level Verbose -Message "Opening dedicated admin connection for password retrieval."
$sourceServerDac = Connect-DbaInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential -DedicatedAdminConnection -WarningAction SilentlyContinue
$dacOpened = $true
Write-Message -Level Verbose -Message "Opening or reusing additional normal connection for all commands that don't require DAC."
$sourceServer = Connect-DbaInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential
}
} else {
if ($dacConnected) {
# Reconnect without DAC for Copy-DbaDatabase
Write-Message -Level Verbose -Message "Opening additional normal connection for all commands that don't require DAC."
$sourceServer = Connect-DbaInstance -SqlInstance $Source.FullName -SqlCredential $SourceSqlCredential
} else {
Write-Message -Level Verbose -Message "Opening or reusing normal connection for all commands that don't require DAC."
$sourceServer = Connect-DbaInstance -SqlInstance $Source -SqlCredential $SourceSqlCredential
}
}
} catch {
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $Source
return
Expand Down Expand Up @@ -382,13 +426,21 @@ function Start-DbaMigration {
if ($Exclude -notcontains 'Credentials') {
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Migrating SQL credentials"
Write-Message -Level Verbose -Message "Migrating SQL credentials"
Copy-DbaCredential -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force
if ($dacNeeded) {
Copy-DbaCredential -Source $sourceServerDac -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Credential $Credential -ExcludePassword:$ExcludePassword -Force:$Force
} else {
Copy-DbaCredential -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Credential $Credential -ExcludePassword:$ExcludePassword -Force:$Force
}
}

if ($Exclude -notcontains 'DatabaseMail') {
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Migrating database mail"
Write-Message -Level Verbose -Message "Migrating database mail"
Copy-DbaDbMail -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force
if ($dacNeeded) {
Copy-DbaDbMail -Source $sourceServerDac -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Credential $Credential -ExcludePassword:$ExcludePassword -Force:$Force
} else {
Copy-DbaDbMail -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Credential $Credential -ExcludePassword:$ExcludePassword -Force:$Force
}
}

if ($Exclude -notcontains 'CentralManagementServer') {
Expand Down Expand Up @@ -474,7 +526,11 @@ function Start-DbaMigration {
if ($Exclude -notcontains 'LinkedServers') {
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Migrating linked servers"
Write-Message -Level Verbose -Message "Migrating linked servers"
Copy-DbaLinkedServer -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Force:$Force
if ($dacNeeded) {
Copy-DbaLinkedServer -Source $sourceServerDac -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Credential $Credential -ExcludePassword:$ExcludePassword -Force:$Force
} else {
Copy-DbaLinkedServer -Source $sourceserver -Destination $Destination -DestinationSqlCredential $DestinationSqlCredential -Credential $Credential -ExcludePassword:$ExcludePassword -Force:$Force
}
}

if ($Exclude -notcontains 'DataCollector') {
Expand Down Expand Up @@ -547,6 +603,9 @@ function Start-DbaMigration {
}
}
end {
if ($dacOpened) {
$null = $sourceServerDac | Disconnect-DbaInstance -WhatIf:$false
}
if (Test-FunctionInterrupt) { return }
$totaltime = ($elapsed.Elapsed.toString().Split(".")[0])
Write-Message -Level Verbose -Message "SQL Server migration complete."
Expand Down
6 changes: 3 additions & 3 deletions tests/Connect-DbaInstance.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ Describe $CommandName -Tag UnitTests {

Describe $CommandName -Tag IntegrationTests {
AfterAll {
Get-DbaConnectedInstance | Disconnect-DbaInstance
$null = Get-DbaConnectedInstance | Disconnect-DbaInstance
Clear-DbaConnectionPool
}

Expand Down Expand Up @@ -222,7 +222,7 @@ Describe $CommandName -Tag IntegrationTests {
}

AfterAll {
$server | Disconnect-DbaInstance
$null = $server | Disconnect-DbaInstance
}

It "clones when using parameter Database" {
Expand Down Expand Up @@ -253,7 +253,7 @@ Describe $CommandName -Tag IntegrationTests {
$serverClone = Connect-DbaInstance -SqlInstance $server -DedicatedAdminConnection
$server.ConnectionContext.ServerInstance | Should -Not -Match "^ADMIN:"
$serverClone.ConnectionContext.ServerInstance | Should -Match "^ADMIN:"
$serverClone | Disconnect-DbaInstance
$null = $serverClone | Disconnect-DbaInstance
}

It "clones when using Backup-DabInstace" {
Expand Down
2 changes: 2 additions & 0 deletions tests/Start-DbaMigration.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Describe $CommandName -Tag UnitTests {
$expectedParameters += @(
"Source",
"Destination",
"Credential",
"DetachAttach",
"Reattach",
"BackupRestore",
Expand All @@ -33,6 +34,7 @@ Describe $CommandName -Tag UnitTests {
"KeepCDC",
"KeepReplication",
"Continue",
"ExcludePassword",
"Force",
"AzureCredential",
"MasterKeyPassword",
Expand Down
Loading