Overriding DefaultDataPath and DefaultLogPath variables when using SqlPackage to publishing a dacpac - dacpac

The environment:
Using a SQL Server Database Tool in Visual Studio 2013, Update 4 to create a DacPac for publishing a database.
Using TFS build to build and drop the database project.
Using SqlPackage version to publish the dacpac to SQL Server 2012.
Using the SqlPackage Action "Publish", the script for deployment is generated using the following variables based on the target database's defaults:
:setvar DefaultDataPath "H:\YourServerDefault"
:setvar DefaultLogPath "H:\YourServerDefault"
I would like to override these locations to land in a specific pair of directories where we have some external encryption configured.
I have attempted to override these SQLCMD values in a couple ways:
Override on the command line:
SQLPackage /Action:Publish /SourceFile:./Db.dacpac /Profile:./MyProfile.publish.xml /Variables:DefaultDataPath=H:\MyNewLocation
Add an override in the publish profile (snippet below):
<ItemGroup>
<SqlCmdVariable Include="DefaultDataPath ">
<Value>H:\YourServerDefault</Value>
</SqlCmdVariable>
</ItemGroup>
While I can use either technique to override my own defined SQLCMD variables, the built in ones like DefaultDataPath and DefaultLogPath are ignored.
Is there a way to override these values when publishing with SqlPackage?

There is no built in support in SqlPackage but you can do this using a deployment contributor. Take a look at the DbLocationModifier sample on GitHub that solves exactly this issue. To actually use this, you need to:
Download the code from Github
Add a signing key to the project
Install to the correct extensions directory, or put it in the same folder as SqlPackage itself.
Pass additional arguments to SqlPackage specifying your contributor name and arguments - see the test code for how to do this via the API, the SqlPackage calls are essentially the same.
For more information about contributors and how to use them you can read the tutorial I wrote or read the walkthroughs on MSDN.
Disclosure: I work on the SQL Server tools team.

Related

How to Auto-populate SQLCMD variable with Build/Assembly Version

Using Visual Studio 2013 with TFS and SQL Server 2012 & 2014, and SQLPackage.exe with a Publish profile.
Does anyone know if/how to get the Build version or Assembly version into a SQLCMD variable?
i.e. I'm trying to obtain whatever system-level build versioning information TFS/Visual Studio has about an SQL project build and push this into a SQLCMD variable and then insert that into a table.
I know that I can explicitly define an SQLCMD variable and then manually set and increment the value of it, but I want whatever build version number has for the project.
I've been able to get the Assembly version info via MSBuild, as detailed here: https://social.msdn.microsoft.com/Forums/sqlserver/en-US/e0c93a55-d8bd-4a32-89d9-f46013fc1235/automatic-version-increment-on-datatier-applications?forum=ssdt
But that is outside of my post-deploy.sql script. I've tried simply referencing the $(IntermediateTargetAssembly) variable, but the project won't build and errors that this variable not been declared.
I realize this is a different deployment setup than you are currently using but I think you will find it may work out better overall.
http://dotnetcatch.com/2016/02/10/deploying-a-database-project-with-msdeploy/
You could set the build version on your MSDeploy package using the MSBuild property you found via MSDeploy Parameterization to update a SQL script that would insert or update the source version in your database.

SQLPackage Post Deployment Script not running

I'm using Visual Studio 2013 with TFS, over SQL-Server 2012. Specifically I'm using SQL-Server Data Tools within VS for our SQL development, rather than within SQL-Server Management Studio.
In short, the Post-Deployment SQL script appears not to be executed by SQLPackage.
The specifics are: I have a VS SQL Database Project 'ADMIR', which has been developing over the last few weeks and gradually becoming more sophisticated both in terms of SQL and it's deployment. The database includes some dynamically generated tables and therefore I've introduced SQL Command Variables to reference two databases (one is the actual ADMIR database, the other a DB on the same Server). Therefore I've added Database References to dacpacs for these two DB's.
The ADMIR Project will Build fine.
There is also a Post-Deployment SQL script, and prior to the introduction of SQL Command Variables, this would also run fine and initialize various table data.
However, my understanding is that SQL Command Variables won't work with the VS/TFS Publish action, in that the Post-Deployment script is no longer being executed.
This is ok, as ultimately I want to script/automate the Publish/Deploy of the ADMIR Project and so I've moved to using SQLPackage to perform the Publish.
Again, this appears to work fine - My SQL Package batch command will execute and log errors/output (as applicable) and Publish my Build dacpac to the target Server successfully. However, it seems to completely ignore the Post-Deployment script. In addition, the output reports that the SQL Command Variables are not set.
These are the various scripts and configuration settings:
SQLPackage batch command:
::Publish the ADMIR database using the ADMIR.DEV.Publish.xml Profile.
"C:\Program Files (x86)\Microsoft SQL Server\120\DAC\bin\SqlPackage.exe"
/Action:Publish
/Profile:"C:\Source Control\ADMIR\ReleaseControl\ADMIR.DEV.publish.xml"
/SourceFile:"C:\Source Control\ADMIR\Build\ADMIR.dacpac"
2> "C:\Source Control\ADMIR\ReleaseControl\Publish_Error.txt"
> "C:\Source Control\ADMIR\ReleaseControl\Publish_Output.txt"
ADMIR.DEV.publish.xml Publishing Profile:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<IncludeCompositeObjects>True</IncludeCompositeObjects>
<TargetDatabaseName>ADMIR</TargetDatabaseName>
<DeployScriptFileName>ADMIR.sql</DeployScriptFileName>
<TargetConnectionString>Data Source=FOOBAR;Integrated Security=True;Pooling=False</TargetConnectionString>
<ProfileVersionNumber>1</ProfileVersionNumber>
</PropertyGroup>
<ItemGroup>
<SqlCmdVariable Include="ADMIRDatabaseName">
<Value>ADMIR</Value>
</SqlCmdVariable>
<SqlCmdVariable Include="XYZDatabaseName">
<Value>XYZDEV</Value>
</SqlCmdVariable>
</ItemGroup>
</Project>
Script.PostDeploy.sql (first few lines):
/* ADMIR Post-Deployment Script Template */
PRINT N'Post Deploy Script started:'+CAST(GETDATE() AS NVARCHAR(20))+N'.'
PRINT N'SQLCMD Variable $ADMIRDBName:'+[$(ADMIRDatabaseName)]+N'.'
PRINT N'SQLCMD Variable $XYZDBName:'+[$(XYZDatabaseName)]+N'.'
/* Purge existing data as we'll repopulate with what is required for a fresh deploy */
PRINT N'Purging existing table data...'
DELETE FROM [ADMIR].[Engine].[ProcessingStatus]
DELETE FROM [ADMIR].[Engine].[DataField]
DELETE FROM [ADMIR].[Engine].[DataKeyField]
...
...
When running a Build, I can see the ADMIR_Create.sql script does contain all the Post-Deploy statements. i.e. I can see the above lines of code in this file.
** ADMIR.sqlproj file contains the Post-Deploy scripts and the SQL Command Variables:**
<ItemGroup>
...
...
<PostDeploy Include="ReleaseControl\Script.PostDeploy.sql" />
</ItemGroup>
...
...
<ItemGroup>
<SqlCmdVariable Include="ADMIRDatabaseName">
<DefaultValue>ADMIR</DefaultValue>
<Value>$(SqlCmdVar__7)</Value>
</SqlCmdVariable>
<SqlCmdVariable Include="XYZDatabaseName">
<DefaultValue>XYZDEV</DefaultValue>
<Value>$(SqlCmdVar__6)</Value>
</SqlCmdVariable>
Publish log, Publish_Output.txt:
Publishing to database 'ADMIR' on server 'FOOBAR'.
Initializing deployment (Start)
*** The following SqlCmd variables are not defined in the target scripts: ADMIRDatabaseName XYZDatabaseName.
Initializing deployment (Complete)
Analyzing deployment plan (Start)
Analyzing deployment plan (Complete)
Updating database (Start)
Update complete.
Updating database (Complete)
Successfully published database.
So:
Why is the Post-Deployment script not being executed? I'm not seeing any logging information relating to it.
Why does the SQLPackage output log state that the SQL Command Variables are not defined, when they are in the Publish profile?
Possibly related to 2. Is the .sqlproj file correct in have the values of the SQL Command Variables as variables themselves e.g. $(SqlCmdVar__7)? where __6 & __7 coming from?
Thanks for any assistance!
I found that a new .sqlproj file created in Visual Studio 2017 did not include "default" Predeployment.sql or PostDeployment.sql files, so I added one manually.
Publishing did not execute the PostDeployment.sql because the file's Build Action was set to None. Changing the Build Action to PostDeploy fixed the issue.
As per my comment, the issue logged in MSDN Connect, appears to be cause. i.e. an SQLCMD variable with the same name as the dacpac for the database is resulting in the script not being executed. When I remove this reference, and change the related SQLCMD variables to literal values for the DB Name, then the project builds and the post-deploy script is executed as required.

Where are the pre- and post-deployment scripts in VS2013 Database Projects?

According to everything I've read about the "SQL Server Database Project" template in Visual Studio 2013, there should be a 'Scripts' folder when you create the project containing pre- and -post deployment SQL scripts that you want to be executed on publication.
When I create a new, blank DB project, the only structures I can see are the 'properties' and 'References' nodes:
I've tried manually adding SQL scripts according in the correct folders according to the articles I've read online, but still no joy. I would say the MSDN docs seem to refer mainly to VS2010 or at best 2012 DB projects, for example this MSDN page which assumes that the 'Scripts' folder has been populated as part of the project creation.
I've also tried installing the November 2014 updates to SQL Server database tooling via the VS2013 updates menu, and creating a new DB project, with the same results.
If you try to add a new item to the project, two of the script templates on offer are a Pre-Deployment script and a Post-deployment script.
Alternatively, if you just add a normal script to the project, you can later mark it for Pre-/Post- deployment via the "Build Action" property:
There are no longer dedicated folders for these scripts.

SQL Server Database Project's Deploy & Publish Targets

There are two distinct targets, Deploy and Publish, in SSDT project alongside Build but I can't find anything on what's the difference between them and which one should be used when? From what I can gather .dbproj used to use Deploy but .sqlproj supposedly replaced it with Publish, how come it's still there and what is it still used for?
$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\SSDT\Microsoft.Data.Tools.Schema.SqlTasks.targets
<UsingTask TaskName="SqlBuildTask" AssemblyFile="$(SqlServerRedistPath)\Microsoft.Data.Tools.Schema.Tasks.Sql.12.dll" />
<UsingTask TaskName="SqlDeployTask" AssemblyFile="$(SqlServerRedistPath)\Microsoft.Data.Tools.Schema.Tasks.Sql.12.dll" />
<UsingTask TaskName="SqlPublishTask" AssemblyFile="$(SqlServerRedistPath)\Microsoft.Data.Tools.Schema.Tasks.Sql.12.dll" />
<DeployDependsOn>
BeforeDeploy;
PreDeployEvent;
SqlDeploy;
PostDeployEvent;
AfterDeploy
</DeployDependsOn>
<Target Name="Deploy" DependsOnTargets="$(DeployDependsOn)">
<PublishDependsOn>
BeforePublish;
PrePublishEvent;
SqlPublish;
PostPublishEvent;
AfterPublish
</PublishDependsOn>
<Target Name="Publish" DependsOnTargets="$(PublishDependsOn)">
The main difference is that Publish is used when you have a publish profile file describing the connection information, whereas Deploy is used when you do not have this file / do not wish to use one. Both are used by SSDT, the Deploy task is not just there for backward compatibility.
The Publish task takes in a "PublishProfile" property that specifies a saved publish profile xml file. The required server name, database name and other properties needed are read from that file. See this forum post for some more information.
The deploy task is used when you do not have a publish profile (for example, the F5 debug deployment uses this, if I recall correctly). I believe this requires that the server, database name and other properties be set explicitly.
I'm assuming it's for backwards compatibility. Upgrading a SQL2008 dbproj to SQL2012 sqlproj after installing the SSDT update, you'd probably still want your dbprojects to function correctly until they are converted.

How to configure Visual Studio 2012 Database Project to Build deployment .sql file

Does anyone know How to configure Visual Studio 2012 Database Project to Build a deployment .sql file package.
I'm attempting to not have to use the .dacpac as my only deployment option.
e.g. generate a .sql file of all database schema alters.
If you're trying to do this automatically, you'll want to use the SQLPackage command to generate a script. If you want to do this within the IDE, publish the database and choose the option to generate a script instead of publishing the changes.
I'll usually build the project first with msbuild. That will generate a dacpac against which I can run the SQLPackage command.
My batch file looks something like this:
msbuild .\MyDB\MyDB.sqlproj /t:build /p:Configuration="Local"
sqlpackage /a:DeployReport /tsn:FTPROD /sf:.\MyDB\sql\Local\MyDB.dacpac /pr:.\MyDB\Publish\Production.publish.xml /op:.\Release\MyDB-Production.xml
sqlpackage /a:script /tsn:DBServer /sf:.\MyDB\sql\Local\MyDB.dacpac /pr:.\MyDB\Publish\Production.publish.xml /op:.\Release\MyDB-Production.sql
The first line builds the project. The second creates a deploy report so I can easily see what's going to be changed. The last generates the script. Your paths may vary so you'll need to tweak as appropriate for your environment. You'll need to be able to access the database against which the script will be run in order to generate the script.

Resources