SCOMAgentHelper.TriggerWriteStatusEvent.WA

SCOMAgentHelper.TriggerWriteStatusEvent.WA (WriteActionModuleType)

Will trigger event to write current MM status to agent event log.

Element properties:

TypeWriteActionModuleType
IsolationAny
AccessibilityPublic
RunAsDefault
InputTypeSystem.BaseData
OutputTypeMicrosoft.Windows.SerializedObjectData

Member Modules:

ID Module Type TypeId RunAs 
POSH_WriteStatusEvent WriteAction Microsoft.Windows.PowerShellWriteAction Default

Overrideable Parameters:

IDParameterTypeSelector
WriteActionTimeoutSecondsint$Config/WriteActionTimeoutSeconds$
SpreadInitializationOverIntervalSecondsint$Config/SpreadInitializationOverIntervalSeconds$
WriteToEventLogbool$Config/WriteToEventLog$

Source Code:

<WriteActionModuleType ID="SCOMAgentHelper.TriggerWriteStatusEvent.WA" Accessibility="Public" Batching="false">
<Configuration>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="AgentDisplayName" minOccurs="1" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="SpreadInitializationOverIntervalSeconds" minOccurs="1" type="xsd:integer"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="WorkflowName" minOccurs="1" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="WriteActionTimeoutSeconds" minOccurs="1" type="xsd:integer"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="WriteToEventLog" minOccurs="1" type="xsd:boolean"/>
</Configuration>
<OverrideableParameters>
<OverrideableParameter ID="WriteActionTimeoutSeconds" Selector="$Config/WriteActionTimeoutSeconds$" ParameterType="int"/>
<OverrideableParameter ID="SpreadInitializationOverIntervalSeconds" Selector="$Config/SpreadInitializationOverIntervalSeconds$" ParameterType="int"/>
<OverrideableParameter ID="WriteToEventLog" Selector="$Config/WriteToEventLog$" ParameterType="bool"/>
</OverrideableParameters>
<ModuleImplementation Isolation="Any">
<Composite>
<MemberModules>
<WriteAction ID="POSH_WriteStatusEvent" TypeID="Windows!Microsoft.Windows.PowerShellWriteAction">
<ScriptName>TriggerAgentTask-WriteMMStatus.ps1</ScriptName>
<ScriptBody><Script>&lt;#
Script: TriggerAgentTask-WriteMMStatus.ps1
Description: This is for the WriteAction on the mgmt server to trigger the agent task which will write the MM status to the event log
Author: Tyson Paul
Version History:
2020.08.04.1531 - Added 'Verify' param to enable immediate exit if $Verify -match 'Workflows|None'. No point running this task in this case.
Added logic so task would trigger if an existing MM got updated and the Verify Workflows param was used. The posh module on agent will depend on the event log as no workflow activity events will occur from an 'update' to a MM window.
2020.07.31.1401 - Added logic to account for no Action param value; will immediately trigger status message.
2020.07.22.1935 - Added SpreadInitializationOverIntervalSeconds, WaitForStatusToUpdateSeconds
2020.07.20 - Original

Note: Why use 'Verify=MgmtPerspectiveOnly'? This would be useful if user is rebooting/poweroff a Computer. There's no need to wait for workflows to unload. The only concern is that the
mgmt server knows the object is in MM, and thus it won't alert about heartbeats. This relatively fast response to the agent log will indicate that it's 'safe' to power off the Computer
without waiting for workflows to unload.
#&gt;
Param (
[string]$Action,
[string]$AgentDisplayName,

# True|False. This indicates if the object was already in MM but was simply updated.
[string]$PreviousMMSettingsUpdated = 'False',

[Parameter(Mandatory=$false,
ValueFromPipeline=$false,
ValueFromPipelineByPropertyName=$false,
ValueFromRemainingArguments=$false,
ParameterSetName='Parameter Set 1')]
[ValidateSet("Workflows","MgmtPerspectiveOnly","None")]
[string]$Verify='MgmtPerspectiveOnly',

[int]$SpreadInitializationOverIntervalSeconds = 0,
[int]$WaitForStatusToUpdateSeconds,
[string]$WorkflowName = '&lt;no name provided&gt;',
[String]$WriteToEventLog='False'
)

[Bool]$WriteToEventLog = [System.Convert]::ToBoolean($WriteToEventLog)


&lt;# TESTING #############
$WriteToEventLog = $true
# $AgentDisplayName = 'db01.contoso.com'
#&gt;

########################################################################################################
Function LogIt {
Param
(
[int]$EventID,
[int]$Type = 2,
[string]$Message = 'No message specified.',
[bool]$Proceed,
$Line
)

If ($Proceed)
{
$output = @"

WorkflowName: $WorkflowName
Message: $Message

ThisScriptInstanceGUID: $ThisScriptInstanceGUID
ScriptLine: $Line
Running As: $whoami
Verify: $Verify
WriteToEventLog: $WriteToEventLog

Any Errors: $Error

"@

$oEvent = New-Object -ComObject 'MOM.ScriptAPI'
If ($output.Length -gt $maxLogLength){
$output = ($output.Substring(0,([math]::Min($output.Length,$maxLogLength) )) + '...TRUNCATED...')
}
$oEvent.LogScriptEvent("$ScriptName",$EventID,$Type,$output )
}
}
########################################################################################################
Function Import-SCOMPowerShellModule{

Import-Module OperationsManager

# Try to locate OperationsManager PowerShell module location
If (-NOT ((Get-Module OperationsManager).Count) ) {
Try {
$InstallDir = (Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\System Center Operations Manager\12\Setup\Powershell\V2").InstallDirectory
$SCOMPosh = (Join-Path $InstallDir "OperationsManager")
Import-module $SCOMPosh -ErrorAction Stop
If (-NOT ((Get-Module OperationsManager).Count) ) {
Throw
}
} Catch {
LogIt -EventID 9996 -Type $Critical -Message "Failed to import OperationsManager module. Exiting" -Proceed $WriteToEventLog -Line $(_LINE_)
Exit
}
}
}
########################################################################################################

Function Get-CurrentLineNumber {
$MyInvocation.ScriptLineNumber
}
########################################################################################################
New-Alias -Name _LINE_ -Value Get-CurrentLineNumber -Description 'Returns the current line number in a PowerShell script file.' -ErrorAction SilentlyContinue
########################################################################################################
[int]$info = 0
[int]$Critical = 1
[int]$warn = 2
[string]$whoami = whoami.exe
$ScriptName ='TriggerAgentTask-WriteMMStatus.ps1'
$ThisScriptInstanceGUID = (New-Guid).Guid.Substring(((New-Guid).Guid.Length ) -6).ToUpper()
[int]$maxLogLength = 31000 #max chars to allow for event log messages
$pauseForStatusCheckSeconds = 15


&lt;#
If the user indicated to Verify Workflows in combination with Force, this gets tricky. If the object was already in MM,
the duration would simply be updated but no workflow activity events would result on the agent so the powershell command would never verify/detect 1215/1216 events.
Check if the object was already in MM and duration was updated. If so, ignore 'Verify' settings, proceed with triggering task to write to event log.
The posh module on the agent will depend on this data because no workflow activity events will be written after a MM settings update.
#&gt;
If ( ($Verify -match 'None') -OR ( ($Verify -match 'Workflows') -AND ($PreviousMMSettingsUpdated -match 'False') )) {
LogIt -EventID 9991 -Type $info -Message "Verify: [$($Verify)]. No point writing status to agent log. Abandoning this script. Exiting." -Proceed $WriteToEventLog -Line $(_LINE_)
Exit
}

If ($SpreadInitializationOverIntervalSeconds) {
LogIt -EventID 9990 -Type $info -Message "Begin script. 'SpreadInitializationOverIntervalSeconds' detected. Pausing for random duration (seconds): [$($SpreadInitializationOverIntervalSeconds)]..." -Proceed $WriteToEventLog -Line $(_LINE_)
$RandSleep = (Get-Random -Minimum 0 -Maximum ($SpreadInitializationOverIntervalSeconds +1))
Start-Sleep -Seconds $RandSleep
}
Else {
LogIt -EventID 9990 -Type $info -Message "Begin script." -Proceed $WriteToEventLog -Line $(_LINE_)
}

. Import-SCOMPowerShellModule

If (-NOT ((Get-Module OperationsManager).Count) ) {
LogIt -EventID 9996 -Type $Critical -Message "Failed to import OperationsManager module. Exiting" -Proceed $WriteToEventLog -Line $(_LINE_)
Exit
}


# Connect to SCOM mgmt group
Try{
$MG = Get-SCOMManagementGroup -ErrorAction Stop
If (-NOT $MG) {
Throw "No SCOMManagementGroupConnection"
}
} Catch {
Try {
New-SCOMManagementGroupConnection -ErrorAction Stop
$MG = Get-SCOMManagementGroup -ErrorAction Stop
If (-NOT $MG) {
Throw "No SCOMManagementGroupConnection"
}
}Catch {
LogIt -EventID 9996 -Type $critical -Message "Unable to establish connect to mgmt group on localhost. Exiting." -Proceed $true -Line $(_LINE_)
Exit
}
}

# Get Computer object
$WinCompClass = Get-SCOMClass -Name "Microsoft.Windows.Computer"
$Criteria = "DisplayName = '$AgentDisplayName'"
$objCriteria = New-Object Microsoft.EnterpriseManagement.Monitoring.MonitoringObjectCriteria($Criteria ,$WinCompClass)
$WinComp = $MG.GetMonitoringObjects($objCriteria)

# If Action is provided it is assumed that MM change was recently triggered, the script will try to wait for the status to be updated so that the resulting status message will be completely accurate.
# Otherwise it is assumed that the intention is to simply trigger a status message with no other action taken.
If ($Action -match 'Enable|Disable') {
switch ($Action){
'Enable' {
$ExpectedStatus = $true
}

'Disable' {
$ExpectedStatus = $false
}
}

If (-NOT (($WinComp.InMaintenanceMode) -eq $ExpectedStatus)) {
LogIt -EventID 9992 -Type $info -Message "WinComp.DisplayName: $($WinComp.DisplayName), 'InMaintenanceMode' status [$($WinComp.InMaintenanceMode.ToString())] is not yet changed to expected status [$($ExpectedStatus)]. Will keep checking every [$($pauseForStatusCheckSeconds)] seconds until timeout [$($WaitForStatusToUpdateSeconds) seconds] is expired. " -Proceed $WriteToEventLog -Line $(_LINE_)
}

$timer = [System.Diagnostics.Stopwatch]::StartNew()
# Loop for a reasonable amount of time to get expected MM status, based on "Action" parameter.
While ( (-NOT (($WinComp.InMaintenanceMode) -eq $ExpectedStatus)) -AND ($timer.Elapsed.TotalSeconds -le ($WaitForStatusToUpdateSeconds + $RandSleep) ) ) {
Start-Sleep -Seconds $pauseForStatusCheckSeconds
$WinComp = $MG.GetMonitoringObjects($objCriteria)
}
$timer.Stop()
}


$hashMMStatus = @{
'True' = 'StatusWindowsComputerInMaintenanceModeTrue'
'False' = 'StatusWindowsComputerInMaintenanceModeFalse'
}
[string]$mmStatus = $hashMMStatus[($WinComp.InMaintenanceMode.ToString())]

LogIt -EventID 9992 -Type $info -Message "WinComp.DisplayName: $($WinComp.DisplayName), MMStatus: `n$($mmStatus) retrieved after $($timer.Elapsed.TotalSeconds) seconds." -Proceed $WriteToEventLog -Line $(_LINE_)


# Get Task
Try {
$TaskName = 'SCOMAgentHelper.WriteMaintModeStatustoEventLog.Task'
$TaskWF = Get-SCOMTask -Name $TaskName -ErrorAction Stop

# Get Task Target Instance
$TaskTargetClass = Get-SCOMClass -Id $TaskWF.Target.Id.Guid -ErrorAction Stop
$objCriteria = New-Object Microsoft.EnterpriseManagement.Monitoring.MonitoringObjectCriteria($Criteria ,$TaskTargetClass)
$TaskTargetInstance = $MG.GetMonitoringObjects($objCriteria)
LogIt -EventID 9992 -Type $info -Message "TaskTargetInstance.DisplayName: $($TaskTargetInstance.DisplayName)" -Proceed $WriteToEventLog -Line $(_LINE_)
} Catch {
LogIt -EventID 9996 -Type $critical -Message "Unable to set up Task: [$($TaskName)]. Exiting." -Proceed $true
Exit
}

# Set up override for task: status in param[1]. Include data indicating if an existing MM window was updated (param[2]).
$hashOverrides = @{
Message = "$($mmStatus)^PreviousMMSettingsUpdated:$($PreviousMMSettingsUpdated)"
}

Try {
$Task = Start-SCOMTask -Instance $TaskTargetInstance -Override $hashOverrides -Task $TaskWF -Verbose -ErrorAction Stop
LogIt -EventID 9992 -Type $info -Message "Task started. Task.Id.Guid:$($Task.Id.Guid)" -Proceed $WriteToEventLog -Line $(_LINE_)
} Catch {
LogIt -EventID 9996 -Type $critical -Message "Error while attempting to start task: [$($TaskName)]. Exiting." -Proceed $true -Line $(_LINE_)
Exit
}

$ScriptTimer = [System.Diagnostics.Stopwatch]::StartNew()
While ($TaskResult.Status -notmatch 'Succeeded' -AND ($ScriptTimer.Elapsed.TotalSeconds -le $WaitForStatusToUpdateSeconds) ) {
#Write-Host "$(Get-Date): Waiting for task to complete. Sleeping for 5 seconds" -F Yellow
$TaskResult = (Get-SCOMTaskResult -Id $Task.Id)
Start-Sleep -Seconds 5
}
$ScriptTimer.Stop()
If ($ScriptTimer.Elapsed.TotalSeconds -gt $WaitForStatusToUpdateSeconds){
LogIt -EventID 9992 -Type $warn -Message "Timer expired. Check task status manually. TaskID: $($Task.Id.Guid)" -Proceed $WriteToEventLog -Line $(_LINE_)
}
Else {
LogIt -EventID 9991 -Type $info -Message "Script end. Task completed. Output: $($TaskResult.Output.ToString())" -Proceed $WriteToEventLog -Line $(_LINE_)
$TaskResult.Output
}
</Script></ScriptBody>
<Parameters>
<Parameter>
<Name>AgentDisplayName</Name>
<Value>$Config/AgentDisplayName$</Value>
</Parameter>
<Parameter>
<Name>SpreadInitializationOverIntervalSeconds</Name>
<Value>$Config/SpreadInitializationOverIntervalSeconds$</Value>
</Parameter>
<Parameter>
<Name>WorkflowName</Name>
<Value>$Config/WorkflowName$</Value>
</Parameter>
<Parameter>
<Name>WriteToEventLog</Name>
<Value>$Config/WriteToEventLog$</Value>
</Parameter>
</Parameters>
<TimeoutSeconds>$Config/WriteActionTimeoutSeconds$</TimeoutSeconds>
</WriteAction>
</MemberModules>
<Composition>
<Node ID="POSH_WriteStatusEvent"/>
</Composition>
</Composite>
</ModuleImplementation>
<OutputType>Windows!Microsoft.Windows.SerializedObjectData</OutputType>
<InputType>System!System.BaseData</InputType>
</WriteActionModuleType>