簡體   English   中英

PowerShell 從遠程 PS 轉發事件 Session

[英]PowerShell forwarding events from remote PS Session

使用Register-EngineerEvent -ForwardNew-Event我試圖將 object 事件從遠程服務器轉發到主機服務器,但它似乎不起作用。

為了證明這個理論,嘗試了下面有效簡單代碼:

$TargetServer = 'localhost'

Register-EngineEvent -SourceIdentifier TimerEventOccured -Action {
   Write-Host "$(Get-Date -format "dd-MM-yyyy hh:mm:ss tt") - $($event.MessageData) received..." -ForegroundColor Green
} | Out-Null

$TimerScriptBlock = {
    Register-EngineEvent -SourceIdentifier TimerEventOccured -Forward | Out-Null
    $Count = 1
    while($Count -lt 3) {
        New-Event -SourceIdentifier TimerEventOccured -MessageData 'Timertriggered'
        Start-Sleep -Seconds 5
        $Count += 1
    }
}

$RemoteTimerScriptBlockJob = Invoke-Command -ComputerName $TargetServer -ScriptBlock $TimerScriptBlock -AsJob

while($RemoteTimerScriptBlockJob.State -in @('NotStarted','Running')) {
    Write-Host "$(Get-Date -format "dd-MM-yyyy hh:mm:ss tt") - remote timer job still running"
    Start-Sleep -Seconds 5
}
Write-Host "$(Get-Date -format "dd-MM-yyyy hh:mm:ss tt") - remote timer job complete"

...在下面添加Register-ObjectEvent 的地方,這是我想要實現的,但沒有

$TargetServer = 'localhost'

Register-EngineEvent -SourceIdentifier TimerEventOccured -Action {
   Write-Host "$(Get-Date -format "dd-MM-yyyy hh:mm:ss tt") - $($event.MessageData) received..." -ForegroundColor Green
} | Out-Null

$TimerScriptBlock = {
    Register-EngineEvent -SourceIdentifier TimerEventOccured -Forward | Out-Null
    $timer = New-Object timers.timer 
    $timer.Enabled = $true 
    $timer.Interval = 3000
    Register-ObjectEvent -InputObject $timer -EventName elapsed –SourceIdentifier thetimer -Action $action {
        New-Event -SourceIdentifier TimerEventOccured -MessageData 'Timertriggered'
    }
    $timer.start()
    Start-Sleep -Seconds 15 #just wait long enough for timer events to trigger a few times
}

$RemoteTimerScriptBlockJob = Invoke-Command -ComputerName $TargetServer -ScriptBlock $TimerScriptBlock -AsJob

while($RemoteTimerScriptBlockJob.State -in @('NotStarted','Running')) {
    Write-Host "$(Get-Date -format "dd-MM-yyyy hh:mm:ss tt") - remote timer job still running"
    Start-Sleep -Seconds 5
}
Write-Host "$(Get-Date -format "dd-MM-yyyy hh:mm:ss tt") - remote timer job complete"

能否請你幫忙? 謝謝。

更新:

請注意,我可以直接將計時器事件轉發到源服務器,而不需要引擎事件作為中介。 但上面的計時器事件只是用來說明這里的觀點。 我正在處理的實際工作是監視某些事件 ID 的 Windows 事件日志(在這里分享已經變得相當復雜)。

因此,如果我直接在事件日志偵聽器 Object 上使用 -forward,那么它會產生從目標服務器到主機 session 的大量流量(即,將調度寫入的每個事件,而不是我所關注的唯一事件)。 我希望能夠首先在遠程服務器本身上處理觸發事件(以匹配輸入事件 ID),然后通過引擎事件轉發過濾后的事件,這就是我遇到的問題。

簡而言之: Register-ObjectEvent在您的情況下不是問題 - 這是您使用單個Start-Sleep調用之后立即退出的事實,這會導致大多數事件丟失。


當您使用Start-Sleep暫停 PowerShell 自己的前台線程時,您也暫停了它的事件處理。

具體來說,在您的情況下,結果如下:

  • Start-Sleep運行時,事件會排隊- 其直接的副作用是您的事件沒有及時處理。

  • Start-Sleep結束並且前台線程重新獲得控制權時,事件隊列開始得到處理,但是由於腳本塊立即結束,所以在遠程腳本塊的整體執行結束之前,只有一個 - 不可預測的 - 排隊事件的子集得到處理。 表面上看,PowerShell 並不能確保排隊的事件在退出之前得到處理。

因此,如果您將單個Start-Sleep -Seconds 15調用分解為多個調用,給 PowerShell 時間來處理其間的事件,您的代碼應該可以工作:

1..3 | ForEach-Object { Start-Sleep -Seconds 5 }

再次注意,不能保證如果事件之后仍然碰巧排隊,它們將在退出前被處理。

但是- 正如您后來發現的那樣 - 您可以使用Wait-Event -Timeout作為Start-Process的更好替代方案,因為它在等待時不會阻止-Action腳本塊和-Forward事件處理,從而允許轉發事件近實時處理。

  • 注意: Wait-Event的(以及Get-Event的)主要目的是檢索和 output排隊的事件,即未被Register-ObjectEvent / Register-EngineEvent事件訂閱消耗的事件基於-Action-Forward和必須按需檢索並采取行動。 但是,作為有益的副作用, Wait-Event還可以在等待時啟用基於注冊(基於訂閱者)的事件處理(通過-Action腳本塊和-Forward )。

以下獨立示例基於您的代碼構建:

  • 顯示Wait-Event在遠程腳本塊和本地的使用。

  • 使用Receive-Job檢索由遠程腳本塊直接生成的 output

  • 執行清理,包括遠程作業和區域設置事件訂閱。

  • 有關詳細信息,請參閱源代碼注釋。

  • 注意:因為使用了“loopback remoting”,所以必須為遠程設置本地機器,並且您必須以 ELEVATION 方式運行(作為管理員)—— #Requires -RunAsAdministrator指令強制執行后者。

#Requires -RunAsAdministrator
# Running ELEVATED is a must if you use Invoke-Command -ComputerName with the local machine.

$TargetServer = 'localhost'

$eventJob = Register-EngineEvent -SourceIdentifier TimerEventOccurred -Action {
   Write-Host "$(Get-Date -format "dd-MM-yyyy hh:mm:ss tt") - $($event.MessageData) received #$((++$i))..." -ForegroundColor Green
}

$TimerScriptBlock = {
    $null = Register-EngineEvent -SourceIdentifier TimerEventOccurred -Forward 
    $timer = New-Object timers.timer 
    $timer.Interval = 1000 # Fire every second
    $null = Register-ObjectEvent -InputObject $timer -EventName elapsed –SourceIdentifier thetimer -Action {
      Write-Host "$(Get-Date -format "dd-MM-yyyy hh:mm:ss tt") - $($event.MessageData) TRIGGERED #$((++$i))..."
      New-Event -SourceIdentifier TimerEventOccurred -MessageData 'Timertriggered'
    }
    $timer.start()
    # Produce events for a certain number of seconds.
    $secs = 5
    # Wait-Event - unlike Start-Sleep - does NOT block the event processing.
    # Note that since events created in this remote session are either forwarded
    # or handled via an -Action script block, Wait-Event will produce *no output*.
    Wait-Event -Timeout $secs
    # Hack only to make this sample code work more predictably: 
    # Ensure that the last event gets processed too:
    # -Timeout only accepts *whole* seconds and unpredictable runtime conditions
    # can result in the last event to not have been processed yet when Wait-Event returns.
    Start-Sleep -Milliseconds 100; Get-Event
    "Exiting remote script block after $secs seconds."
  }

$remoteTimerScriptBlockJob = Invoke-Command -ComputerName $TargetServer -ScriptBlock $TimerScriptBlock -AsJob


Write-Host "Processing events while waiting for the remote timer job to complete..."
do {
  # Note that since the TimerEventOccurred is handled via an -Action script block,
  # Wait-Event will produce *no output*, but it enables processing of those script blocks,
  # unlike Start-Sleep.
  Wait-Event -SourceIdentifier TimerEventOccurred -Timeout 3
} while ($remoteTimerScriptBlockJob.State -in 'NotStarted', 'Running')

Write-Host "$(Get-Date -format "dd-MM-yyyy hh:mm:ss tt") - Remote timer job terminated with the following output:"
# Receive the remote script block's output and clean up the job.
$remoteTimerScriptBlockJob | Receive-Job -Wait -AutoRemoveJob

# Also clean up the local event job.
$eventJob | Remove-Job -Force # -Force is needed, because event jobs run indefinitely.
# Note: This automatically also removes the job as an event subscriber, so there's no need
#       for an additional Unregister-Event call.

示例 output:

示例輸出

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM