我正在尝试在所有SQL Server实例上远程重启SQL代理服务。我在登录到CSV文件中列出的每个服务器上具有本地管理员权限的帐户时,从我的某个开发服务器运行以下脚本。该文件包含我的所有SQL实例的HostName和InstanceName,在此开发服务器和远程服务器上。所有这些都是Windows Server 2008R2或2012,运行SQL Server 2008R2或2012. PowerShell正在以管理员身份运行。
$Servers = Import-CSV C:\Temp\InstanceList.csv
ForEach ($Line In $Servers)
{
$sqlHostName = $Line.hostName
$sqlInstanceName = if ($Line.instanceName -eq "") {$Line.instanceName} else {("$" + $Line.instanceName)}
$serviceName = if ($sqlInstanceName -eq "") {"SQLSERVERAGENT"} else {"SQLAgent" + $sqlInstanceName}
Get-Service -computer $sqlHostName ($serviceName)
Get-Service -computer $sqlHostName ($serviceName) | Restart-Service
}
它在本地计算机的实例上成功但在远程实例上失败。这是我在远程默认实例中看到的错误。第一行显示我有正确的服务名称,下一行声称它不存在:
Running SQLSERVERAGENT SQL Server Agent (MSSQLSERVER)
Restart-Service : Cannot find any service with service name 'SQLSERVERAGENT'.
At line:11 char:70
+ Get-Service -computer $sqlHostName ($serviceName) | Restart-Service <<<<
+ CategoryInfo : ObjectNotFound: (SQLSERVERAGENT:String) [Restart-Service], ServiceCommandException
+ FullyQualifiedErrorId : NoServiceFoundForGivenName,Microsoft.PowerShell.Commands.RestartServiceCommand
先前已在每台服务器上本地运行启用 - 远程处理。
为什么Get-Service能够显示服务名称,但随后Restart-Service声称该服务不存在。感觉它必须是权限问题,但我不知道如何解决它。有什么想法吗?
答案 0 :(得分:2)
那是因为Get-Service仍在本地运行;它不知道管道中前一个命令的-Computer参数,它不支持像Get-Service那样的计算机。要将Restart-Service用于远程服务,您有几个选择:
选项1:使用Enter-PSSession
您可以使用Enter-PSSession在相关计算机上输入远程PowerShell会话。当你这样做时,它基本上在远程计算机上打开一个shell,你运行的任何命令都会运行。 Enter-PSSession还支持-Credential参数,因此,如果您在本地运行的帐户不是具有远程计算机权限的管理帐户,则可以先创建凭据对象,然后再使用该帐户。代码可能如下所示:
$Credential = Get-Credential
Enter-PSSession -ComputerName $ComputerName -Credential $Credential
当你运行它时,你的提示会改变:它会在方括号中标注远程计算机的名称(让你知道你正在针对不同的主机执行命令)。它可能也会默认为远程主机上的users \ documents文件夹,如下所示:
[ComputerName]: PS C:\Users\Username\Documents>
现在,您运行的任何命令都将在目标计算机上运行。你不再需要-Computer on Get-Service了。所以你可以运行:
Get-Service ($serviceName) | Restart-Service
只需确保完成后,使用Exit-PSSession返回本地提示符。
选项2:使用Invoke-Command
另一个选项(以及可能更适合您正在进行的自动化的选项)是使用Invoke-Command。 Invoke-Command支持-Credential和-ComputerName参数,以及-ScriptBlock参数。 ScriptBlock很简洁,因为它允许您轻松传递和修改具有自己参数的代码。因此,在上面的示例中,您可以执行以下操作:
$ScriptBlock = {Get-Service | Where-Object {$_.Name -like "SQL*"} | Restart-Service}
Invoke-Command -ComputerName $ComputerName -ScriptBlock $ScriptBlock
根据您尝试做的事情,我会像这样重写您的脚本:
$Servers = Import-CSV C:\Temp\InstanceList.csv
$ScriptBlock = {param([string] $RemoteServiceName) Get-Service | Where-Object {$_.Name -eq $RemoteServiceName} | Restart-Service}
ForEach ($Line In $Servers)
{
$sqlHostName = $Line.hostName
$sqlInstanceName = if ($Line.instanceName -eq "") {$Line.instanceName} else {("$" + $Line.instanceName)}
$serviceName = if ($sqlInstanceName -eq "") {"SQLSERVERAGENT"} else {"SQLAgent" + $sqlInstanceName}
Invoke-Command -ComputerName $sqlHostName -ScriptBlock $ScriptBlock -ArgumentList $serviceName
}
我正在使用你的循环逻辑,但是我使用你从.CSV文件中获得的变量来指定我希望运行Invoke-Command的-ComputerName,并且我正在使用带有ArgumentList的ScriptBlock和用于提供最终运行的值的参数。
如果您想了解更多关于几个概念的内容,请点击以下链接:
选项3:在对象上使用方法
当然,它并不需要那么难。您可以使用Get-Service获取变量的服务,然后对该对象使用.Restart()方法。虽然您无法将对象传递给Restart-Service,但可以只是调用该方法,它甚至可以远程重启该服务:
$Servers = Import-CSV C:\Temp\InstanceList.csv
ForEach ($Line In $Servers)
{
$sqlHostName = $Line.hostName
$sqlInstanceName = if ($Line.instanceName -eq "") {$Line.instanceName} else {("$" + $Line.instanceName)}
$serviceName = if ($sqlInstanceName -eq "") {"SQLSERVERAGENT"} else {"SQLAgent" + $sqlInstanceName}
$Service = Get-Service -computer $sqlHostName ($serviceName)
$Service.Restart()
}
对于PowerShell而言,这是一件非常棒的事情。这些答案都没有&#34;错误&#34;因为你可以使用其中任何一个;他们是所有潜在的解决方案,我相信还有更多的解决方案。