好的,我无法完全确定在ServerFault或SO上是否更好地提出这个问题,但我认为这更像是一个编程问题......(你可能不同意)
我正在尝试将各种部署活动包装到一个控制台应用程序中,然后使用TeamCity构建命令行运行程序使用适当的参数执行它。但是,尝试在远程计算机上停止/启动/安装Windows服务似乎是一个棘手的业务,使用ServiceController从一个无法以提升的权限运行的进程。
此时,它实际上可能是在每个远程主机上使用Invoke-Command执行Powershell脚本的最简单方法。 (当然,允许这是一个不同的安全漏洞)而不是禁用UAC或其他选项......
有人会关注哪种方法论是最好的方法吗?
答案 0 :(得分:2)
好的,我创建了一个基于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();
}
}
}
以下是在控制台应用中使用它的方法:
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();
}
}
希望其他人觉得这很有用。