Functions/Invoke-SqlDbCopy.ps1

<#
    .SYNOPSIS
        Invoke a SQL Server database copy.
 
    .DESCRIPTION
        This command will resolve the common use case of a database copy
        required to copy a production system database back to a test or
        development system.
#>

function Invoke-SqlDbCopy
{
    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
    param
    (
        # Source SQL Server to copy the database from.
        [Parameter(Mandatory = $true)]
        [System.String]
        $SourceSqlInstance,

        # SQL credential to the source SQL Server. If not specified, use the
        # integrated Windows authentication.
        [Parameter(Mandatory = $false)]
        [System.Management.Automation.PSCredential]
        $SourceSqlCredential,

        # Name of the database to copy.
        [Parameter(Mandatory = $true)]
        [System.String]
        $SourceDatabaseName,

        # Destination SQL Server to copy the database too.
        [Parameter(Mandatory = $true)]
        [System.String]
        $DestinationSqlInstance,

        # SQL credential to the destination SQL Server. If not specified, use the
        # integrated Windows authentication.
        [Parameter(Mandatory = $false)]
        [System.Management.Automation.PSCredential]
        $DestinationSqlCredential,

        # Name of the restored database. If not specified, the name is equals to
        # the source database.
        [Parameter(Mandatory = $false)]
        [System.String]
        $DestinationDatabaseName
    )

    # Define and verify the connection splat to the source SQL Server.
    $sqlSource = @{
        SqlInstance = $SourceSqlInstance
    }
    if ($PSBoundParameters.ContainsKey('SourceSqlCredential'))
    {
        $sqlSource['SqlCredential'] = $SourceSqlCredential
    }
    $sqlSourceData = Test-SqlConnection @sqlSource -Verbose:$false

    # Define and verify the connection splat to the destination SQL Server.
    $sqlDestination = @{
        SqlInstance = $DestinationSqlInstance
    }
    if ($PSBoundParameters.ContainsKey('DestinationSqlCredential'))
    {
        $sqlDestination['SqlCredential'] = $DestinationSqlCredential
    }
    $sqlDestinationData = Test-SqlConnection @sqlDestination -Verbose:$false

    # Check if the destination database name was specified. If not, use the
    # source database name.
    if (-not $PSBoundParameters.ContainsKey('DestinationDatabaseName'))
    {
        if ($sqlSourceData.Server -eq $sqlDestinationData.Server)
        {
            throw 'Please specify the destination database name if the source and destination SQL Server is the same.'
        }

        $DestinationDatabaseName = $SourceDatabaseName
    }

    Write-Verbose "SQL DB COPY: Query last full disk backup from database '$SourceDatabaseName' on SQL Server '$SourceSqlInstance'."

    # Get and check the last full backup from the source SQL Server.
    $backup = Get-DbaDbBackupHistory @sqlSource -Database $SourceDatabaseName -DeviceType 'Disk' -LastFull
    if ($null -eq $backup)
    {
        throw "Last full backup to disk not found for database '$SourceDatabaseName' on SQL Server '$SourceSqlInstance'."
    }

    if ($PSCmdlet.ShouldProcess("SQL Se1rver $DestinationSqlInstance", "Restore Database $DestinationDatabaseName"))
    {
        Write-Verbose "SQL DB COPY: Restore database '$DestinationDatabaseName' to SQL Server '$DestinationSqlInstance' from path '$($backup.Path)'."

        # Performe the database restore on the destination SQL Server.
        Restore-DbaDatabase @sqlDestination -Path $backup.Path -DatabaseName $DestinationDatabaseName -ReplaceDbNameInFile -WithReplace

        Write-Verbose "SQL DB COPY: Update database '$DestinationDatabaseName' owner to 'sa'."

        # Restore the owner to sa.
        Invoke-DbaQuery @sqlDestination -Database $DestinationDatabaseName -Query "ALTER AUTHORIZATION ON DATABASE::[$DestinationDatabaseName] TO [sa]"

        # Get all files and rename their logical file names, if they do not match
        # the physical file name.
        $files = Get-DbaDbFile @sqlDestination -Database $DestinationDatabaseName
        foreach ($file in $files)
        {
            $actualLogicalName   = $file.LogicalName
            $expectedLogicalName = [System.IO.Path]::GetFileNameWithoutExtension($file.PhysicalName)
            if ($actualLogicalName -ne $expectedLogicalName)
            {
                Write-Verbose "SQL DB COPY: Rename database '$DestinationDatabaseName' logical file '$actualLogicalName' to '$expectedLogicalName'."

                Invoke-DbaQuery @sqlDestination -Database $DestinationDatabaseName -Query "ALTER DATABASE [$DestinationDatabaseName] MODIFY FILE (NAME=N'$actualLogicalName', NEWNAME=N'$expectedLogicalName')"
            }
        }
    }
}