Cyotek Development Bloghttps://devblog.cyotek.com/tag/backup/atom.xml2016-06-18T19:02:26ZCreating and restoring bacpac files without using a GUIurn:uuid:9dc74326-4f46-42d8-94c4-1b9312ae1df22016-06-18T19:02:26Z2016-06-18T19:02:26Z<p>Almost all databases I use are SQL Server databases. They are
created with hand written SQL scripts and upgraded with hand
written SQL scripts - it is very rare I'll use SQL Server
Management Studio's (SSMS) designers to work with database
objects. When backing up or restoring databases, I have various
SQL scripts to do this, which works fine when SQL Server has
access to your file system, or you theirs.</p>
<p>This isn't always the case. Last year I replaced our woefully
inadequate error logging system with something slightly more
robust and modern, and this system is hosted on Microsoft's
Azure platform using SaaS. No direct file access there!</p>
<p>Rather than using traditional database backups, for Azure hosted
databases you need to use <a href="https://msdn.microsoft.com/en-us/library/ee210546.aspx" rel="external nofollow noopener">Data-tier Applications</a>. While
these do serve more advanced purposes than traditional backups,
in my scenario I am simply treating them as a means of getting a
database from A to B.</p>
<p>SSMS allows you to work with these files, but only via GUI
commands - there's no SQL statements equivalent to <code>BACKUP DATABASE</code> or <code>RESTORE DATABASE</code>, which is a royal pain. Although
I have my Azure database backed up to blog storage once a week,
I want to make my own backups more frequently, and be able to
restore these locally for development work and performance
profiling. Doing this using SQL Server's GUI tools is not
conductive to an easy workflow.</p>
<h2 id="a-cli-for-working-with-bacpac-files">A CLI for working with BACPAC files</h2>
<p>Fortunately, as I work with Visual Studio I have the <a href="https://msdn.microsoft.com/en-us/library/mt204009.aspx" rel="external nofollow noopener">SQL Server
Data Tools (SSDT)</a> installed, which includes
<code>SqlPackage.exe</code>, a magical tool that will let me import and
export BACPAC files locally and remotely.</p>
<p>Less fortunately, it isn't part of the path and so we can't just
merrily type <code>sqlpackage</code> into a command window the same way you
can type <code>sqlcmd</code> and expect it to work; it won't. And it
doesn't seem to have a convenient version-independent way of
grabbing it from the registry either. On my machine it is
located at <code>C:\Program Files (x86)\Microsoft SQL Server\120\DAC\bin</code>, but this may change based on what version
of the tools you have installed.</p>
<h2 id="creating-a-bacpac-file-from-an-existing-database">Creating a BACPAC file from an existing database</h2>
<p>To export a database into a BACPAC file, you can run the
following command. Note that this works for databases on a
local/remote SQL Server instance or Azure SQL Database.</p>
<figure class="lang-bat highlight"><figcaption><span>bat</span></figcaption><pre class="code">
sqlpackage.exe /a:Export /ssn:&lt;ServerName&gt; /sdn:&lt;DatabaseName&gt; /su:&lt;UserName&gt; /sp:&lt;Password&gt; /tf:&lt;ExportFileName&gt;
</pre>
</figure>
<p>Listed below are the arguments we're using. In my example above, I'm using the short form, you can use either long or short forms to suit your needs.</p>
<ul>
<li><code>/Action</code> (<code>a</code>) - the action to perform, in this case
<strong>Export</strong></li>
<li><code>/SourceServerName</code> (<code>ssn</code>) - the source server name. Can be
either the URI of an Azure database server, or the more
traditional ServerName\InstanceName</li>
<li><code>/SourceDatabaseName</code> (<code>sdn</code>) - the name of the database to
export</li>
<li><code>/SourceUser</code> (<code>su</code>) - the login user name</li>
<li><code>/SourcePassword</code> (<code>sp</code>) - the login password</li>
</ul>
<p>For trusted connections, you can skip the <code>su</code> and <code>sp</code>
arguments.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/sqlpackage-export.png" class="gallery" title="Exporting an Azure SQL Database to a data-tier application file via the command line" ><img src="https://images.cyotek.com/image/thumbnail/devblog/sqlpackage-export.png" alt="Exporting an Azure SQL Database to a data-tier application file via the command line" decoding="async" loading="lazy" /></a><figcaption>Exporting an Azure SQL Database to a data-tier application file via the command line</figcaption></figure>
<p>The screenshot above shows typical output.</p>
<h2 id="restoring-a-database-from-a-bacpac-file">Restoring a database from a BACPAC file</h2>
<p>Restoring a database is just as easy, just use an action of
<strong>Import</strong> instead of export, and invert <em>source</em> and <em>target</em>
in arguments.</p>
<figure class="lang-cmd highlight"><figcaption><span>cmd</span></figcaption>sqlpackage.exe /a:Import /tsn:<ServerName> /tdn:<DatabaseName> /tu:<UserName> /tp:<Password> /sf:<ExportFileName>
</figure>
<p>There are a couple of caveats however - if the target database
already exists and contains objects such as tables or views,
then the import will fail. The database must either not exist,
or be completely empty.</p>
<p>Sadly, despite the fact that you have separate source and target
arguments, it doesn't appear to be possible to do a direct copy
from the source server to the target server.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/sqlpackage-import.png" class="gallery" title="Importing a data-tier application into a local SQL Server instance from a BACPAC file via the command line" ><img src="https://images.cyotek.com/image/thumbnail/devblog/sqlpackage-import.png" alt="Importing a data-tier application into a local SQL Server instance from a BACPAC file via the command line" decoding="async" loading="lazy" /></a><figcaption>Importing a data-tier application into a local SQL Server instance from a BACPAC file via the command line</figcaption></figure><h2 id="an-automated-batch-script-for-restoring-a-database">An automated batch script for restoring a database</h2>
<p>The following batch file is a simple script I use to restore the
newest available <strong>bacpac</strong> file in a given directory. The
script also deletes any existing local database using <code>sqlcmd</code>
prior to importing the database via <code>sqlpackage</code>, resolving a
problem where non-empty SQL databases can't be restored using
the package tool.</p>
<p>It's a very simple script, and not overly robust but it does the
job I need it to do. I still tend to use batch files over
PowerShell for simple tasks, no complications about loaded
modules, slow startup, just swift execution without fuss.</p>
<figure class="lang-cmd highlight"><figcaption><span>cmd</span></figcaption>@ECHO OFF

SETLOCAL

REM This is the directory where the SQL data tools are installed
SET SQLPCKDIR=C:\Program Files (x86)\Microsoft SQL Server\120\DAC\bin\
SET SQLPCK="%SQLPCKDIR%SqlPackage.exe"

REM The directory where the bacpac files are stored
SET DBDIR=D:\Backups\azuredbbackups\

REM The name of the database to import
SET DBNAME=MyDatabase

REM The SQL Server name / instance
SET SERVERNAME=.

REM SQL statement to delete the import database as SQLPACKAGE won't import to an existing database
SET DROPDATABASESQL=IF EXISTS (SELECT * FROM [sys].[databases] WHERE [name] = '%DBNAME%') DROP DATABASE [%DBNAME%];

REM Try and find the newest BACPAC file
FOR /F "tokens=*" %%a IN ('DIR %DBDIR%*.bacpac /B /OD /A-D') DO SET PACNAME=%%a

IF "%PACNAME%"=="" GOTO :bacpacnotfound

SET DBFILE=%DBDIR%%PACNAME%

SQLCMD -S %SERVERNAME% -E -Q "%DROPDATABASESQL%" -b
IF %errorlevel% NEQ 0 GOTO :error

%SQLPCK% /a:Import /sf:%DBFILE% /tdn:%DBNAME% /tsn:%SERVERNAME%
IF %errorlevel% NEQ 0 GOTO :error

GOTO :done

:bacpacnotfound
ECHO No bacpac file found to import. 
EXIT /B 1

:error
ECHO Failed to import bacpac file.
EXIT /B 1

:done
ENDLOCAL
</figure>
<h2 id="update-history">Update History</h2>
<ul>
<li>2016-06-18 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/creating-and-restoring-bacpac-files-without-using-a-gui .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.com