Powershell Invoke-Command 导致不同的结果
Powershell Invoke-Command causes different result
我有一个用于清除机器上的消息队列的函数,但我希望对其进行调整并使其更加健壮。我希望能够将命令发送到一台机器,即使当前机器没有安装 MSMQ。
本地命令可以正常工作,但是当调用 invoke-command 时,检查队列是否存在 returns false(即使队列确实存在)。以前有人 运行 做过类似的事情吗?有什么建议吗?
这是我的功能:
Function Purge-MessageQueue
{
<#
.Synopsis
Use hostname to purge message queue
.DESCRIPTION
Checks if MSMQ is locally installed otherwise fire the purge
command to the machine you are purging
.EXAMPLE
Purge-MessageQueue -targetMachine $env:computername -queueName 'test'
#>
Param
(
[Parameter(Mandatory=$true)]
[String]$targetMachine,
[Parameter(Mandatory=$true)]
[string]$queueName
)
Write-Verbose "Purging $queueName queue on: $targetMachine"
$queueName = "$targetMachine$queueName"
$error.clear()
[void] [Reflection.Assembly]::LoadWithPartialName("System.Messaging")
try {[void][System.Messaging.MessageQueue]::Exists($queueName)}
catch
{
if ($_.exception.ToString() -like '*Message Queuing has not been installed on this computer.*')
{
#push command to machine
$RemoteSuccess = Invoke-Command -ComputerName $targetMachine -ScriptBlock { Param($queueName)
[void] [Reflection.Assembly]::LoadWithPartialName("System.Messaging")
If([System.Messaging.MessageQueue]::Exists($queueName))
{
$queue = new-object -TypeName System.Messaging.MessageQueue -ArgumentList $queueName
Try{$queue.Purge()}
Catch{$error}
}
} -ArgumentList $queueName
}
}
If(!$error)
{
If([System.Messaging.MessageQueue]::Exists($queueName))
{
$queue = new-object -TypeName System.Messaging.MessageQueue -ArgumentList $queueName
$queue.Purge()
}
}
If(!$Error -and !$RemoteSuccess)
{
Write-Host "$queueName queue on $targetMachine cleared"
}
Else
{
Write-Warning "Failed locating queue $queueName on $targetMachine"
}
}
注意事项:
为了确定到底发生了什么,我在 exists 语句上使用了 write-host 并且它 returns false。当我传递脚本块时未找到队列。它正在另一台机器上执行(测试写入成功的文件)。当我 运行:
Write-Host "$([System.Messaging.MessageQueue]::Exists($queueName))`n$queueName"
$objqueue = new-object -TypeName System.Messaging.MessageQueue -ArgumentList $queueName
我得到错误的、正确的队列名称和以下错误:
Exception calling "Purge" with "0" argument(s): "The queue does not
exist or you do not have sufficient permissions to perform the
operation."
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : MessageQueueException
+ PSComputerName : XXXXXX
运行 直接在机器上执行相同的命令没有问题。
我还发现其他人试图在 serverfault 上做类似的事情:
https://serverfault.com/questions/399178/how-to-retrieve-names-of-all-private-msmq-queues-efficiently
当我尝试这个时:
Invoke-Command -ComputerName $targetMachine -ScriptBlock { Get-MsmqQueue }
我得到以下结果:
Cannot find specified machine.
+ CategoryInfo : ObjectNotFound: (:) [Get-MsmqQueue], MessageQueueException
+ FullyQualifiedErrorId : MachineNotFound,Microsoft.Msmq.PowerShell.Commands.GetMSMQQueueCommand
以下命令执行 return 数据,但它不允许我发送清除命令:
Invoke-Command -ComputerName $targetMachine -ScriptBlock {Get-WmiObject -class Win32_PerfRawData_MSMQ_MSMQQueue}
我还尝试将内容写入脚本文件,然后调用该文件,当 运行 在机器上时,它可以正常工作,但通过 invoke-command:
调用时却没有问题
$filewriter = @"
[Reflection.Assembly]::LoadWithPartialName("System.Messaging")
If([System.Messaging.MessageQueue]::Exists('$queueName'))
{
`$objqueue = new-object -TypeName System.Messaging.MessageQueue -ArgumentList $queueName
Try{`$objqueue.Purge()}
Catch{`$error}
}
"@
$session = New-PSSession -ComputerName $targetMachine
Invoke-Command -Session $session -ScriptBlock {Param($FileWriter)
$FileWriter | Out-File "C:\Temp\PurgeQueue.ps1"
} -ArgumentList $filewriter
$test = Invoke-Command -Session $session -scriptblock {Pushd "C:\Temp\"
.\PurgeQueue.ps1}
从你的分析我感觉是权利问题
您检查过您用户的权限了吗?
如果您是普通用户,您必须在
目的地 computer/server/VM:
1) 首先创建一个组并添加用户
net localgroup "Remote PowerShell Session Users" /add
net localgroup "Remote PowerShell Session Users" the-user /add
2) 调用 GUI
Set-PSSessionConfiguration microsoft.powershell -ShowSecurityDescriptorUI
3) 添加 Remote PowerShell Session Users
组并授予其 execute (invoke)
权限
4) 重启服务:
Set-PSSessionConfiguration microsoft.powershell -ShowSecurityDescriptorUI
5) 用户现在应该能够 运行 远程会话
原出处为here。
我还没有找到造成这个问题的原因,但我会总结一下我发现的内容和我的解决方法
总结:
当通过 invoke-command 调用 msmq 命令时,只有私有队列出现并且可以被操作。
解决方法:
我已经构建了一个函数来通过在远程计算机上创建计划任务来调用命令创建的脚本来处理消息的清除和添加到队列。
Function Push-MSMQRemoteCommand
{
Param(
[Parameter(Mandatory=$true)]
$targetMachine,
[Parameter(Mandatory=$true)]
$password,
[Parameter(Mandatory=$true)]
$queueName,
[Switch]$purge,
$user,
$message)
Begin
{
If(!$user){$user = "$env:USERDOMAIN$env:USERNAME"}
If($purge -and $message.Length -ne 0){Write-Error "Choose to purge or add... not both" -ErrorAction Stop}
$queuepath = "$targetMachine$queueName"
#build commands to push
If($purge)
{
$scriptblock = @"
[void] [Reflection.Assembly]::LoadWithPartialName("System.Messaging")
If ([System.Messaging.MessageQueue]::Exists('$queuePath')) {
`$queue = new-object -TypeName System.Messaging.MessageQueue -ArgumentList $queuePath
`$queue.Purge()
}
"@
}
ElseIf($message)
{
If($message.Length -eq 0){Write-Error "No message provided to add message" -ErrorAction Stop}
$scriptblock = @"
[void] [Reflection.Assembly]::LoadWithPartialName("System.Messaging")
`$queue = new-object System.Messaging.MessageQueue "$queuepath"
`$utf8 = new-object System.Text.UTF8Encoding
`$msgBytes = `$utf8.GetBytes('$message')
`$msgStream = new-object System.IO.MemoryStream
`$msgStream.Write(`$msgBytes, 0, `$msgBytes.Length)
`$msg = new-object System.Messaging.Message
`$msg.BodyStream = `$msgStream
`$msg.Label = "RemoteQueueManagerPowershell"
`$queue.Send(`$msg)
"@
}
#Push Commands
Invoke-Command -ComputerName $targetMachine -ScriptBlock {
Param($user,$password,$scriptblock)
$scriptblock | Out-file -FilePath "C:\temp\ManageQueue.ps1" -Force
$action = New-ScheduledTaskAction -execute 'powershell.exe' -Argument '-File "C:\temp\ManageQueue.ps1"'
#scheudling action to start 2 seconds from now
$trigger = New-ScheduledTaskTrigger -Once -At ((Get-Date)+(New-TimeSpan -Seconds 2))
Register-ScheduledTask -TaskName RemoteQueueManager `
-Action $action `
-Trigger $trigger `
-User "$user"`
-Password $password
#Start-Sleep -Seconds 10
Unregister-ScheduledTask -TaskName RemoteQueueManager -Confirm:$false
Remove-Item -Path "C:\temp\ManageQueue.ps1" -Force
} -ArgumentList $user,$password,$scriptblock
}
}
我有一个用于清除机器上的消息队列的函数,但我希望对其进行调整并使其更加健壮。我希望能够将命令发送到一台机器,即使当前机器没有安装 MSMQ。
本地命令可以正常工作,但是当调用 invoke-command 时,检查队列是否存在 returns false(即使队列确实存在)。以前有人 运行 做过类似的事情吗?有什么建议吗?
这是我的功能:
Function Purge-MessageQueue
{
<#
.Synopsis
Use hostname to purge message queue
.DESCRIPTION
Checks if MSMQ is locally installed otherwise fire the purge
command to the machine you are purging
.EXAMPLE
Purge-MessageQueue -targetMachine $env:computername -queueName 'test'
#>
Param
(
[Parameter(Mandatory=$true)]
[String]$targetMachine,
[Parameter(Mandatory=$true)]
[string]$queueName
)
Write-Verbose "Purging $queueName queue on: $targetMachine"
$queueName = "$targetMachine$queueName"
$error.clear()
[void] [Reflection.Assembly]::LoadWithPartialName("System.Messaging")
try {[void][System.Messaging.MessageQueue]::Exists($queueName)}
catch
{
if ($_.exception.ToString() -like '*Message Queuing has not been installed on this computer.*')
{
#push command to machine
$RemoteSuccess = Invoke-Command -ComputerName $targetMachine -ScriptBlock { Param($queueName)
[void] [Reflection.Assembly]::LoadWithPartialName("System.Messaging")
If([System.Messaging.MessageQueue]::Exists($queueName))
{
$queue = new-object -TypeName System.Messaging.MessageQueue -ArgumentList $queueName
Try{$queue.Purge()}
Catch{$error}
}
} -ArgumentList $queueName
}
}
If(!$error)
{
If([System.Messaging.MessageQueue]::Exists($queueName))
{
$queue = new-object -TypeName System.Messaging.MessageQueue -ArgumentList $queueName
$queue.Purge()
}
}
If(!$Error -and !$RemoteSuccess)
{
Write-Host "$queueName queue on $targetMachine cleared"
}
Else
{
Write-Warning "Failed locating queue $queueName on $targetMachine"
}
}
注意事项: 为了确定到底发生了什么,我在 exists 语句上使用了 write-host 并且它 returns false。当我传递脚本块时未找到队列。它正在另一台机器上执行(测试写入成功的文件)。当我 运行:
Write-Host "$([System.Messaging.MessageQueue]::Exists($queueName))`n$queueName"
$objqueue = new-object -TypeName System.Messaging.MessageQueue -ArgumentList $queueName
我得到错误的、正确的队列名称和以下错误:
Exception calling "Purge" with "0" argument(s): "The queue does not exist or you do not have sufficient permissions to perform the operation." + CategoryInfo : NotSpecified: (:) [], MethodInvocationException + FullyQualifiedErrorId : MessageQueueException + PSComputerName : XXXXXX
运行 直接在机器上执行相同的命令没有问题。
我还发现其他人试图在 serverfault 上做类似的事情: https://serverfault.com/questions/399178/how-to-retrieve-names-of-all-private-msmq-queues-efficiently
当我尝试这个时:
Invoke-Command -ComputerName $targetMachine -ScriptBlock { Get-MsmqQueue }
我得到以下结果:
Cannot find specified machine. + CategoryInfo : ObjectNotFound: (:) [Get-MsmqQueue], MessageQueueException + FullyQualifiedErrorId : MachineNotFound,Microsoft.Msmq.PowerShell.Commands.GetMSMQQueueCommand
以下命令执行 return 数据,但它不允许我发送清除命令:
Invoke-Command -ComputerName $targetMachine -ScriptBlock {Get-WmiObject -class Win32_PerfRawData_MSMQ_MSMQQueue}
我还尝试将内容写入脚本文件,然后调用该文件,当 运行 在机器上时,它可以正常工作,但通过 invoke-command:
调用时却没有问题 $filewriter = @"
[Reflection.Assembly]::LoadWithPartialName("System.Messaging")
If([System.Messaging.MessageQueue]::Exists('$queueName'))
{
`$objqueue = new-object -TypeName System.Messaging.MessageQueue -ArgumentList $queueName
Try{`$objqueue.Purge()}
Catch{`$error}
}
"@
$session = New-PSSession -ComputerName $targetMachine
Invoke-Command -Session $session -ScriptBlock {Param($FileWriter)
$FileWriter | Out-File "C:\Temp\PurgeQueue.ps1"
} -ArgumentList $filewriter
$test = Invoke-Command -Session $session -scriptblock {Pushd "C:\Temp\"
.\PurgeQueue.ps1}
从你的分析我感觉是权利问题
您检查过您用户的权限了吗?
如果您是普通用户,您必须在 目的地 computer/server/VM:
1) 首先创建一个组并添加用户
net localgroup "Remote PowerShell Session Users" /add
net localgroup "Remote PowerShell Session Users" the-user /add
2) 调用 GUI
Set-PSSessionConfiguration microsoft.powershell -ShowSecurityDescriptorUI
3) 添加 Remote PowerShell Session Users
组并授予其 execute (invoke)
权限
4) 重启服务:
Set-PSSessionConfiguration microsoft.powershell -ShowSecurityDescriptorUI
5) 用户现在应该能够 运行 远程会话
原出处为here。
我还没有找到造成这个问题的原因,但我会总结一下我发现的内容和我的解决方法
总结: 当通过 invoke-command 调用 msmq 命令时,只有私有队列出现并且可以被操作。
解决方法: 我已经构建了一个函数来通过在远程计算机上创建计划任务来调用命令创建的脚本来处理消息的清除和添加到队列。
Function Push-MSMQRemoteCommand
{
Param(
[Parameter(Mandatory=$true)]
$targetMachine,
[Parameter(Mandatory=$true)]
$password,
[Parameter(Mandatory=$true)]
$queueName,
[Switch]$purge,
$user,
$message)
Begin
{
If(!$user){$user = "$env:USERDOMAIN$env:USERNAME"}
If($purge -and $message.Length -ne 0){Write-Error "Choose to purge or add... not both" -ErrorAction Stop}
$queuepath = "$targetMachine$queueName"
#build commands to push
If($purge)
{
$scriptblock = @"
[void] [Reflection.Assembly]::LoadWithPartialName("System.Messaging")
If ([System.Messaging.MessageQueue]::Exists('$queuePath')) {
`$queue = new-object -TypeName System.Messaging.MessageQueue -ArgumentList $queuePath
`$queue.Purge()
}
"@
}
ElseIf($message)
{
If($message.Length -eq 0){Write-Error "No message provided to add message" -ErrorAction Stop}
$scriptblock = @"
[void] [Reflection.Assembly]::LoadWithPartialName("System.Messaging")
`$queue = new-object System.Messaging.MessageQueue "$queuepath"
`$utf8 = new-object System.Text.UTF8Encoding
`$msgBytes = `$utf8.GetBytes('$message')
`$msgStream = new-object System.IO.MemoryStream
`$msgStream.Write(`$msgBytes, 0, `$msgBytes.Length)
`$msg = new-object System.Messaging.Message
`$msg.BodyStream = `$msgStream
`$msg.Label = "RemoteQueueManagerPowershell"
`$queue.Send(`$msg)
"@
}
#Push Commands
Invoke-Command -ComputerName $targetMachine -ScriptBlock {
Param($user,$password,$scriptblock)
$scriptblock | Out-file -FilePath "C:\temp\ManageQueue.ps1" -Force
$action = New-ScheduledTaskAction -execute 'powershell.exe' -Argument '-File "C:\temp\ManageQueue.ps1"'
#scheudling action to start 2 seconds from now
$trigger = New-ScheduledTaskTrigger -Once -At ((Get-Date)+(New-TimeSpan -Seconds 2))
Register-ScheduledTask -TaskName RemoteQueueManager `
-Action $action `
-Trigger $trigger `
-User "$user"`
-Password $password
#Start-Sleep -Seconds 10
Unregister-ScheduledTask -TaskName RemoteQueueManager -Confirm:$false
Remove-Item -Path "C:\temp\ManageQueue.ps1" -Force
} -ArgumentList $user,$password,$scriptblock
}
}