【问题标题】:Powershell Invoke-Command causes different resultPowershell Invoke-Command 导致不同的结果
【发布时间】:2018-02-23 20:52:38
【问题描述】:

我有一个用于清除机器上的消息队列的函数,但我希望对其进行调整并使其更加健壮。即使当前机器没有安装 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" 
}
}

注意事项: 为了确定到底发生了什么,我在 exists 语句上使用了 write-host,它返回 false。当我通过脚本块时找不到队列。它正在另一台机器上执行(测试写入成功的文件)。当我跑步时:

Write-Host "$([System.Messaging.MessageQueue]::Exists($queueName))`n$queueName"
$objqueue = new-object -TypeName System.Messaging.MessageQueue -ArgumentList $queueName

我得到了错误的、正确的队列名称和以下错误:

使用“0”参数调用“Purge”的异常:“队列没有 存在或您没有足够的权限来执行 手术。” + 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 }

我得到以下结果:

找不到指定的机器。 + 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}

【问题讨论】:

    标签: powershell msmq invoke-command


    【解决方案1】:

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

    总结: 通过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
        }
    }
    

    【讨论】:

      【解决方案2】:

      从你的分析我感觉是权利问题。

      您是否检查了用户的权限?

      如果您是普通用户,您必须在 目标计算机/服务器/虚拟机:

      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 -ComputerName $targetMachine -ScriptBlock { gwmi -class Win32_PerfRawData_MSMQ_MSMQQueue }
      • @jewtus 所以我们需要进一步调试。您是否尝试在工作站上使用wireshark 来查看是否有任何异常?在服务器上,您看到命令传入?
      • 老实说,我对wireshark 不是很熟悉(我尝试下载并安装,但不确定如何设置实际监控)。我确实在远程机器上运行了 PROCMON,如果我将命令写入文件并在远程机器上调用该文件,我可以看到脚本(C:\Temp\PurgeQueue.ps1)在机器上运行。没有记录/捕获错误,队列仍然没有被清除。
      • @Jewtus 我明白了,如果你不使用它,wireshark 可能会很有挑战性。如果您通过 procmon 看到它,现在应该足够了。客户端上有哪些 powershell 版本,服务器上有哪些版本?
      • 要清楚,我问的是两者的 powershell 版本是否相同?
      猜你喜欢
      • 1970-01-01
      • 2022-12-02
      • 2014-10-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-12
      相关资源
      最近更新 更多