MSBuild. Check if windows service is installed - deployment

I'm new to msbuild and currently I'm trying to create msbuild script that will deploy my C# windows service to remote test server.
I'm thinking about using sc.exe utility for this purpose. Reading about it I didn't find a way to check whether windows service is installed on a remote server. If the service is installed then I need to stop it and update necessary files, otherwise I need to register the service.
P.S. For release builds I plan to use WiX to create MSI package.

You need MSBuild Comminity Tasks.
In latest build exists an example in MSBuild.Community.Tasks.v1.2.0.306\Source\Services.proj.
It will solve first part of your question:
<PropertyGroup>
<MSBuildCommunityTasksPath>$(MSBuildProjectDirectory)\MSBuild.Community.Tasks\bin\Debug</MSBuildCommunityTasksPath>
</PropertyGroup>
<Import Project="$(MSBuildProjectDirectory)\MSBuild.Community.Tasks\MSBuild.Community.Tasks.Targets"/>
<Target Name="Test">
<CallTarget Targets="DoesServiceExist" />
<CallTarget Targets="GetServiceStatus" />
<CallTarget Targets="ServiceControllerStuff" />
</Target>
<Target Name="DoesServiceExist">
<ServiceQuery ServiceName="MSSQLServer123" MachineName="127.0.0.1" >
<Output TaskParameter="Exists" PropertyName="Exists" />
<Output TaskParameter="Status" PropertyName="ServiceStatus" />
</ServiceQuery>
<Message Text="MSSQLServer Service Exists: $(Exists) - Status: $(ServiceStatus)"/>
</Target>
<Target Name="GetServiceStatus">
<ServiceQuery ServiceName="MSSQLServer" MachineName="127.0.0.1">
<Output TaskParameter="Status" PropertyName="ResultStatus" />
</ServiceQuery>
<Message Text="MSSQLServer Service Status: $(ResultStatus)"/>
</Target>
<Target Name="ServiceControllerStuff">
<ServiceController ServiceName="aspnet_state" MachineName="127.0.0.1" Action="Start" />
<ServiceController ServiceName="aspnet_state" MachineName="127.0.0.1" Action="Stop" />
</Target>
Those MSBuild task is just a wrapper around .Net class ServiceController. Take a look for documentation to understand how it works and how you can configure it in details.
Second part includes installing service. For that purpose sc.exe suits very well.

A complete solution is posted here. May help future visitors.
Update: Link updated as the other blogging service went down.

Related

Azure DevOps build pipeline for Service Fabric Guest .Net Core 3.1 API exe fails on creating package

I have a .NET Core 3.1 API that I am attempting to deploy as a Guest Executable in Service Fabric using an Azure DevOps build pipeline. The platform for all projects/configurations is x64.
I have locally tested the API and also locally tested the API running as a guest executable within service fabric. I am able to build/rebuild the API and I am able to package the service fabric application.
In my build pipeline, I have the following steps shown in the picture below.
The Create Service Fabric Package is defined in the picture below
When the build pipeline runs, it always fails with the error:
##[error]AppraisalStatusUpdatesContainer\AppraisalStatusUpdatesContainer.sfproj(0,0): Error MSB4057: The target "Package" does not exist in the project.
I cannot find any documentation on what to do to solve this issue and have spent a couple of days trying. Does anyone know how to get this to work?
Edit 1
The sfproj is provided below
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\Microsoft.VisualStudio.Azure.Fabric.MSBuild.1.6.10\build\Microsoft.VisualStudio.Azure.Fabric.Application.props" Condition="Exists('..\packages\Microsoft.VisualStudio.Azure.Fabric.MSBuild.1.6.10\build\Microsoft.VisualStudio.Azure.Fabric.Application.props')" />
<PropertyGroup Label="Globals">
<ProjectGuid>ffefa7ed-cf72-4780-9910-816deed2ed4f</ProjectGuid>
<ProjectVersion>2.5</ProjectVersion>
<MinToolsVersion>1.5</MinToolsVersion>
<SupportedMSBuildNuGetPackageVersion>1.6.10</SupportedMSBuildNuGetPackageVersion>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
</PropertyGroup>
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<None Include="ApplicationPackageRoot\ApplicationManifest.xml" />
<None Include="ApplicationParameters\Prod.xml" />
<None Include="ApplicationParameters\QA.xml" />
<None Include="ApplicationParameters\UAT.xml" />
<None Include="ApplicationParameters\Local.1Node.xml" />
<None Include="ApplicationParameters\Local.5Node.xml" />
<None Include="PublishProfiles\QA.xml" />
<None Include="PublishProfiles\UAT.xml" />
<None Include="PublishProfiles\Prod.xml" />
<None Include="PublishProfiles\Local.1Node.xml" />
<None Include="PublishProfiles\Local.5Node.xml" />
<None Include="Scripts\Deploy-FabricApplication.ps1" />
</ItemGroup>
<ItemGroup>
<Content Include="..\AppraisalStatusUpdates\bin\Release\netcoreapp3.1\publish\**\*.*">
<Link>ApplicationPackageRoot\AppraisalStatusUpdatesContainerPkg\Code\%(RecursiveDir)%(Filename)%(Extension)</Link>
</Content>
<Content Include="ApplicationPackageRoot\AppraisalStatusUpdatesContainerPkg\Config\Settings.xml" />
<Content Include="ApplicationPackageRoot\AppraisalStatusUpdatesContainerPkg\ServiceManifest.xml" />
<Content Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.Common.targets" />
<PropertyGroup>
<ApplicationProjectTargetsPath>$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Service Fabric Tools\Microsoft.VisualStudio.Azure.Fabric.ApplicationProject.targets</ApplicationProjectTargetsPath>
</PropertyGroup>
<Import Project="$(ApplicationProjectTargetsPath)" Condition="Exists('$(ApplicationProjectTargetsPath)')" />
<Import Project="..\packages\Microsoft.VisualStudio.Azure.Fabric.MSBuild.1.6.10\build\Microsoft.VisualStudio.Azure.Fabric.Application.targets" Condition="Exists('..\packages\Microsoft.VisualStudio.Azure.Fabric.MSBuild.1.6.10\build\Microsoft.VisualStudio.Azure.Fabric.Application.targets')" />
<Target Name="ValidateMSBuildFiles" BeforeTargets="PrepareForBuild">
<Error Condition="!Exists('..\packages\Microsoft.VisualStudio.Azure.Fabric.MSBuild.1.6.10\build\Microsoft.VisualStudio.Azure.Fabric.Application.props')" Text="Unable to find the '..\packages\Microsoft.VisualStudio.Azure.Fabric.MSBuild.1.6.10\build\Microsoft.VisualStudio.Azure.Fabric.Application.props' file. Please restore the 'Microsoft.VisualStudio.Azure.Fabric.MSBuild' Nuget package." />
<Error Condition="!Exists('..\packages\Microsoft.VisualStudio.Azure.Fabric.MSBuild.1.6.10\build\Microsoft.VisualStudio.Azure.Fabric.Application.targets')" Text="Unable to find the '..\packages\Microsoft.VisualStudio.Azure.Fabric.MSBuild.1.6.10\build\Microsoft.VisualStudio.Azure.Fabric.Application.targets' file. Please restore the 'Microsoft.VisualStudio.Azure.Fabric.MSBuild' Nuget package." />
</Target>
</Project>
Edit 2
#LeoLiu-MSFT, I have attempted the approach you mentioned. I am not getting the original error, but am now attempting to resolve the subsequent issues that resulted. Also, I am doing dotnet publish and dotnet test tasks before this step. It seems like the publish step is unnecessary except that it is needed for running the tests.
My msbuild step is now as follows
This results in the exception below:
##[error]C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\MSBuild\Current\Bin\amd64\Microsoft.Common.CurrentVersion.targets(777,5): Error : The OutputPath property is not set for project 'AppraisalStatusUpdatesContainer.sfproj'. Please check to make sure that you have specified a valid combination of Configuration and Platform for this project. Configuration='Release' Platform='x64'. You may be seeing this message because you are trying to build a project without a solution file, and have specified a non-default Configuration or Platform that doesn't exist for this project.
I have a custom Powershell script in my service fabric repositories for this.
It does a separate nuget restore for .sfproj.
The script restore-sf.ps1:
Push-Location $PSScriptRoot
$ProjectFolder = "..\src\YourProjectFolder"
$PackageFolder = [System.IO.Path]::GetFullPath((Join-Path $PSScriptRoot "..\Packages"))
Push-Location $ProjectFolder
nuget restore YourServiceFabricProject.sfproj -PackagesDirectory $PackageFolder -MSBuildVersion 15
Pop-Location
Pop-Location
My folder structure:
├───Root
├───packages
├───src
├───YourProjectFolder
├───YourServiceFabricProject.sfproj
├───scripts
├───restore-sf.ps1
Depending on your folder structure, you'll need to change the paths in the script.
In the pipeline one of the first steps is to call the script, to restore the Microsoft.VisualStudio.Azure.Fabric.MSBuild package.
The script assumes nuget is available in the path.
Azure DevOps build pipeline for Service Fabric Guest .Net Core 3.1 API exe fails on creating package
According to the error:
MSB4057: The target "Package" does not exist in the project.
When you are using MSBuild for a solution of projects (.sln) with target Package, but not all projects have the Package task defined. That may be one of the reasons why you get this error.
To resolve this issue, we could add following custom target in the .sfproj file:
<Target Name="ForcePackageTarget" AfterTargets="Build" Condition="'$(ForcePackageTarget)' =='true'">
<CallTarget Targets="Package"/>
</Target>
Then add /p:ForcePackageTarget=true as an argument to the msbuild build task.
Please check this thread and this post for some more details.

Get the location of NuGet packages

In my company we have some home made tools that are used in the build process when building other projects.
I need to use these tools in VS2017 BeforeBuild and AfterBuild scripts and it must work in MS Build as well.
The tools are distributed as NuGet packages and most of our projects are ported to PackageReference instead of Packages.config
I know that the current installation of MyTool (version X.Y.Z) is at C:\Users\Me\.nuget\packages\MyTool\X.Y.Z, but how do I reference it in my project file, so it also works when the next version is released?
I think C:\Users\Me\.nuget\packages can be replaced with $(NuGetPackageRoot), but what to do to always reference the version installed in the project?
Some Nuget packages seem to put contributions into MyProject.csproj.nuget.g.props and MyProject.csproj.nuget.g.targets in the obj folder, but I can find very little useful information about these files.
Inside a target, you can use this to create a property based on an item:
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="11.0.1" />
</ItemGroup>
<Target Name="PrintStuff" AfterTargets="AfterBuild">
<PropertyGroup>
<NewtonsoftJsonVersion Condition="'%(PackageReference.Identity)' == 'Newtonsoft.Json'">%(PackageReference.Version)</NewtonsoftJsonVersion>
<NewtonsoftJsonPath>$(NuGetPackageRoot)newtonsoft.json\$(NewtonsoftJsonVersion)\</NewtonsoftJsonPath>
</PropertyGroup>
<Message Importance="high" Text="JSON.NET version: $(NewtonsoftJsonVersion)" />
<Message Importance="high" Text="JSON.NET path: $(NewtonsoftJsonPath)" />
<Exec Command="ls" WorkingDirectory="$(NewtonsoftJsonPath)" Condition="'$(OS)' != 'Windows_NT'" />
<Exec Command="dir" WorkingDirectory="$(NewtonsoftJsonPath)" Condition="'$(OS)' == 'Windows_NT'" />
</Target>

CruiseControl.net, send dynamic values to some files

I have been searching for a few days, I have also asked a question on the cc.net forum, but still don't have the answer.
My task is to fill Web.config with specific values during the building using cc.net. Here is the example:
I'm developing an ASP.NET website, I have a Web.config with some configuration, e.g. connection string:
<add name="ContextName" connectionString="Data Source=.\SQLEXPRESS;Initial Catalog=DatabaseName;User Id = UserName;Password=UserPassword;" providerName="System.Data.SqlClient" />
After the building I want to replace some values and make the config look like this:
<add name="ContextName" connectionString="Data Source=%SERVER%;Initial Catalog=%DATABASE%;User Id = %USER%;Password=%PASSWORD%;" providerName="System.Data.SqlClient" />
I tried this solution, but it didn't help me (or maybe I didn't understand how to use it properly).
Please help me to solve the task.
Thanks!
You can try to use this Config Transformation Tool which is XDT transformation command tool based on XDT (web.config) transform engine, which allows you to run XDT transformation on any XML files. You can use ccnet's task block to run it after your msbuild
<tasks>
<exec>
</exec>
</tasks>
More on ccnet executable task see here.
For more information on XDT transformation see this link in MSDN library
You can create a target on your build file to update the web.config and call that target after the build. I use something similar in my build files, here is an example:
<target name="update-config" >
<property name="export.config" value="" unless="${property::exists('export.config')}" />
<call target="${config-settings}" /> <!-- test or stage -->
<xmlpoke file="${export.config}" xpath="/configuration/appSettings/add[#key='ContextName']/#connectionString" value="${configValue.connectionString}" failonerror="true" />
</target>
<target name="test">
<property name="configValue.connectionString" value="test connection string here" />
</target>
<target name="stage">
<property name="configValue.connectionString" value="stage connection string here" />
</target>
After executing the target that compile your code and export you can run the target update-config, in this case I'm expecting a variable export.config with the path of the exported web.config then calling another target that sets the the value of the connectionstring variable (this can be target test or stage) and finally xmlpoke the web.config with the value.
Hope this helps!

Is there a way to create more than one deployment with msbuild on a single build?

I am using msbuild command line multiple times to create a deployment zip file for my dev / test / production websites. I have already configured the parameters and configuration xml for each one. I want to know if i can condense my 3 calls to msbuild down to one, and have it build all three at once?
right now i have to run
msbuild.exe myproject.sln /T:Build /p:DeployOnBuild=True /p:PublishProfile="Dev Server"
msbuild.exe myproject.sln /T:Build /p:DeployOnBuild=True /p:PublishProfile="Prod Server"
etc
The continuous deployment solution i'm using (bamboo) is struggling with this multiple call to msbuild for some reason (i have an open ticket and they are perplexed as well). I'm trying to simplify things.
I have a template for building out all skus of the same solution in parallel.
This is the same concept as Stijn's approach that uses an ItemGroup as a project definition rather than a series of options for a particular property + the msbuild task will build both at the same time, saving you time and bubbling up any configuration issues when building in parallel.
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<SolutionToBuild Include="$(MSBuildThisFileDirectory)\MyProject.sln">
<Properties>DeployOnBuild=True;PublishProfile="Dev Server"</Properties>
</SolutionToBuild>
<SolutionToBuild Include="$(MSBuildThisFileDirectory)\MyProject.sln">
<Properties>DeployOnBuild=True;PublishProfile="Prod Server"</Properties>
</SolutionToBuild>
</ItemGroup>
<Target Name="Build">
<MsBuild BuildInParallel="true" ContinueOnError="true" Projects="#(SolutionToBuild)" />
</Target>
<Target Name="Clean">
<MsBuild BuildInParallel="true" ContinueOnError="true" Projects="#(SolutionToBuild)" Targets="Clean" />
</Target>
</Project>
You can only invoke multiple different targets on the commandline, but you can't supply multiple different values for properties. At least not in a way that I'm aware off. The workaround however is simple, easier to extend than a commandline, and the typical msbuild way of doing things: create a master build file like below and call it from Bamboo instead of the solution.
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
DefaultTargets=Build>
<Target Name="Build">
<ItemGroup>
<PublishProfiles Include="Dev Server"/>
<PublishProfiles Include="Prod Server"/>
</ItemGroup>
<MsBuild Projects="myproject.sln" Targets="Build"
Properties="DeployOnBuild=True;PublishProfile=%(PublishProfile.Identity)"/>
</Target>
</Project>
(this will invoke MsBuild myproject.sln once for each item in the PublishProfiles list with the properties as shown)

How to stop MSBuild _WPPCopyWebApplication target cleaning App_Data folder

I'm using the _WPPCopyWebApplication MSBuild target in a CruiseControl.net build-and-deploy script, but it appears that this target cleans files not part of the project before the deployment - specifically, App_Data files (which for this app, include uploaded images, etc).
From Microsoft.Web.Publishing.targets;
<OnBefore_WPPCopyWebApplication>
$(OnBefore_WPPCopyWebApplication);
CleanWebProjectOutputDir;
PipelineTransformPhase;
</OnBefore_WPPCopyWebApplication>
How can I stop it doing the CleanWebProjectOutputDir, given this target;
<Target Name="Deploy" DependsOnTargets="Tests">
<MSBuild Projects="$(TargetPath)Website.csproj" Properties="Configuration=Debug;WebProjectOutputDir=\\servername\share;Outdir=$(ProjectDir)bin\;" Targets="ResolveReferences;_WPPCopyWebApplication" />
</Target>
This is from a VS2010 solution, albeit built under CC.Net; I'm aware of MSDeploy, but haven't got my head around that fully yet, so would prefer to stick with MSBuild/_WPPCopyWebApplication for now.
EDIT:
I've further narrowed this to this part of the target;
<!-- In the case of the incremental Packaging/Publish, we need to find out the extra file and delee them-->
<ItemGroup>
<_AllExtraFilesUnderProjectOuputFolder Include="$(WebProjectOutputDir)\**" />
<_AllExtraFilesUnderProjectOuputFolder Remove="#(FilesForPackagingFromProject->'$(WebProjectOutputDir)\%(DestinationRelativePath)')" />
</ItemGroup>
<!--Remove all extra files in the temp folder that's not in the #(FilesForPackagingFromProject-->
<Delete Files="#(_AllExtraFilesUnderProjectOuputFolder)" />
So I guess the question becomes, how can I supress this specific Delete task, or at least add App_Data** to the _AllExtraFilesUnderProjectOuputFolder exclusions?
Add CleanWebProjectOutputDir=False to your properties:
<Target Name="Deploy" DependsOnTargets="Tests">
<MSBuild Projects="$(TargetPath)Website.csproj" Properties="Configuration=Debug;CleanWebProjectOutputDir=False;WebProjectOutputDir=\\servername\share;Outdir=$(ProjectDir)bin\;" Targets="ResolveReferences;_WPPCopyWebApplication" />
</Target>