从 Visual Studio Team Services 中的构建部署到私有 IIS 服务器

Deploying to a private IIS server from a build in Visual Studio Team Services

已完成此处建议的操作: ...如何在构建整个分支时将自动部署设置为构建的一部分 **/*.sln?

我试过的...

在VS中我可以获得最新版本的代码,打开一个解决方案然后... 右键单击 > 发布 > 选择发布配置文件 > 部署

我将我的发布配置文件命名为 "dev"、"qa"、"production",这些指的是项目将部署到的环境,配置文件包含所有VS 使用 "one click deploy" 该应用程序部署(通过 webdeploy / msdeploy)所需的配置信息。

我想让构建服务器上的 Team Services 对在构建代码后定义了发布配置文件的项目执行完全相同的操作。

我的理解是我可以像这样添加 msbuild args ...

这导致构建的部署部分在构建日志中抛出以下异常...

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.)

如果不是发布配置文件中定义的用户,这使用的是什么用户?

相关问题:

我在有问题的服务器上添加了一个帐户(因为要部署的构建和服务器是同一台服务器,这使事情变得更容易),我还向服务器添加了一个名为 "MSDepSvcUsers" 的组并添加了有问题的新帐户和盒子上的管理员组。

然后我将 Web Deployment Agent 服务和 Team Services Agent 服务都告诉了这个帐户下的 运行(并重新启动了它们)。

不幸的是,结果是一样的......我现在真的很想知道我如何确保用于 msdeploy 命令的帐户是我所期望的,而不依赖于大量的脚本......或者也许就是这样为什么 Microsoft 尚未将其设置为 Team Services 中的默认部署步骤选项!

不,您不需要大量 PS 脚本来实现此目的。 MSDeploy.exe 是一个非常有用的工具,可能可以满足您的需求。将 /t:Package 构建参数添加到 VS 构建任务以创建包。然后使用命令行任务将 MSDeploy 程序包部署到您的 IIS 站点。以下是 WebDeploy/MSDeploy 作品的更多详情:

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

好的,所以我与 Microsoft 的 VSTS 团队就此进行了长时间的对话,总而言之是...

微软:

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

...

我就是我,想出了一些 "trick to make it happen"。

出于某种奇怪的原因,我设法弄清楚了构建框也不能与您正在部署的服务器相同(不知道为什么),但是弄​​清楚了这一点后,我编写了一个简单的控制台应用程序,其中包含一些额外的内容Microsoft 的反馈非常好。

它甚至可以将进度报告回流程,并且可以将部署中的异常记录为异常,以便通过调用 "internal commands" 来使构建失败(顺便说一句,这是团队的荣誉).

这里有一些 hack,它并不完美,但希望它能帮助其他人,我这样称呼它是因为它是我的 repo 中构建的代码的一部分,所以我可以在构建过程以从传递我要部署到的环境名称的构建输出中调用它。

这个实习生抓取所有包(根据上面的设置)并使用它们的发布配置文件来确定包需要去哪里并将它们发送到正确的服务器以进行部署...

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;
        }
    }
}

我一直这样做。我所做的是在发布选项卡中设置发布并注册以启用部署组。在您的帐户上启用部署组后(需要联系 MS 才能启用)。我可以在我想要部署到的每台机器上下载 PS 我 运行 的脚本。然后在发布屏幕中,我可以在部署组中设置 运行 的步骤,然后在本地服务器上设置各种发布任务 运行 以允许它们工作。

使用部署组是一个很好的解决方案,因为如果您对其进行负载平衡,它将一次仅部署到一部分负载平衡服务器。允许应用一直保持运行状态。