Powershell Invoke-Command 导致不同的结果

标签 powershell msmq invoke-command

我有一个用于清除计算机上的消息队列的函数,但我希望对其进行调整并使其更加健壮。我希望能够向计算机发送命令,即使当前计算机没有安装 MSMQ。

本地命令可以正常工作,但是当调用调用命令时,检查队列是否存在将返回 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" 
}
}

注意: 为了确定到底发生了什么,我在存在语句上使用了 write-host ,它返回 false。当我传递脚本 block 时,未找到队列。它正在另一台机器上执行(测试写入文件成功)。当我运行时:

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

直接在计算机上运行相同的命令不会出现问题。

我还发现其他人试图在服务器故障上做类似的事情: 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

以下命令确实返回数据,但不允许我发送清除命令:

Invoke-Command -ComputerName $targetMachine -ScriptBlock {Get-WmiObject -class Win32_PerfRawData_MSMQ_MSMQQueue}

我还尝试将内容写入脚本文件,然后调用该文件,该文件在计算机上运行时可以正常工作,但通过调用命令调用时则不然:

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

最佳答案

我还没有找到造成这种情况的原因,但我会总结我所发现的内容和解决方法

摘要: 当通过invoke-command调用msmq命令时,只有私有(private)队列出现并且可以被操作。

解决方法: 我构建了一个函数,通过在远程计算机上创建计划任务来调用由命令创建的脚本来处理清除消息并将消息添加到队列中的功能。

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

关于Powershell Invoke-Command 导致不同的结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48956055/

相关文章:

msmq - 多播、消息传递、ActiveMQ 与 MSMQ?

java - 从远程服务器的 PowerShell 获取 Java 版本

powershell - 如何将 -Verbose 参数传递给 Invoke-Command 内部调用的函数?

c# - 如何将特定输出值从 exe (c#) 发送到 powershell 脚本

powershell - 如何防止 Powershell 使用 "Caching"管道数据

powershell - ConvertTo-Xml Powershell 未按预期工作

powershell - 如何在任何用户的帐户中显示 “Description”属性?

http - 寻找一种通过互联网传输关键实时数据的好方法

c# - 使用 msmq 和 wcf

powershell - 如何在powershell中解析json响应