SSDT Publish does not use connection string from MSBuild command line - msbuild

I'm trying to publish a .sqlproj project using MSBuild from my (TeamCity) build server. I use a .publish.xml for setting up most of the publish settings, but want to specify the connection string on the command line, so I can securely store it in TeamCity properties.
However, it seems like MSBuild doesn't use the connection string specified on the command line at all, and insists on using the one in the .publish.xml. I've tried removing it from and emptying it in the XML, but MSBuild doesn't like that so much, and spits back Before you can deploy a database, you must specify a connection string in the project properties or at a command prompt., which, of course, is exactly what I'm trying to do...
The full command used to publish is as follows:
msbuild /t:Build;Publish /p:SqlPublishProfilePath="c:\path\to\profile.publish.xml" /p:Publish_TargetConnectionString="the connection string" "c:\path\to\project.sqlproj"
I've tried using /p:TargetConnectionString and /p:SqlPublishProfilePath="c:\path\to\profile.publish.xml";Publish_TargetConnectionString="the connection string" as well, but to no avail.
The .publish.xml is as follows:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<IncludeCompositeObjects>True</IncludeCompositeObjects>
<TargetDatabaseName><!-- snip --></TargetDatabaseName>
<DeployScriptFileName><!-- snip -->.sql</DeployScriptFileName>
<TargetConnectionString><!-- snip snip --></TargetConnectionString>
<ScriptDatabaseOptions>False</ScriptDatabaseOptions>
<BlockOnPossibleDataLoss>False</BlockOnPossibleDataLoss>
<DeployDatabaseInSingleUserMode>False</DeployDatabaseInSingleUserMode>
<ProfileVersionNumber>1</ProfileVersionNumber>
</PropertyGroup>
</Project>
SSDT is at the latest version I could download from Microsoft as of today.
Has anyone ever gotten this to work?

As said above in the comments, I resorted to using MSBuild for building the .dacpac and sqlpackage.exe to publish it to SQL Server, and that works fine.

Related

How to deploy website using visual publish profile and Cake script

I would like to use Cake script to automate my build and publish process. I started some tests with local publish but I was unable to make my Cake script to use publish profile that I've created in Visual Studio. Is it possible at all?
Here is my publish profile file:
<?xml version="1.0" encoding="utf-8"?>
<!--
This file is used by the publish/package process of your Web project. You can customize the behavior of this process
by editing this MSBuild file. In order to learn more about this please visit https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<WebPublishMethod>FileSystem</WebPublishMethod>
<LastUsedBuildConfiguration>Debug</LastUsedBuildConfiguration>
<LastUsedPlatform>Any CPU</LastUsedPlatform>
<SiteUrlToLaunchAfterPublish />
<LaunchSiteAfterPublish>True</LaunchSiteAfterPublish>
<ExcludeApp_Data>False</ExcludeApp_Data>
<publishUrl>C:\Users\radoslaw.stolarczyk\Desktop\CakeWebSite</publishUrl>
<DeleteExistingFiles>True</DeleteExistingFiles>
</PropertyGroup>
</Project>
And here are two versions of my cake script:
I tried to publish my website using pubslish profile (CakeProfile.pubxml). It's not working at all the website is published to the folder inside the project folder.
MSBuild("./CakeWebSite.sln", settings =>
settings.WithProperty("DeployOnBuild", "true")
.WithProperty("PublishProfile", "CakeProfile"));
It didn't worked well so I tried to set the same properties as in publish file:
MSBuild("./CakeWebSite.sln", new MSBuildSettings()
.WithProperty("WebPublishMethod", "FileSystem")
.WithProperty("LastUsedBuildConfiguration", "Debug")
.WithProperty("LastUsedPlatform", "Any CPU")
.WithProperty("ExcludeApp_Data", "False")
.WithProperty("publishUrl", cakePublishPath)
.WithProperty("OutDir", cakePublishPath)
.WithProperty("DeleteExistingFiles", "True")
Second option worked better but still not sa expected. The build output from Cake script is different then from Visual Studio publish option as you can see on the picture below:
Publish outputs
Maybe it's not a big deal for example with Cake I got precompiledApp.config and with Visual studio I don't but still I would like to get the same output from both publish methods.
So my question is: Is it possible to use publish profile from Visual Studio in Cake script or to recive the same output (from Cake, and VS) and how?
I have the configuration below that works for me. I use the project file instead of the solution.
MSBuild(prjFile, new MSBuildSettings()
.UseToolVersion(MSBuildToolVersion.VS2017)
.WithTarget("Package")
.WithProperty("Configuration",configuration)
.WithProperty("_PackageTempDir", stagingDir)
.WithProperty("SolutionDir","../")
where configuration is:
var configuration = Argument("configuration", "Release");

.NET Core console application is not creating an EXE file

I'm trying to publish a .NET Core console application following this tutorial, but when I publish, I don't get an executable file in the PublishOutput folder (I get a DLL file). I've also read this article.
My project file looks like this:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp1.1</TargetFramework>
</PropertyGroup>
</Project>
It seems pretty easy and straightforward, but what am I doing wrong?
Yeah, this is a weird one. I am still trying to work through it. I did find there seems to be a delay in getting the functionality from the CLI to Visual Studio 2017: This Stack Overflow article talks about that.
Also, there is ongoing confusion around exactly what Output type means since it is not what we all think. This GitHub issue talks about it.
I tried this on the Hello, World! template that Visual Studio provides. Change your .csproj file to the following:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!--<OutputType>Exe</OutputType>-->
<TargetFramework>netcoreapp1.1</TargetFramework>
<RuntimeIdentifiers>win10-x64</RuntimeIdentifiers>
</PropertyGroup>
</Project>
I don't think the OutputType matters more than the RuntimeIdentifiers property.
Then using the console, run dotnet restore followed by dotnet publish -c release -r win10-x64
This should generate an EXE file under \bin\Release\netcoreapp1.1\win10-x64\publish
View this article from the same person in your first link.

Use $(SolutionName) in the MsBuild commandline parameters

In order to emulate the "PerProject" option in TFS 2013's XAML build in the new Build 2015 task based builds, I'd like to be able to pass the SolutionName to the msbuild commandline arguments without having to manually set it every time.
I'd like to do something like:
/p:OutputPath=$(Build.BinariesDirectory)\$(SolutionName)\
Where I'd like MsBuild to infer the $(SolutionName) parameter. But when passing this on the commandline, the new task runner will substitute the $(Build.BinariesDirectory) with the correct target path and leaves $(SolutionName) alone. Unfortunately MsBuild subsequently also leaves the property alone:
Copying file from "obj\Debug\TFSBuild.exe" to "bin\debug\$(SolutionName)\TFSBuild.exe".
TFSBuild -> b\$(SolutionName)\TFSBuild.exe
Copying file from "obj\Debug\TFSBuild.pdb" to "b\$(SolutionName)\TFSBuild.pdb".
I can't remember a way to pass a property to the commandline and have it do late-expansion... Any tips?
For those looking to emulate SingleFolder or AsConfigured, those are easy:
SingleFolder -> /p:OutputPath="$(Build.BinariesDirectory)"
Asconfigured -> don't pass OutputPath
PerProject -> /p:OutputPath="$(Build.BinariesDirectory)\HARDCODESOLUTIONNAME"
As I feared there doesn't seem to be a simple way to override a property from the commandline and "inject" the value of another property into it during the evaluation stage.
There are a few ways to get around it, but they're not ideal and certainly not universal for each language supported by MsBuild. A pity.
I've debugged the MsBuild targets files and found a solution to reproduce the old behaviour from the 2005/2008 era. Not entirely per solution, but it does redirect projects into a subfolder.
/p:GenerateProjectSpecificOutputFolder=true /p:OutDirWasSpecified=true
/p:OutputPath=$(Build.BinariesDirectory)
Normally, $(SolutionName) is defined when executing solution-level MSBuild pipelines, such as running dotnet restore in the root solution directory.
To make $(SolutionName) available for project-level MSBuild pipelines, add a Directory.Build.props file in the root of your solution with the following contents:
<Project>
<PropertyGroup>
<SolutionName Condition="'$(SolutionName)' == ''">
$([System.IO.Path]::GetFileNameWithoutExtension($([System.IO.Directory]::GetFiles("$(MSBuildThisFileDirectory)", "*.sln")[0])))
</SolutionName>
</PropertyGroup>
</Project>
Now $(SolutionName) will be defined even when executing project-level MSBuild pipelines.
This answer works best when there is exactly one solution file in the root of the solution directory. You'll need to massage the above a bit for other project structures.
Of course, you can also be lazy and specify the solution name directly, but this opens up the possibility of refactoring issues (need to remember to update this file if the solution name changes).
<Project>
<PropertyGroup>
<SolutionName Condition="'$(SolutionName)' == ''">
MySolutionName
</SolutionName>
</PropertyGroup>
</Project>
One solution is to mimic such 'late evaluation' yourself by altering OutputPath withing the projectfile. To do without manually changing each single project file you can use the CustomBeforeMicrosoftCSharpTargets extension point. Which is an fancy way of saying it is just a property which when found and pointing to an existing file, will lead that file to be imported somewhere before all the actual build logic. Here's the idea: create a file like paths.targets somewhere - either include it in source control or you can generate it on the fly as part of the build process. Contents:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<OutputPath Condition="'$(OutputPathBaseDir)'!=''">$(OutputPathBaseDir)\$(SolutionName)</OutputPath>
</PropertyGroup>
</Project>
So this just overrides OutputPath to some base dir + solutionname. Then if you build the solution like
msbuild my.sln /p:CustomBeforeMicrosoftCSharpTargets=paths.targets;
OutputPathBaseDir=$(Build.BinariesDirectory)
each project will import the paths.targets file and set output property to valueOfBinariesDirectory\my which I think is exactly what you are after.
You are right that TFS vNext build can't recognize $(SolutionName) in OutputPath, as $(SolutionName) doesn't list in the Predefined variables.
As an alternative, we may name the build definition with the solution name, then specify the MSBuild argument to: /p:OutputPath="$(Build.BinariesDirectory)\$(Build.DefinitionName)"in this way, we can get the output under the solution name.

MSBuild + .dbproj

i am trying to deploy db project through msbuild.
i am getting below error
MSBuild - Deploy DB project
C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe
/nologo
/noconsolelogger "C:\Builds\2\Test\ePlanner\Sources\WebSiteBuildTest\Database\ePlanner\ePlanner\ePlanner.dbproj"
/m:1
/t:"Deploy"
/fl
/flp:"logfile=deploymentdb.log;encoding=Unicode;verbosity=normal"
/p:TargetDatabase="ePlanner3";"TargetConnectionString="Data Source=SACHIN-PC%3Buser=sa%3Bpwd=M0nday!";"DefaultDataPath="C:\Program Files\Microsoft SQL Server\MSSQL10.MSSQLSERVER\MSSQL\DATA";DeployToDatabase=true
/p:OutDir="C:\Builds\2\Test\ePlanner\Binaries\\"
/p:Configuration="Debug"
/p:RunCodeAnalysis="False"
/dl:WorkflowCentralLogger,"C:\Program Files\Microsoft Team Foundation Server 2010\Tools\Microsoft.TeamFoundation.Build.Server.Logger.dll";"Verbosity=Normal;BuildUri=vstfs:///Build/Build/53;InformationNodeId=10631;TFSUrl=http://sachin-pc:8080/tfs/DefaultCollection;"*WorkflowForwardingLogger,"C:\Program Files\Microsoft Team Foundation Server 2010\Tools\Microsoft.TeamFoundation.Build.Server.Logger.dll";"Verbosity=Normal;"
MSBUILD : error MSB1008: Only one project can be specified.
Switch: Source=SACHIN-PC%3Buser=sa%3Bpwd=M0nday!;
DefaultDataPath=C:\Program Files\Microsoft SQL Server\MSSQL10.MSSQLSERVER\MSSQL\DATA;DeployToDatabase=true
For switch syntax, type "MSBuild /help"
TF270015: 'MSBuild.exe' returned an unexpected exit code. Expected '0'; actual '1'.
Please help me out
In order to run a .dbproj in MsBuild the machine needs to have VisualStudio with the database project components installed, something to do with licensing. I cannot tell from the error dump that you provided if this is what is causing you the issue but it solved our issue when building on our build server.
You've got conflicting quotation marks in your command line:
/p:TargetDatabase="ePlanner3";"TargetConnectionString="Data Source=SACHIN-PC%3Buser=sa%3Bpwd=M0nday!";"DefaultDataPath="C:\Program Files\Microsoft SQL Server\MSSQL10.MSSQLSERVER\MSSQL\DATA";DeployToDatabase=true
The TargetConnectionString property is completely surrounded in double quotes, but then you use double quotes to specify the connection string. Try escaping the quotes for the connection string.
Consider converting the project to .sqlproj. This new format provides a "Publish" MSBuild target. You just need to specify at SqlPublishProfilePath property a publish profile file with your deployment options.
Look that sample:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="ProjectsBuild">
<ItemGroup>
<!-- Defines a collection of projects to deploy -->
<ProjectsToPublish Include="SQLServer.sqlproj">
<Properties>SqlPublishProfilePath=myprofile.publish.xml</Properties>
</ProjectsToPublish>
<!-- Runs the target Publish -->
<MSBuild Projects="#(ProjectsToPublish)" Targets="Publish"/>
</ItemGroup>
</Project>
Visual Studio 2010/2012 needs the SSDT (Sql Server Data Tools - ) installed to understand .sqlproj. Your build server too. It will install the MSBuild .targets extensions that contains the target "Publish".
Publish profile example (it's a MSBuild project):
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<CreateNewDatabase>True</CreateNewDatabase>
<TargetDatabaseName>DB1</TargetDatabaseName>
<TargetConnectionString>Data Source=server1\dev;Integrated Security=True;Pooling=False</TargetConnectionString>
<BackupDatabaseBeforeChanges>False</BackupDatabaseBeforeChanges>
<BlockOnPossibleDataLoss>False</BlockOnPossibleDataLoss>
<NoAlterStatementsToChangeCLRTypes>False</NoAlterStatementsToChangeCLRTypes>
<DropObjectsNotInSource>True</DropObjectsNotInSource>
<DeployDatabaseInSingleUserMode>False</DeployDatabaseInSingleUserMode>
<DeployScriptFileName>db1.sql</DeployScriptFileName>
<ProfileVersionNumber>1</ProfileVersionNumber>
</PropertyGroup>
</Project>

Can MS Deploy do a package and transform, but not deploy?

Using msbuild in .NET 4.0, I can build web project with the "Package" target, and it does a nice job of putting the package in a zip file. But, when I look at the web.config in there, it's not transformed, it has "$(ReplacableToken_Web_SiteConnection-Web.config Connection String_0)"
I can run the "TransformWebConfig" target and it will do the proper transform, but just in its own silo.
I can also run the "Build" target and pass the "DeployOnBuild=True;DeployTarget=MSDeployPublish" properties and it will deploy the package on my server with the proper web.config transform done.
But, if I want to manually deploy the package to the server, how do I do a "Package" with a "TransformWebConfig" so that the zip file has the final web.config in there?
If you want to skip this from happening then you need to set a property in your build. You can do this in two ways
Edit your project file
Create a .wpp.targets file
I would recommend #2. For this case create a new file in the same directory as your project file with the name {ProjectName}.wpp.targets where {ProjectName} is the name of your project. Then inside of this file you should place the following contents.
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
ToolsVersion="4.0">
<PropertyGroup>
<AutoParameterizationWebConfigConnectionStrings>false</AutoParameterizationWebConfigConnectionStrings>
</PropertyGroup>
</Project>
In this case you are setting the property AutoParameterizationWebConfigConnectionStrings which tells the Web Publishing Pipeline to not insert those {} placeholders in the web.config for the connection strings.
The way we do this is by modifiying the project build to do the transform prior to packaging it up.
The Target is call TransformXml and is a part of Microsoft.Web.Publishing.Tasks.dll
In your own targets its
<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.Tasks.dll" />
But it will be included in a default VS C# build.
So
<TransformXml Source="web.config" Transform="web.release.config" Destination="$(DeployPath)\web.config" />
Does the trick for us.
Set up those paths with the right ItemGroup ("content" most likely) and make sure that target gets fired prior to the call to Package in your .csproj, and the build output will contain a "Web.config" like normal, with the right transformed values.
Alternatively (we've used this for packages that need to be everything to everyone), you can use that trick to do ALL the transforms and include each of them in the final package.
Then you call Msdeploy manually and use its skip and replace directives (forgot the technical term) to only output the right one at deploy-time
Assuming you have a web.usethisone.config in your package, that looks like
-skip:objectname=filepath,absolutepath=web\..*\.config
-replace:objectName=filepath,match=.*web\.usethisone\.config,replace=web.config