3

Having done what is suggested here: Deploy from Visual Studio Online build to private IIS server ... how do I setup automatic deploys as part of my build when I build a whole branch **/*.sln?

What I have tried ...

In VS I can get the latest version of the code, open a solution and then ... right click > publish > pick publish profile > deploy

I have named my publish profiles things like "dev", "qa", "production", these refer to the environments into which the project will be deployed and the profiles contain all of the configuration information needed for VS to deploy (via webdeploy / msdeploy) using "one click deploy" that application.

I want to have Team Services on the build server do the exact same thing for projects that have publish profiles defined after it's built the code.

My understanding was that I could just add the msbuild args like this ...

Build Step - With deploy

this results in the deployment part of the build throwing the following exception in to the build log ...

C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v14.0\Web\Microsoft.Web.Publishing.targets(4288,5): 
Error ERROR_USER_NOT_ADMIN: Web deployment task failed. 
(Connected to 'server' using the Web Deployment Agent Service, but could not authorize. Make sure you are an administrator on 'server'. 
Learn more at: http://go.microsoft.com/fwlink/?LinkId=221672#ERROR_USER_NOT_ADMIN.)

What user is this using if not the user defined in the publish profile?

Related issues:

I added an account to the server in question (since the build and server to be deployed to are the same server it made things easier), I also added a group to the server called "MSDepSvcUsers" and added the new account in question to it and the admins group on the box.

I then told both the Web Deployment Agent service and the Team Services Agent service to run under this account (and restarted them).

Unfortunately the result is the same ... I now really want to know how I go about ensuring the account that is used for the msdeploy command is something I expect without relying on loads of scripting ... or maybe that's why Microsoft haven't set this up as a default deploy step option in Team Services already!

Community
  • 1
  • 1
War
  • 8,539
  • 4
  • 46
  • 98

3 Answers3

2

Ok so I had some long conversations with the VSTS team over at Microsoft about this and the long and short of it is ...

Microsoft:

We understand your frustration with this area and a big project is about to spin up to resolve this issue

...

Me being me, came up with some "trick to make it happen".

I managed to figure out that the build box for some odd reason can't be the same server that you are deploying too (no idea why) but having figured that out I wrote a simple console app that with some additional feedback from Microsoft came out pretty good.

It even reports progress back to the process and can log exceptions in the deployment as exceptions in order to fail the build by calling up "internal commands" (neat how this works by the way kudos to the team for that).

There are some hacks in here and it's not perfect but hopefully it'll help someone else, I call this because it's part of the code that gets built in my repo so I am able to add a step in to the build process to call this from within the build output passing the environment name I want to deploy to.

This in tern grabs all the packages (as per the settings above) and uses their publish profiles to figure out where the packages need to go and sends them to the right servers to be deployed ...

using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;

namespace Deploy
{
    class Program
    {
        static string msDeployExe = @"C:\Program Files\IIS\Microsoft Web Deploy V3\msdeploy.exe";

        static void Main(string[] args)
        {
            var env = args[0];
            var buildRoot = Path.Combine(Assembly.GetExecutingAssembly().Location.Replace("Deploy.exe", ""), env);
            //var commands = GetCommands(buildRoot);
            var packages = new DirectoryInfo(buildRoot).GetFiles("*.zip", SearchOption.AllDirectories);

            bool success = true;
            for (int i = 0; i < packages.Length; i++)
            {
                if (!Deploy(packages[i], env)) success = false;
                Console.WriteLine("##vso[task.setprogress]" + (int)(((decimal)i / (decimal)packages.Length) * 100m));
            }

            Console.WriteLine("##vso[task.setprogress]100");

            if(success) Console.WriteLine("##vso[task.complete result=Succeeded]");
            else        Console.WriteLine("##vso[task.complete result=SucceededWithIssues]");
        }

        static bool Deploy(FileInfo package, string environment)
        {
            bool succeeded = true;
            Console.WriteLine("Deploying " + package.FullName);
            var procArgs = new ProcessStartInfo
            {
                FileName = msDeployExe,
                UseShellExecute = false,
                RedirectStandardOutput = true,
                RedirectStandardError = true,
                Arguments =
                    "-source:package='" + package.FullName + "' " +
                    "-dest:auto,ComputerName='" + environment + ".YourDomain.com',UserName='deployment user',Password='password',AuthType='ntlm',IncludeAcls='False' " +
                    "-verb:sync " +
                    "-disableLink:AppPoolExtension " +
                    "-disableLink:ContentExtension " +
                    "-disableLink:CertificateExtension " +
                    "-setParamFile:\"" + package.FullName.Replace("zip", "SetParameters.xml") + "\""
            };

            try
            {
                Console.WriteLine(msDeployExe + " " + procArgs.Arguments);
                using (var process = Process.Start(procArgs))
                {
                    var result = process.StandardOutput.ReadToEnd().Split('\n');
                    var error = process.StandardError.ReadToEnd();
                    process.WaitForExit();

                    if (!string.IsNullOrEmpty(error))
                    {
                        Console.WriteLine("##vso[task.logissue type=error]" + error);
                        succeeded = false;
                    }

                    foreach (var l in result)
                        if (l.ToLowerInvariant().StartsWith("error"))
                        {
                            Console.WriteLine("##vso[task.logissue type=error]" + l);
                            succeeded = false;
                        }
                        else
                            Console.WriteLine(l);
                }
            }
            catch (Exception ex) {
                succeeded = false;
                Console.WriteLine("##vso[task.logissue type=error]" + ex.Message);
                Console.WriteLine("##vso[task.logissue type=error]" + ex.StackTrace);
            }

            return succeeded;
        }
    }
}
War
  • 8,539
  • 4
  • 46
  • 98
0

No you don't need a ton of PS scripts to achieve this. MSDeploy.exe is an incredibly useful tool that can probably cover your needs. Add the /t:Package build argument to your VS build task to create a package. Then use a Commandline task to deploy the MSDeploy package to your IIS site. Here are more details about out WebDeploy/MSDeploy works:

http://www.dotnetcatch.com/2016/02/25/the-anatomy-of-a-webdeploy-package/

chief7
  • 14,263
  • 14
  • 47
  • 80
  • I've gotten this working through VS already I have 1 click deploys working but my projects just build and publish (using an MSDeploy publish profile), I have the publish profiles set to deploy directly to the MSDeploy service (instead of just creating a package) ... can I not somehow have VSO's build use these publish profiles instead of building a package which means writing scripts and having variables all over the place to handle things like auth and store the server URL, all of which is already in my existing publish profiles ... if so, how? – War Aug 01 '16 at 09:18
  • Yes, you should be able to add "/p:DeployOnBuild=true /p:PublishProfile=" to your VSO build arg list to accomplish this. – chief7 Aug 01 '16 at 09:31
  • Yeh I've done that ... doesn't seem to work, that's why i ended up asking this question :( – War Aug 01 '16 at 11:27
  • I narrowed my problem down to this question: http://stackoverflow.com/questions/38936190/deploying-web-packages-with-msdeploy – War Aug 13 '16 at 23:02
0

I do this all of the time. What I did was setup a Release in the Release tab and signed up to enable Deployment Groups. Once you have a the Deployment Group enabled on your account (needed contact MS to get this enabled). I could download PS script that I run on each of the machines that I want to deploy to. Then in the Release screen I can setup the steps to run in a Deployment Group and then the various publish tasks run on the local server allowing them to work.

Using the Deployment Groups is an excellent solution because if you have it load balanced it will deploy to only a portion of the load balanced servers at a time. Allowing the app to stay up the whole time.

Paul Cavacas
  • 4,194
  • 5
  • 31
  • 60