Microsoft_Exchange_2010_Execute_TestGenericRollupHealth_Diagnostic_Script (DataSourceModuleType)

Element properties:

TypeDataSourceModuleType
IsolationAny
AccessibilityPublic
RunAsSystem.PrivilegedMonitoringAccount
OutputTypeSystem.PropertyBagData

Member Modules:

ID Module Type TypeId RunAs 
DS DataSource Microsoft_Exchange_2010_Execute_Diagnostic_Script Default

Overrideable Parameters:

IDParameterTypeSelector
IntervalSecondsint$Config/IntervalSeconds$
SyncTimestring$Config/SyncTime$
TimeoutSecondsint$Config/TimeoutSeconds$
Argumentsstring$Config/Arguments$
MonitoringDataSourcestring$Config/MonitoringDataSource$
MaxStartDelaySecondsint$Config/MaxStartDelaySeconds$

Source Code:

<DataSourceModuleType ID="Microsoft_Exchange_2010_Execute_TestGenericRollupHealth_Diagnostic_Script" Accessibility="Public" RunAs="System!System.PrivilegedMonitoringAccount">
<Configuration>
<xsd:element name="IntervalSeconds" type="xsd:int"/>
<xsd:element name="SyncTime" type="xsd:string"/>
<xsd:element name="TimeoutSeconds" type="xsd:int"/>
<xsd:element name="Arguments" type="xsd:string"/>
<xsd:element name="MonitoringDataSource" type="xsd:string"/>
<xsd:element name="MaxStartDelaySeconds" type="xsd:int"/>
</Configuration>
<OverrideableParameters>
<OverrideableParameter ID="IntervalSeconds" ParameterType="int" Selector="$Config/IntervalSeconds$"/>
<OverrideableParameter ID="SyncTime" ParameterType="string" Selector="$Config/SyncTime$"/>
<OverrideableParameter ID="TimeoutSeconds" ParameterType="int" Selector="$Config/TimeoutSeconds$"/>
<OverrideableParameter ID="Arguments" ParameterType="string" Selector="$Config/Arguments$"/>
<OverrideableParameter ID="MonitoringDataSource" ParameterType="string" Selector="$Config/MonitoringDataSource$"/>
<OverrideableParameter ID="MaxStartDelaySeconds" ParameterType="int" Selector="$Config/MaxStartDelaySeconds$"/>
</OverrideableParameters>
<ModuleImplementation>
<Composite>
<MemberModules>
<DataSource ID="DS" TypeID="Microsoft_Exchange_2010_Execute_Diagnostic_Script">
<IntervalSeconds>$Config/IntervalSeconds$</IntervalSeconds>
<SyncTime>$Config/SyncTime$</SyncTime>
<TimeoutSeconds>$Config/TimeoutSeconds$</TimeoutSeconds>
<ScriptName>TestGenericRollupHealth.ps1</ScriptName>
<Arguments>$Config/Arguments$</Arguments>
<ScriptBody><Script>
# Copyright (c) 2010 Microsoft Corporation. All rights reserved.
#
# THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK
# OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
#
# Diagnostic script that calculates the health of an "aggregation" entity based on its rollup relationships.
# This functions similarly to a DependencyMonitor.
#
# Given monitoring class ($MonitoringClassName) A,
# and a specified relationship class ($RelationshipClassName) defined as:
# Source = A, Target = B (A-&gt;B);
# rollup health will be calculated for A by aggregating the health of all instances of class B.
#
# Usage:
# .\TestGenericRollupHealth.ps1
# -MonitoringClassName:&lt;monitoring class name&gt;
# -RelationshipClassName:&lt;relationship class name&gt;
# -RollupPolicy:&lt;rollup policy&gt;
# -RollupPercentage:&lt;rollup percentage&gt;
# -MaintenanceModeRollup:&lt;rollup when member is in maintenance mode&gt;
# -MonitoringUnavailableRollup:&lt;rollup when member is unavailable&gt;
# -UninitializedStateRollup:&lt;rollup when member has no state (Uninitialized)&gt;
# -OpsMgrRmsServerName:&lt;OpsMgr RMS server name&gt;
#
# Examples:
# .\TestGenericRollupHealth.ps1 -MonitoringClassName:Microsoft.Exchange.2010.Mailbox.DatabaseService -RelationshipClassName:ExchangeReverseRollup_Microsoft.Exchange.2010.Mailbox.DatabaseService.Contains.Microsoft.Exchange.2010.Mailbox.DatabaseCopyMounted -RollupPolicy:BestOf -MaintenanceModeRollup:Success -MonitoringUnavailableRollup:Success -UninitializedStateRollup:Success

param(
# Name of the monitoring class to calculate the rollup health for.
[string] $MonitoringClassName = $(throw "MonitoringClassName must be specified."),

# Name of the relationship class to rollup health from.
[string] $RelationshipClassName = $(throw "RelationshipClassName must be specified."),

# Rollup policy.
[string] $RollupPolicy = $(throw "RollupPolicy must be specified."),

# Rollup percentage. Must be specified if Percentage RollupPolicy was specified.
# The group state will take on the worst state of the specified percentage of members in good health state.
# Using the same percentage definition as OpsMgr's DependencyMonitor.
[UInt16] $RollupPercentage,

# Rollup state of member when it is in maintenance mode.
# Only when a member is in maintenance mode will this rollup logic be exercised.
# Has precedence over $MonitoringUnavailableRollup and $UninitializedStateRollup.
# Health state is Uninitialized (0) when an entity is in maintenance mode.
# Using the same default mapping as OpsMgr's DependencyMonitor.
[string] $MaintenanceModeRollup = "Ignore",

# Rollup state of member when it is unavailable.
# Ignored if member is in maintenance mode.
# Has precedence over $UninitializedStateRollup.
# Using the same default mapping as OpsMgr's DependencyMonitor.
[string] $MonitoringUnavailableRollup = "Error",

# Rollup state of member when has no state (Uninitialized).
# Ignored if member is in maintenance mode or is unavailable.
[string] $UninitializedStateRollup = "Success",

# Name of the OpsMgr RMS server.
[string] $OpsMgrRmsServerName
)

# Validate parameters that are mandatory or has a restricted set of values.
#
$RollupPolicyValidSet = @("BestOf", "WorstOf", "Percentage");
if ($RollupPolicyValidSet -cnotcontains $RollupPolicy)
{
throw "RollupPolicy must be one of the following values: $($RollupPolicyValidSet)";
}

if (($RollupPercentage -gt 0) -and
!(($RollupPercentage -ge 1) -and ($RollupPercentage -le 99)))
{
throw "When specifying RollupPercentage, its value must be between 1 and 99.";
}

$MaintenanceModeRollupValidSet = @("Error", "Warning", "Success", "Ignore");
if (![String]::IsNullOrEmpty($MaintenanceModeRollup) -and
($MaintenanceModeRollupValidSet -cnotcontains $MaintenanceModeRollup))
{
throw "When specifying MaintenanceModeRollup, its value must be one of the following values: $($MaintenanceModeRollupValidSet)";
}

$MonitoringUnavailableRollupValidSet = @("Error", "Warning", "Success", "Ignore", "LastAvailableState");
if (![String]::IsNullOrEmpty($MonitoringUnavailableRollup) -and
($MonitoringUnavailableRollupValidSet -cnotcontains $MonitoringUnavailableRollup))
{
throw "When specifying MonitoringUnavailableRollup, its value must be one of the following values: $($MonitoringUnavailableRollupValidSet)";
}

$UninitializedStateRollupValidSet = @("Error", "Warning", "Success", "Ignore");
if (![String]::IsNullOrEmpty($UninitializedStateRollup) -and
($UninitializedStateRollupValidSet -cnotcontains $UninitializedStateRollup))
{
throw "When specifying UninitializedStateRollup, its value must be one of the following values: $($UninitializedStateRollupValidSet)";
}

# Dot source the common library.
. ".\DiagnosticScriptCommonLibrary.ps1";
$EVENT_ERROR = "Error";
$EVENT_WARNING = "Warning";
$EVENT_SUCCESS = "Information";
# Calculates the rollup health for all instances of the specified monitoring class and relationship.
#
function Calculate-RollupHealth
{
# RollupPercentage must be specified if Percentage RollupPolicy was specified.
#
if (($RollupPolicy -eq "Percentage") -and ($RollupPercentage -eq 0))
{
Add-MonitoringEvent -Id 1001 -Type $EVENT_TYPE_ERROR -Message "Parameter -RollupPercentage must be specified when Percentage RollupPolicy is specified.";
return;
}

# Add the OpsMgr snapin.
#
$momSnapinName = "Microsoft.EnterpriseManagement.OperationsManager.Client";
$momSnapin = Get-PSSnapin -Name $momSnapinName -ErrorAction SilentlyContinue;
if ($momSnapin -eq $null)
{
Add-PSSnapin -Name $momSnapinName -ErrorAction Stop;
}

# Connect to the RMS.
#
if (![String]::IsNullOrEmpty($OpsMgrRmsServerName))
{
$momRmsName = $OpsMgrRmsServerName;
}
else
{
# Assume we are running on the RMS which all aggregation objects run on.
$momRmsName = "localhost";
}
Set-Location -Path "OperationsManagerMonitoring::";
$mgConnection = New-ManagementGroupConnection -ConnectionString $momRmsName;
if ($mgConnection -eq $null)
{
Add-MonitoringEvent -Id 1001 -Type $EVENT_TYPE_ERROR -Message "Failed to connect to the Root Management Server '$($momRmsName)' with the following error: $($Error[0])";
return;
}
Set-Location -Path $momRmsName;

# Get the ManagementGroup object used for calling various SDK methods.
$mg = [Microsoft.EnterpriseManagement.ManagementGroup]::Connect($momRmsName);

# Get a collection of relationship instances
# for the specified relationship class which
# has the specified monitoring class as the source.
#
$monClass = Get-MonitoringClass -Name $MonitoringClassName;
$relClass = Get-RelationshipClass -Name $RelationshipClassName;
$criteria = "(MonitoringRelationshipClassId == '$($relClass.Id.ToString())') AND (IsDeleted != 'True')";
$relObjCriteria = New-Object Microsoft.EnterpriseManagement.Monitoring.MonitoringRelationshipObjectGenericCriteria $criteria;
$relObjs = $mg.GetMonitoringRelationshipObjectsBySourceMonitoringClass($relObjCriteria, $monClass);

if (($relObjs -eq $null) -or ($relObjs.Count -eq 0))
{
# Found no relationship objects for the $monClass and $relClass.
# Emit a warning event; author can choose whether to monitor this event.
Add-MonitoringEvent -Id 1002 -Type $EVENT_TYPE_WARNING -Message "Found no relationship object.";
return;
}

# Group by each source entity as we will be rolling up health from target entities that related to the same source.
#
# The grouped object is a hashtable of:
# Key = MonitoringObject Id
# Value = Collection of MonitoringRelationshipObject
#
$entities = @{};
foreach ($relObj in $relObjs)
{
$entityId = $relObj.SourceMonitoringObject.Id;

$relationships = $entities.($entityId);
if ($relationships -eq $null)
{
$relationships = @();
}
$relationships += $relObj;
$entities.($entityId) = $relationships;
}

# Calculate rollup health for each entity.
#
$entities.GetEnumerator() | foreach { Calculate-EntityRollupHealth -Relationships $_.Value; }
#Add a Monitoring event to notify that rollup calculation succeeded.
Add-MonitoringEvent -Id 1000 -Type $EVENT_SUCCESS -Message "Rollup Health calculation completed";
}

# Calculates the rollup health for the specified entity by aggregating the health of members in its relationships.
#
function Calculate-EntityRollupHealth (
$relationships)
{
# Get the MonitoringObject for the entity from any of the relationships' SourceMonitoringObject.
$entity = $relationships[0].SourceMonitoringObject;

# Get the InstanceName property on the entity.
# $instanceName will be assigned $null if this property does not exist.
$instanceNameProperty = Get-MonitoringObjectProperty -MonitoringObject $entity | where { $_.Name -eq "InstanceName"; }
$instanceName = $instanceNameProperty.Value;

# Create an object to represent each member as we need to
# derive each member's state based on input parameters.
#
$members = @();
foreach ($relationship in $relationships)
{
$member = New-Object System.Management.Automation.PSObject;
$member | Add-Member -MemberType NoteProperty -Name "Name" -Value $relationship.TargetMonitoringObject.DisplayName;
$member | Add-Member -MemberType NoteProperty -Name "IsAvailable" -Value $relationship.TargetMonitoringObject.IsAvailable;
$member | Add-Member -MemberType NoteProperty -Name "InMaintenanceMode" -Value $relationship.TargetMonitoringObject.InMaintenanceMode;
$member | Add-Member -MemberType NoteProperty -Name "HealthState" -Value $relationship.TargetMonitoringObject.HealthState;

# Calculate the derived health state for each member.
# Maintenance mode has precedence over whether the member is available.
#
if ($member.InMaintenanceMode)
{
switch ($MaintenanceModeRollup)
{
"Error"
{
$member | Add-Member -MemberType NoteProperty -Name "DerivedHealthState" -Value ([Microsoft.EnterpriseManagement.Configuration.HealthState] [Microsoft.EnterpriseManagement.Configuration.HealthState]::Error);
break;
}
"Warning"
{
$member | Add-Member -MemberType NoteProperty -Name "DerivedHealthState" -Value ([Microsoft.EnterpriseManagement.Configuration.HealthState] [Microsoft.EnterpriseManagement.Configuration.HealthState]::Warning);
break;
}
"Success"
{
$member | Add-Member -MemberType NoteProperty -Name "DerivedHealthState" -Value ([Microsoft.EnterpriseManagement.Configuration.HealthState] [Microsoft.EnterpriseManagement.Configuration.HealthState]::Success);
break;
}
"Ignore"
{
# Uninitialized health state will be excluded from the health state aggregation.
$member | Add-Member -MemberType NoteProperty -Name "DerivedHealthState" -Value ([Microsoft.EnterpriseManagement.Configuration.HealthState] [Microsoft.EnterpriseManagement.Configuration.HealthState]::Uninitialized);
break;
}
}
}
else
{
if (!$member.IsAvailable)
{
switch ($MonitoringUnavailableRollup)
{
"Error"
{
$member | Add-Member -MemberType NoteProperty -Name "DerivedHealthState" -Value ([Microsoft.EnterpriseManagement.Configuration.HealthState] [Microsoft.EnterpriseManagement.Configuration.HealthState]::Error);
break;
}
"Warning"
{
$member | Add-Member -MemberType NoteProperty -Name "DerivedHealthState" -Value ([Microsoft.EnterpriseManagement.Configuration.HealthState] [Microsoft.EnterpriseManagement.Configuration.HealthState]::Warning);
break;
}
"Success"
{
$member | Add-Member -MemberType NoteProperty -Name "DerivedHealthState" -Value ([Microsoft.EnterpriseManagement.Configuration.HealthState] [Microsoft.EnterpriseManagement.Configuration.HealthState]::Success);
break;
}
"Ignore"
{
# Uninitialized health state will be excluded from the health state aggregation.
$member | Add-Member -MemberType NoteProperty -Name "DerivedHealthState" -Value ([Microsoft.EnterpriseManagement.Configuration.HealthState] [Microsoft.EnterpriseManagement.Configuration.HealthState]::Uninitialized);
break;
}
"LastAvailableState"
{
# When an entity is unavailable, it still holds the health state from when it was last available.
$member | Add-Member -MemberType NoteProperty -Name "DerivedHealthState" -Value $member.HealthState;
break;
}
}

}
else
{
if ($member.HealthState -eq [Microsoft.EnterpriseManagement.Configuration.HealthState]::Uninitialized)
{
switch ($UninitializedStateRollup)
{
"Error"
{
$member | Add-Member -MemberType NoteProperty -Name "DerivedHealthState" -Value ([Microsoft.EnterpriseManagement.Configuration.HealthState] [Microsoft.EnterpriseManagement.Configuration.HealthState]::Error);
break;
}
"Warning"
{
$member | Add-Member -MemberType NoteProperty -Name "DerivedHealthState" -Value ([Microsoft.EnterpriseManagement.Configuration.HealthState] [Microsoft.EnterpriseManagement.Configuration.HealthState]::Warning);
break;
}
"Success"
{
$member | Add-Member -MemberType NoteProperty -Name "DerivedHealthState" -Value ([Microsoft.EnterpriseManagement.Configuration.HealthState] [Microsoft.EnterpriseManagement.Configuration.HealthState]::Success);
break;
}
"Ignore"
{
# Uninitialized health state will be excluded from the health state aggregation.
$member | Add-Member -MemberType NoteProperty -Name "DerivedHealthState" -Value ([Microsoft.EnterpriseManagement.Configuration.HealthState] [Microsoft.EnterpriseManagement.Configuration.HealthState]::Uninitialized);
break;
}
}
}
else
{
# The member is not in MM, unavailable, nor has an Uninitialized state. Assign DerivedHealthState to be the same as HealthState.
$member | Add-Member -MemberType NoteProperty -Name "DerivedHealthState" -Value $member.HealthState;
}
}
}

$members += $member;
}

# Filter out the members that should be ignored from the calculation,
# and sort the members by DerivedHealthState in ascending order (best to worst).
#
# HealthState is a enum type defined as:
# Uninitialized = 0
# Success = 1
# Warning = 2
# Error = 3
#
$filteredMembers = @($members | `
where { $_.DerivedHealthState -ne [Microsoft.EnterpriseManagement.Configuration.HealthState]::Uninitialized; } | `
Sort-Object -Property DerivedHealthState);

# Calculate the aggregated health state for the passed in entity.
#
if ($filteredMembers.Length -eq 0)
{
# No valid health state can be determined.
# This most likely means that there's no monitors targeting any member --
# assume green.
Add-MonitoringEventForEntityAndMembers `
-HealthState ([Microsoft.EnterpriseManagement.Configuration.HealthState]::Success) `
-InstanceName $instanceName `
-EntityName $entity.DisplayName `
-Members $members;
}
else
{
switch ($RollupPolicy)
{
"BestOf"
{
# Pick the first member in the sorted (best-&gt;worst) array.
Add-MonitoringEventForEntityAndMembers `
-HealthState ($filteredMembers[0].DerivedHealthState) `
-InstanceName $instanceName `
-EntityName $entity.DisplayName `
-Members $members;
break;
}

"WorstOf"
{
# Pick the last member in the sorted (best-&gt;worst) array.
Add-MonitoringEventForEntityAndMembers `
-HealthState ($filteredMembers[$filteredMembers.Length - 1].DerivedHealthState) `
-InstanceName $instanceName `
-EntityName $entity.DisplayName `
-Members $members;
break;
}

# The group state will take on the worst state of the specified percentage of members in good health state.
"Percentage"
{
# First select the best $RollupPercentage amongst the members.
# Since the $filteredMembers array is already sorted from best to worst,
# we will just pick out the first $RollupPercentage members.
$i = [Math]::Round($filteredMembers.Length * ($RollupPercentage / 100));
$percentageFilteredMembers = $filteredMembers[0..$i];

# Then get health state from the last (worst state) member out of the percentage filtered set of members.
Add-MonitoringEventForEntityAndMembers `
-HealthState ($percentageFilteredMembers[$percentageFilteredMembers.Length - 1].DerivedHealthState) `
-InstanceName $instanceName `
-EntityName $entity.DisplayName `
-Members $members;

break;
}
}
}
}

# Adds a monitoring event to the windows event log.
#
function Add-MonitoringWindowsEvent (
[int] $id = $(throw "Id must be specified."),
[string] $type = $(throw "Type must be specified."),
[string] $message = $(throw "Message must be specified."),
[string] $instanceName,
[string] $server = ".",
[string] $logName = "Exchange Monitoring Events",
[string] $eventSource = "MSExchange Diagnostic Rollup" )
{
#Create event log if it does not exist.
if (!([System.Diagnostics.EventLog]::SourceExists($eventSource, $server)))
{
write-verbose "Event source $eventSource doesn't exist on $Server. Creating it."

$creationData = new-object System.Diagnostics.EventSourceCreationData($eventSource, $logName)
$creationData.MachineName = $server
[System.Diagnostics.EventLog]::CreateEventSource($creationData)
}

$evt=new-object System.Diagnostics.EventLog($logName);
$evt.Source=$eventSource;

$eventInstance = new-object System.Diagnostics.EventInstance($id, 1, $type)

if ([String]::IsNullOrEmpty($instanceName))
{
[string[]]$messageAndParams= @($message);
}
else
{
[string[]]$messageAndParams= @($message) + @($instanceName)
}
$evt.WriteEvent($eventInstance, $MessageAndParams);
}



# Creates windows events for the entity health.
#
function Add-MonitoringEventForEntityAndMembers (
[Microsoft.EnterpriseManagement.Configuration.HealthState] $healthState,
$instanceName,
$entityName,
$members)
{

$message = "Name: $($entityName) `n";
$message += "RollupPolicy: $($RollupPolicy) `n";
$message += "RollupPercentage: $($RollupPercentage) `n";
$message += "MaintenanceModeRollup: $($MaintenanceModeRollup) `n";
$message += "MonitoringUnavailableRollup: $($MonitoringUnavailableRollup) `n";
$message += "UninitializedStateRollup: $($UninitializedStateRollup) `n";
$message += "Members ($($members.Length)): `n";
foreach ($member in $members)
{
$message += "{ Name=$($member.Name); IsAvailable=$($member.IsAvailable); InMaintenanceMode=$($member.InMaintenanceMode); HealthState=$($member.HealthState); DerivedHealthState=$($member.DerivedHealthState) } `n";
}

switch ([int] $healthState)
{
([int] [Microsoft.EnterpriseManagement.Configuration.HealthState]::Success)
{
Add-MonitoringWindowsEvent -Id 1000 -Type $EVENT_SUCCESS -Message $message -InstanceName $instanceName;
break;
}
([int] [Microsoft.EnterpriseManagement.Configuration.HealthState]::Warning)
{
Add-MonitoringWindowsEvent -Id 1002 -Type $EVENT_WARNING -Message $message -InstanceName $instanceName;
break;
}
([int] [Microsoft.EnterpriseManagement.Configuration.HealthState]::Error)
{
Add-MonitoringWindowsEvent -Id 1001 -Type $EVENT_ERROR -Message $message -InstanceName $instanceName;
break;
}
}
}

# Save the original location as OpsMgr commands require setting a specific path.
$originalLocation = Get-Location;

Calculate-RollupHealth;
trap
{
Add-MonitoringEvent -Id 1001 -Type $EVENT_TYPE_ERROR -Message "Calculate-RollupHealth failed with the following error: $($Error[0])";
continue;
}

# Done with OpsMgr commands, restore the original location.
Set-Location -Path $originalLocation;

# Output events.
Write-MonitoringEvents;
</Script></ScriptBody>
<MonitoringDataSource>$Config/MonitoringDataSource$</MonitoringDataSource>
<MaxStartDelaySeconds>$Config/MaxStartDelaySeconds$</MaxStartDelaySeconds>
</DataSource>
</MemberModules>
<Composition>
<Node ID="DS"/>
</Composition>
</Composite>
</ModuleImplementation>
<OutputType>System!System.PropertyBagData</OutputType>
</DataSourceModuleType>