Moving and Renaming files with Restore-DbaDatabase
Yesterday we looked at renaming databases while restoring them. The observant may have wondered how we can avoid filename conflicts as well. We’ll be covering that today
There are quiet a number of reasons why you’d want to change filenames or paths for data file while you’re restoring them:
- Avoid a filename conflict
- Original FilePaths don’t exist on target server
- Different disk layouts
- Just because you’ve never liked the naming schema
We’ll be going through a lot of examples today so this will be quite a long post.
One caveat. Due to SQL Server’s restore not supporting it we can only change the Physical name of the data file during the restore. To change the logical name you need to use the Rename-DbaDatabse function after the restore has completed.
Let’s look at some examples:
SQL Server Instance Default Paths
Each SQL Server instance has a set of default folders, which are where data files will be created if you don’t specify anywhere else.
On my test SQLExpress instance the defauls are:
ComputerName : WIN-0USQQH9BDOG
InstanceName : SQLEXPRESS
SqlInstance : WIN-0USQQH9BDOG\SQLEXPRESS
Data : C:\Program Files\Microsoft SQL Server\MSSQL12.SQLEXPRESS\MSSQL\DATA
Log : C:\Program Files\Microsoft SQL Server\MSSQL12.SQLEXPRESS\MSSQL\LOGFILES
Backup : C:\Program Files\Microsoft SQL Server\MSSQL12.SQLEXPRESS\MSSQL\Backup
ErrorLog : C:\Program Files\Microsoft SQL Server\MSSQL12.SQLEXPRESS\MSSQL\Log
And on my Developer edition instance they are:
ComputerName : WIN-0USQQH9BDOG
InstanceName : MSSQLSERVER
SqlInstance : WIN-0USQQH9BDOG
Data : C:\Program Files\Microsoft SQL Server\MSSQL15.MSSQLSERVER\MSSQL\DATA
Log : C:\Program Files\Microsoft SQL Server\MSSQL15.MSSQLSERVER\MSSQL\LOGFILES
Backup : C:\Program Files\Microsoft SQL Server\MSSQL15.MSSQLSERVER\MSSQL\Backup
ErrorLog : C:\Program Files\Microsoft SQL Server\MSSQL15.MSSQLSERVER\MSSQL\Log
So if I tried restoring a backup from Express to Enterprise I’d get an error as the file paths don’t exist.
A quick fix for this is to use the DestinationDefaultDirectories
switch. This will cause Restore-DbaDatabase to move the files so that they’ll now be in the default folder for the destination:
Restore-DbaDatabase -SqlInstance localhost -Path c:\backups\sqlexpress -DestinationDefaultDirectories
So now all restored database files would be moved into in to ‘C:\Program Files\Microsoft SQL Server\MSSQL15.MSSQLSERVER\MSSQL\LOGFILES’ or ‘C:\Program Files\Microsoft SQL Server\MSSQL15.MSSQLSERVER\MSSQL\DATA’ depending on which type of file they are.
This is the default action for Restore-DbaDatabase. Specifying it explicitly on the command line makes it easier for others to understand what’s going on if they’re not up to speed with Restore-DbaDatabase
Destination by file type
Perhaps you’ve added extra storage to the target server, and rather than setting the default paths you just know that you want to make files to:
D:\DataFiles
E:\LogFiles
F:\FileStream
In that case we have the DestinationDataDirectory, DestinationLogDirectory and DestinationFileStreamDirectory parameters for you. Each one does what it says on the tin. You use them to specify the path you want each particular type of file to.
Restore-DbaDatabase -SqlInstance localhost -Path c:\backups\sqlexpress -DestinationDataDirectory D:\DataFiles -DestinationLogDirectory E:\LogFiles -DestinationFileStreamDirectory F:\FileStream
As a fallback, any file that isn’t identified as a Transaction Log file or a FileStream file will end up in the DestinationDataDirectory
They can be used independantly of each other. For example, if you wanted to use the default paths for log and data files, but put FileStream data into it’s own folder you’d use:
Restore-DbaDatabase -SqlInstance localhost -Path c:\backups\sqlexpress -DestinationFileStreamDirectory F:\FileStream
And it’ll all get sorted out in the background
FileName Suffix and Prefix
It might be that you don’t want to so anything more that alter the filename to avoid a clash. Perhaps you’re wanting to restore all of the prod databases to create dev instances, and because you’re tight on resources you want to do it do the same SQL Server instance (I won’t judge!).
What we’ll do is,
- Restore all the databases from their latest backup
- Rename all the databases so their name is prefixed with Dev
- Rename all the database files so there filename is prefixed with Dev
This is easily done with the following command:
Restore-DbaDatabase -SqlInstance localhost -Path c:\backups\ -RestoredDatabaseNamePrefix Dev -DestinationFilePrefix Dev
and we’re done. This is also good for building up instances with lots of databases for testing. Lets say we wanted 5 times as many databases in an instance for some load testing:
1..5 | Foreach {Restore-DbaDatabase -SqlInstance localhost -Path c:\backups\ -RestoredDatabaseNamePrefix $_ -DestinationFilePrefix $_}
If you’ve not met the construct ‘1..5’ before, this is PowerShell shorthand for creating the number seriess 1,2,3,4,5. Give any pair of numbers x..y it will create the integers between x and y, including x and y. If x is greater than y, then it will generate them in a descending seriess
In this case it will restore all the databases with the databasename and files prefixed with 1, then again prefixed with 2 and so on until it’s done it 5 times.
Or if you’ve a group of developers, Alice, Bob, Carol, Dan, and Elaine who all want their own dev database:
('Alice', 'Bob', 'Carol', 'Dan', 'Elaine') | Foreach {Restore-DbaDatabase -SqlInstance localhost -Path c:\backups\ -RestoredDatabaseNameSuffix "_$_" -DestinationFilePrefix $_}
You’ve now given them all their own database identiied with their name
FileMapping
FileMapping gives you the most options for renaming or moving files. You can rename whichever files you want to whatver you want them to be. However, it is the most complex to setup up.
The FileMapping parameter expects a PowerShell hashtable, mapping logical filenames to physical filenames. This could look like:
$fileMap = @{
'DataFile1' = 'd:\data\datafile_1.mdf'
'LogFile' = 'e:\logs\logfile.ldf'
}
If you’ve not come across one before a hashtable is a key/value array. In the above example the Logical filename is the Key and the physical filename is the value.
In $fileMap
we’re telling Restore-DbaDatabase that we want the file with Logical filename ‘DataFile1’ to be restored to ‘d:\data\datafile_1.mdf’, and the one with the logical filename ‘LogFile’ to be restored to the physical location ‘e:\logs\logfile.ldf’
You can have as many entries in the Filemapping hashtable as you want:
$fileMap = @{
'DataFile1' = 'd:\data\datafile_1.mdf'
'DataFile2' = 'd:\data\datafile_2.mdf'
'DataFile3' = 'f:\data\accounts_1.mdf'
'LogFile' = 'e:\logs\logfile.ldf'
}
Any files not in the hash table will go to the default location on the target server.
Creating the hashtable may look like a lot of work, especially if you’re moving an instance with a lot of files or databases. But thanks to the wonders of PowerShell and dbatools, we can do this pretty simply.
If you want to just create the FileMapping hashtable for a single database, this will do you:
$hashTable = @{}
Get-DbaDbFile -SqlInstance server1\instance -Database roles1 | Select-Object LogicalName, PhysicalName | ForEach {$ht[$_.LogicalName] = $_.PhysicalName}
Firs we create an emtpy HashTable called $ht
. Get-DbaDbFile will list all of the files used by a specific database, we take LogicalName and PhysicalName from the output, and then for each file returned we add it to $ht
.
If you want to do it for everydatabase on the instances, then just omit the Database parameter:
$hashTable = @{}
Get-DbaDbFile -SqlInstance server1\instance -Database roles1 | Select-Object LogicalName, PhysicalName | ForEach {$ht[$_.LogicalName] = $_.PhysicalName}
This will include the files for tempdb, master, model and msdb, but as you won’t be restoring those they’ll be ignored.
If you’re not happy with editing a hashtable directly, we can dump it to JSON, edit in the text editor of your choice, and then convert it back:
$hashTable | Convert-ToJson | Out-File ./hashtable.json
code ./hashtable.json
# Edit json, save file when done
$hashTable = ConverFrom-Json -InputObject (Get-Content ./hashtable.json -raw)
and you’re good to go.
Conclusions
Restore-DbaDatabase makes it simple to rename files during restore. We’ve tried to provide as many logical ways as we can to rename your files, and hopefully we’ve caught every way you’d like to do it. If not, let us know via Slack or Github Issues
All posts in this series can be found at 31 Days of dbatools Backup and Restores
Anthony Fritz
Stuart, is there a way to restore the databases to the same folders as on the source server instead of the destination defaults? In other words I want to DISABLE -DestinationDefaultDirectories.
Stuart Moore
Hi Anthony,
Theres the
-ReuseSourceFolderStructure
switch which will do exactly what you want.Hope that helps
Steve Kim
What does -ReplaceDbNameInFile flag will do then? Will it automatically rename the file to the database name?
Stuart Moore
Hi Steve,
It will replace the original name of the database with the value passed in with the DatabaseName parameter