TFS2010 Build Definition to Deploy to multiple servers?
Asked Answered
A

5

9

I've been looking into TFS2010 new build and deployment features with MSDeploy. So far everything is going well (although its been hard to find information about specific scenarios).

Can I modify my Build Definition to specify 2 or more servers to deploy to? What I need to do is deploy to multiple servers (as I have two in my testing environment which uses a NLB).

What I have now is a Build definition which Builds, runs my tests, and then Deploys to ONE of my testing servers (which has the MsDeployAgentService running on it). It works fine, and each web project is deployed as configured in its project file. The MSBuild Arguments I use are:

* /p:DeployOnBuild=True
* /p:DeployTarget=MsDeployPublish
* /p:MSDeployServiceURL=http://oawww.testserver1.com.au/MsDeployAgentService
* /p:CreatePackageOnPublish=True
* /p:MsDeployPublishMethod=RemoteAgent
* /p:AllowUntrustedCertificated=True
* /p:UserName=myusername
* /p:Password=mypassword 

NB: I dont use /p:DeployIISAppPath="xyz" as it doesnt deploy all my projects and overrides my project config.

Can I add another build argument to get it to call more than one MSDeployServiceURL? Like something like a second /p:MSDeployServiceURL argument that specifies another server?

Or do I have to look for another solution, such as editing the WF?

I saw an almost exact same question here posted 2 months ago: TFS 2010 - Deploy to Multiple Servers After Build , so it doesn't look like I'm the only one trying to solve this.

I also posted on the IIS.NET forums where MSDeploy is discussed: http://forums.iis.net/t/1170741.aspx . It's had quite a lot of views, but again, no answers.

Ambidextrous answered 19/8, 2010 at 7:4 Comment(0)
S
7

You don't have to build the project twice to deploy to two servers. The build process will build a set of deployment files. You can then use the InvokeProcess to deploy to multiple servers.

First create a variable named ProjectName. Then add an Assign activity to the "Compile the Project" sequence. This is located in the "Try to Compile the Project" sequence. Here are the properties of the Assign:

To: ProjectName
Value: System.IO.Path.GetFileNameWithoutExtension(localProject)

Here are the properties of our InvokeProcess activity that deploys to the test server:

Arguments: "/y /M:<server> /u:<domain>\<user> /p:<password>"
FileName: String.Format("{0}\{1}.deploy.cmd", BuildDetail.DropLocation, ProjectName)

You will need to change <server>, <domain>, <user>, and <password> to the values that reflect your environment.

If you need to manually deploy to a server you can run the command below from your build folder:

deploy.cmd /y /M:<server> /u:<domain>\<user> /p:<password>
Seema answered 30/11, 2010 at 15:43 Comment(2)
This looks like the right thing to do if I had a chance to redo my implementation. It's just a pity that it doesn't come out of the box with something like this. Thanks!Ambidextrous
I have updated our process so these are now parameterized. For the MSBuild Arguments you can also specify the IIS App Path. This too can be parameterized if you need to deploy multiple instances to the same server.Seema
A
6

I couldn't find the solution I was looking for, but here's what I came up with in the end.

I wanted to keep the solution simple and configurable within the TFS arguments while at the same time staying in line with the already provided MSBuildArguments method which has been promoted a lot. So I created a new Build Template, and added a new TFS WorkFlow Argument called MSBuildArguments2 in the Arguments tab of the WorkFlow.

alt text

I searched through the BuildTemplate WorkFlow for all occurances of the MSBuildArguments (there were two occurances).

The two tasks that use MSBuildArguments are called Run MSBuild for Project. Directly below this task, I added a new "If" block with the condition:

Not String.IsNullOrEmpty(MSBuildArguments2)

I then copied the "Run MSBuild for Project" task and pasted it into the new If's "Then" block, updating its title accordingly. You'll also need to update the new Task's ConmmandLineArguments property to use your new Argument.

CommandLineArguments = String.Format("/p:SkipInvalidConfigurations=true {0}", MSBuildArguments2)

After these modifications, the WorkFlow looks like this:

alt text

Save and Check In the new WorkFlow. Update your Build Definition to use this new WorkFlow, then in the build definition's Process tab you will find a new section called Misc with the new argument ready to be used. Because I'm simply using this new argument for deployment, I copied the exact same arguments I used for MSBuild Arguments and updated the MSDeployServiceURL to my second deployment server.

alt text

And that's that. I suppose a more elegant method would be to convert MSBuildArguments into an array of strings and then loop through them during the WorkFlow process. But this suits our 2 server requirements.

Hope this helps!

Ambidextrous answered 23/8, 2010 at 2:34 Comment(0)
F
2

My solution to this is a new Target that runs after Package. Each project that needs to produce a package includes this targets file, and I chose to make the Include conditional on an externally-set "DoDeployment" property. Additionally each project defines the DeploymentServerGroup property so that the destination server(s) are properly filtered depending on what kind of project it is.

As you can see towards the bottom I'm simply executing the command file with the server list, pretty simple.

<!-- 
This targets file allows a project to deploy its package  

As it is used by all project typesconditionally included from the project file 

-->

<UsingTask TaskName="Microsoft.TeamFoundation.Build.Tasks.BuildStep" AssemblyFile="$(TeamBuildRefPath)\Microsoft.TeamFoundation.Build.ProcessComponents.dll" />

<!-- Each Server needs the Group metadatum, either Webservers, Appservers, or Batch. -->
<Choose>
    <When Condition="'$(Configuration)' == 'DEV'">
        <ItemGroup>
            <Servers Include="DevWebServer">
                <Group>Webservers</Group>
            </Servers>
            <Servers Include="DevAppServer">
                <Group>Appservers</Group>
            </Servers>
        </ItemGroup>
    </When>
    <When Condition="'$(Configuration)' == 'QA'">
        <ItemGroup>
            <Servers Include="QAWebServer1">
                <Group>Webservers</Group>
            </Servers>
            <Servers Include="QAWebServer2">
                <Group>Webservers</Group>
            </Servers>
            <Servers Include="QAAppServer1">
                <Group>Appservers</Group>
            </Servers>
            <Servers Include="QAAppServer2">
                <Group>Appservers</Group>
            </Servers>
        </ItemGroup>
    </When>
</Choose>

<!-- DoDeploy can be set in the build defintion -->
<Target Name="StartDeployment" AfterTargets="Package">

    <PropertyGroup>
        <!-- The _PublishedWebsites area -->
        <PackageLocation>$(WebProjectOutputDir)_Package</PackageLocation>

        <!-- Override for local testing -->
        <PackageLocation Condition="$(WebProjectOutputDirInsideProject)">$(IntermediateOutputPath)Package\</PackageLocation>

    </PropertyGroup>

    <Message Text="Tier servers are @(Servers)" />

    <!-- A filtered list of the servers.  DeploymentServerGroup is defined in each project that does deployment -->
    <ItemGroup>
        <DestinationServers Include="@(Servers)" Condition="'%(Servers.Group)' == '$(DeploymentServerGroup)'" />
    </ItemGroup>

    <Message Text="Dest servers are @(DestinationServers)" />

</Target>

<!-- Only perform the deployment if any servers fit the filters -->
<Target Name="PerformDeployment" AfterTargets="StartDeployment" Condition="'@(DestinationServers)' != ''">

    <Message Text="Deploying $(AssemblyName) to @(DestinationServers)" />

    <!-- Fancy build steps so that they better appear in the build explorer -->
    <BuildStep
                    TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
                    BuildUri="$(BuildUri)"
                    Message="Deploying $(AssemblyName) to @(DestinationServers)...">
        <Output TaskParameter="Id" PropertyName="StepId" />
    </BuildStep>

    <!-- The deployment command will be run for each item in the DestinationServers collection.  -->
    <Exec Command="$(AssemblyName).deploy.cmd /Y /M:%(DestinationServers.Identity)" WorkingDirectory="$(PackageLocation)" />

    <BuildStep
                    TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
                    BuildUri="$(BuildUri)"
                    Id="$(StepId)"
                    Status="Succeeded"
                    Message="Deployed $(AssemblyName) to @(DestinationServers)"/>
    <OnError ExecuteTargets="MarkDeployStepAsFailed" />
</Target>

<Target Name="MarkDeployStepAsFailed">
    <BuildStep
            TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
            BuildUri="$(BuildUri)"
            Id="$(StepId)"
            Status="Failed" />
</Target>

Fugue answered 1/12, 2011 at 20:45 Comment(2)
Interesting... so you are taking the work out of the TFS Workflow and putting it back into an MSBUILD Script. Is every project in your solution calling this target after build? And here I thought MSBUILD was dead and buried :)Ambidextrous
Once I got better at writing MSBuild files I started to prefer them to using the clunky and crash-prone WorkFlow designer. Each project that needs to deploy its package includes this targets file from its csproj, and the target invocation is automatic as it's done after the Package target.Fugue
M
0

I am the author of the other similar post. I have yet to find a solution. I believe it is going to be modifying the workflow to add a postprocessing MSBUILD -sync task. That seems to be the most elegant, but was still hoping to find something a bit less intrusive.

Mozellamozelle answered 20/8, 2010 at 12:7 Comment(1)
I was going to go down that path. There's a blog entry here: blogs.blackmarble.co.uk/blogs/rfennell/archive/2010/08/13/… which covers how to do that. But in the end I added a second call to MSBUILD and reused the Arguments idea they provide for us (as detailed in my answer).Ambidextrous
J
0

I'm not sure if that could help you with TFS 2010, but I have a blog post for TFS 2012: Multiple web projects deployment from TFS 2012 to NLB enabled environment.

Johnsson answered 25/1, 2013 at 8:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.