Windows services 使用TeamCity停止和重新启动远程主机上的服务的最佳方法?

Windows services 使用TeamCity停止和重新启动远程主机上的服务的最佳方法?,windows-services,teamcity,Windows Services,Teamcity,好的,我不能完全决定这是不是最好在ServerFault上问,但我认为这更多的是一个编程问题的核心…(你可能不同意) 我试图将各种各样的部署活动打包到一个控制台应用程序中,然后使用TeamCity build命令行运行程序使用适当的参数执行它。然而,试图在远程计算机上停止/启动/安装Windows服务似乎是一项棘手的任务,从无法使用提升权限运行的进程中使用ServiceController 此时,在每个远程主机上使用Invoke命令执行Powershell脚本实际上可能是最简单的方法。(当然,允

好的,我不能完全决定这是不是最好在ServerFault上问,但我认为这更多的是一个编程问题的核心…(你可能不同意)

我试图将各种各样的部署活动打包到一个控制台应用程序中,然后使用TeamCity build命令行运行程序使用适当的参数执行它。然而,试图在远程计算机上停止/启动/安装Windows服务似乎是一项棘手的任务,从无法使用提升权限运行的进程中使用ServiceController

此时,在每个远程主机上使用Invoke命令执行Powershell脚本实际上可能是最简单的方法。(当然,允许这是一个不同的安全漏洞)比禁用UAC或其他选项


是否有人愿意大胆地提出一个意见,认为什么方法是最好的继续进行的方法?

好的,我创建了一个基于Powershell的服务控制器类,允许应用程序在没有提升权限的情况下运行,以控制远程服务

这是:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
//
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Management.Automation.Runspaces;

namespace PowershellBasedServiceControl
{
    // All the relevant bits came from this article: http://www.codeproject.com/KB/cs/HowToRunPowerShell.aspx
    // if script execution fails, try running 'Enable-PSRemoting' using Powershell as an admin on the target host

public class PowershellServiceController
{
    public bool StartService(string serviceName, string remoteHost)
    {
        return StartService(serviceName, remoteHost, String.Empty, String.Empty);
    }

    public bool StartService(string serviceName, string remoteHost, string remoteUserName, string remotePassword)
    {
        bool result = false;
        string execOutput = ExecuteScript(BuildStartServiceScript(serviceName, remoteHost, remoteUserName, remotePassword, false));
        result = execOutput.StartsWith("0");
        if (!result)
            Console.WriteLine(execOutput);
        return result;
    }

    public bool StopService(string serviceName, string remoteHost)
    {
        return StopService(serviceName, remoteHost, String.Empty, String.Empty);
    }

    public bool StopService(string serviceName, string remoteHost, string remoteUserName, string remotePassword)
    {
        bool result = false;
        string execOutput = ExecuteScript(BuildStopServiceScript(serviceName, remoteHost, remoteUserName, remotePassword, false));
        result = execOutput.StartsWith("0");
        if (!result)
            Console.WriteLine(execOutput);
        return result;
    }

    string ExecuteScript(string scriptText)
    {
        Runspace runspace = RunspaceFactory.CreateRunspace();
        runspace.Open();
        // create a pipeline and feed it the script text
        Pipeline pipeline = runspace.CreatePipeline();
        pipeline.Commands.AddScript(scriptText);
        // add an extra command to transform the script
        // output objects into nicely formatted strings
        // remove this line to get the actual objects
        // that the script returns. For example, the script
        // "Get-Process" returns a collection
        // of System.Diagnostics.Process instances.
        pipeline.Commands.Add("Out-String");
        // execute the script
        Collection<PSObject> results = pipeline.Invoke();
        // close the runspace
        runspace.Close();
        // convert the script result into a single string
        StringBuilder stringBuilder = new StringBuilder();
        foreach (PSObject obj in results)
        {
            stringBuilder.AppendLine(obj.ToString());
        }
        return stringBuilder.ToString();
    }

    string BuildStartServiceScript(string serviceName, string remoteHost, string remoteUserName, string remotePassword, bool echoScript)
    {
        StringBuilder script = new StringBuilder();
        if (!String.IsNullOrEmpty(remoteUserName))
        {
            script.AppendLine("$block = {");
            script.AppendLine("$returnCode = 1");
            script.AppendLine("try {");
            script.AppendLine(String.Format("$service = \"{0}\"", serviceName));
            script.AppendLine("$serviceController = (new-Object System.ServiceProcess.ServiceController($service,\"localhost\"))");
            script.AppendLine("if($serviceController.Status -notlike 'Running') {");
            script.AppendLine("$serviceController.Start()");
            script.AppendLine("$serviceController.WaitForStatus('Running',(new-timespan -seconds 120))");
            script.AppendLine("if ($serviceController.Status -eq 'Running') { ");
            script.AppendLine("$returnCode = 0 }");
            script.AppendLine("} else { ");
            script.AppendLine("$returnCode = 0 } }");
            script.AppendLine("catch {");
            script.AppendLine("return 1");
            script.AppendLine("exit }");
            script.AppendLine("return $returnCode}");
            script.AppendLine("");

            script.AppendLine(String.Format("$pass = convertto-securestring \"{0}\" -asplaintext -force", remotePassword));
            script.AppendLine(String.Format("$mycred = new-object -typename System.Management.Automation.PSCredential -argumentlist \"{0}\",$pass", remoteUserName));

            script.AppendLine(String.Format("$res = Invoke-Command -computername \"{0}\" -Credential $mycred -scriptblock $block", remoteHost));
            script.AppendLine("return $res");
        }
        if (echoScript)
            Console.WriteLine(script.ToString());
        return script.ToString();
    }

    string BuildStopServiceScript(string serviceName, string remoteHost, string remoteUserName, string remotePassword, bool echoScript)
    {
        StringBuilder script = new StringBuilder();
        if (!String.IsNullOrEmpty(remoteUserName))
        {
            script.AppendLine("$block = {");
            script.AppendLine("$returnCode = 1");
            script.AppendLine("try {");
            script.AppendLine(String.Format("$service = \"{0}\"", serviceName));
            script.AppendLine("$serviceController = (new-Object System.ServiceProcess.ServiceController($service,\"localhost\"))");
            script.AppendLine("if($serviceController.Status -notlike 'Stopped') {");
            script.AppendLine("$serviceController.Stop()");
            script.AppendLine("$serviceController.WaitForStatus('Stopped',(new-timespan -seconds 120))");
            script.AppendLine("if ($serviceController.Status -eq 'Stopped') { ");
            script.AppendLine("$returnCode = 0 }");
            script.AppendLine("} else { ");
            script.AppendLine("$returnCode = 0 } }");
            script.AppendLine("catch {");
            script.AppendLine("return 1");
            script.AppendLine("exit }");
            script.AppendLine("return $returnCode}");
            script.AppendLine("");

            script.AppendLine(String.Format("$pass = convertto-securestring \"{0}\" -asplaintext -force", remotePassword));
            script.AppendLine(String.Format("$mycred = new-object -typename System.Management.Automation.PSCredential -argumentlist \"{0}\",$pass", remoteUserName));

            script.AppendLine(String.Format("$res = Invoke-Command -computername \"{0}\" -Credential $mycred -scriptblock $block", remoteHost));
            script.AppendLine("return $res");
        }
        if (echoScript)
            Console.WriteLine(script.ToString());
        return script.ToString();
    }
}

希望其他人会觉得这很有用。

似乎powershell脚本应该是解决这一问题的好方法。也在寻找示例。@Andrew Orsich:请参阅我在下面发布的示例,谢谢。安德鲁·奥里希:我修复了生成停止脚本时的一个错误。另外,最初的“WaitFor…”调用似乎在生产中造成了问题,所以我删除了它们。
class Program
{
    static void Main(string[] args)
    {
        PowershellServiceController runner = new PowershellServiceController();
        if (runner.StartService("w3svc", "myremotehost.com", "myusername", "mypassword"))
            Console.WriteLine("Service was started successfully");
        else
            Console.WriteLine("Failed to start remote service");
        Console.WriteLine("Press ENTER to quit");
        Console.ReadLine();
    }
}