[英]PowerShell forwarding events from remote PS Session
使用Register-EngineerEvent -Forward和New-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.