LS QoE Discovery Data Source

Microsoft.LS.2013.QoE.MonitoringNode.DiscoveryDataSource (DataSourceModuleType)

Microsoft Lync Server 2013 QoE Discovery Data Source

Element properties:

TypeDataSourceModuleType
IsolationAny
AccessibilityInternal
RunAsMicrosoft.LS.2013.RunAsAccount
OutputTypeSystem.Discovery.Data

Member Modules:

ID Module Type TypeId RunAs 
Microsoft.LS.2013.QoE.MonitoringNode.DiscoverProvider DataSource Microsoft.LS.2013.Discovery.Common.DS.ShellOut Default

Overrideable Parameters:

IDParameterTypeSelectorDisplay NameDescription
Subnet_ExcludeInstancesstring$Config/Subnet_ExcludeInstances$Excluded instances (Calls from or to a Subnet)
Subnet_CriticalAlertThresholdPercentagedouble$Config/Subnet_CriticalAlertThresholdPercentage$Critical alert threshold \% (Calls from or to a Subnet)
Subnet_WarningAlertThresholdPercentagedouble$Config/Subnet_WarningAlertThresholdPercentage$Warning alert threshold \% (Calls from or to a Subnet)
IntraUserSite_ExcludeInstancesstring$Config/IntraUserSite_ExcludeInstances$Excluded instances (Calls within a Site)
IntraUserSite_CriticalAlertThresholdPercentagedouble$Config/IntraUserSite_CriticalAlertThresholdPercentage$Critical alert threshold \% (Calls within a Site)
IntraUserSite_WarningAlertThresholdPercentagedouble$Config/IntraUserSite_WarningAlertThresholdPercentage$Warning alert threshold \% (Calls within a Site)
InterUserSite_ExcludeInstancesstring$Config/InterUserSite_ExcludeInstances$Excluded instances (Calls between Sites)
InterUserSite_CriticalAlertThresholdPercentagedouble$Config/InterUserSite_CriticalAlertThresholdPercentage$Critical alert threshold \% (Calls between Sites)
InterUserSite_WarningAlertThresholdPercentagedouble$Config/InterUserSite_WarningAlertThresholdPercentage$Warning alert threshold \% (Calls between Sites)
InterRegion_ExcludeInstancesstring$Config/InterRegion_ExcludeInstances$Excluded instances (Calls between Regions)
InterRegion_CriticalAlertThresholdPercentagedouble$Config/InterRegion_CriticalAlertThresholdPercentage$Critical alert threshold \% (Calls between Regions)
InterRegion_WarningAlertThresholdPercentagedouble$Config/InterRegion_WarningAlertThresholdPercentage$Warning alert threshold \% (Calls between Regions)
GatewayClient_ExcludeInstancesstring$Config/GatewayClient_ExcludeInstances$Excluded instances (Gateway (Mediation Server Bypass))
GatewayClient_CriticalAlertThresholdPercentagedouble$Config/GatewayClient_CriticalAlertThresholdPercentage$Critical alert threshold \% (Gateway (Mediation Server Bypass))
GatewayClient_WarningAlertThresholdPercentagedouble$Config/GatewayClient_WarningAlertThresholdPercentage$Warning alert threshold \% (Gateway (Mediation Server Bypass))
GatewayMS_ExcludeInstancesstring$Config/GatewayMS_ExcludeInstances$Excluded instances (Gateway and Mediation Server Leg)
GatewayMS_CriticalAlertThresholdPercentagedouble$Config/GatewayMS_CriticalAlertThresholdPercentage$Critical alert threshold \% (Gateway and Mediation Server Leg)
GatewayMS_WarningAlertThresholdPercentagedouble$Config/GatewayMS_WarningAlertThresholdPercentage$Warning alert threshold \% (Gateway and Mediation Server Leg)
MediationServer_ExcludeInstancesstring$Config/MediationServer_ExcludeInstances$Excluded instances (Mediation Server and Client Endpoint Leg)
MediationServer_CriticalAlertThresholdPercentagedouble$Config/MediationServer_CriticalAlertThresholdPercentage$Critical alert threshold \% (Mediation Server and Client Endpoint Leg)
MediationServer_WarningAlertThresholdPercentagedouble$Config/MediationServer_WarningAlertThresholdPercentage$Warning alert threshold \% (Mediation Server and Client Endpoint Leg)
AVConf_ExcludeInstancesstring$Config/AVConf_ExcludeInstances$Excluded instances (A/V Conferencing Server)
AVConf_CriticalAlertThresholdPercentagedouble$Config/AVConf_CriticalAlertThresholdPercentage$Critical alert threshold \% (A/V Conferencing Server)
AVConf_WarningAlertThresholdPercentagedouble$Config/AVConf_WarningAlertThresholdPercentage$Warning alert threshold \% (A/V Conferencing Server)
Default_IncludeExternalCallsbool$Config/Default_IncludeExternalCalls$Include external calls (Default)
Default_IncludeVPNCallsbool$Config/Default_IncludeVPNCalls$Include VPN calls (Default)
Default_IncludeWIFICallsbool$Config/Default_IncludeWIFICalls$Include WIFI calls (Default)
Default_CriticalAlertThresholdPercentagedouble$Config/Default_CriticalAlertThresholdPercentage$Critical alert threshold \% (Default)
Default_WarningAlertThresholdPercentagedouble$Config/Default_WarningAlertThresholdPercentage$Warning alert threshold \% (Default)
Default_MinUsersAffectedint$Config/Default_MinUsersAffected$Minimum affected users (Default)
Default_MinCallsAffectedint$Config/Default_MinCallsAffected$Minimum affected calls (Default)
Default_MinutesToQueryint$Config/Default_MinutesToQuery$Minutes to query (Default)
Default_Frequencyint$Config/Default_Frequency$Frequency (Default)

Source Code:

<DataSourceModuleType ID="Microsoft.LS.2013.QoE.MonitoringNode.DiscoveryDataSource" RunAs="Microsoft.LS.2013.RunAsAccount" Accessibility="Internal" Batching="false">
<Configuration>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="ComputerID" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="SQLInstance" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="MirrorSQLInstance" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="ReportingServiceUrl" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="TimeoutSeconds" type="xsd:int"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="Default_Frequency" type="xsd:int"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="Default_MinutesToQuery" type="xsd:int"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="Default_MinCallsAffected" type="xsd:int"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="Default_MinUsersAffected" type="xsd:int"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="Default_WarningAlertThresholdPercentage" type="xsd:double"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="Default_CriticalAlertThresholdPercentage" type="xsd:double"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="Default_IncludeWIFICalls" type="xsd:boolean"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="Default_IncludeVPNCalls" type="xsd:boolean"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="Default_IncludeExternalCalls" type="xsd:boolean"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="AVConf_WarningAlertThresholdPercentage" type="xsd:double"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="AVConf_CriticalAlertThresholdPercentage" type="xsd:double"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="AVConf_ExcludeInstances" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="MediationServer_WarningAlertThresholdPercentage" type="xsd:double"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="MediationServer_CriticalAlertThresholdPercentage" type="xsd:double"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="MediationServer_ExcludeInstances" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="GatewayMS_WarningAlertThresholdPercentage" type="xsd:double"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="GatewayMS_CriticalAlertThresholdPercentage" type="xsd:double"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="GatewayMS_ExcludeInstances" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="GatewayClient_WarningAlertThresholdPercentage" type="xsd:double"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="GatewayClient_CriticalAlertThresholdPercentage" type="xsd:double"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="GatewayClient_ExcludeInstances" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="InterRegion_WarningAlertThresholdPercentage" type="xsd:double"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="InterRegion_CriticalAlertThresholdPercentage" type="xsd:double"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="InterRegion_ExcludeInstances" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="InterUserSite_WarningAlertThresholdPercentage" type="xsd:double"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="InterUserSite_CriticalAlertThresholdPercentage" type="xsd:double"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="InterUserSite_ExcludeInstances" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="IntraUserSite_WarningAlertThresholdPercentage" type="xsd:double"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="IntraUserSite_CriticalAlertThresholdPercentage" type="xsd:double"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="IntraUserSite_ExcludeInstances" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="Subnet_WarningAlertThresholdPercentage" type="xsd:double"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="Subnet_CriticalAlertThresholdPercentage" type="xsd:double"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="Subnet_ExcludeInstances" type="xsd:string"/>
</Configuration>
<OverrideableParameters>
<OverrideableParameter ID="Default_Frequency" ParameterType="int" Selector="$Config/Default_Frequency$"/>
<OverrideableParameter ID="Default_MinutesToQuery" ParameterType="int" Selector="$Config/Default_MinutesToQuery$"/>
<OverrideableParameter ID="Default_MinCallsAffected" ParameterType="int" Selector="$Config/Default_MinCallsAffected$"/>
<OverrideableParameter ID="Default_MinUsersAffected" ParameterType="int" Selector="$Config/Default_MinUsersAffected$"/>
<OverrideableParameter ID="Default_WarningAlertThresholdPercentage" ParameterType="double" Selector="$Config/Default_WarningAlertThresholdPercentage$"/>
<OverrideableParameter ID="Default_CriticalAlertThresholdPercentage" ParameterType="double" Selector="$Config/Default_CriticalAlertThresholdPercentage$"/>
<OverrideableParameter ID="Default_IncludeWIFICalls" ParameterType="bool" Selector="$Config/Default_IncludeWIFICalls$"/>
<OverrideableParameter ID="Default_IncludeVPNCalls" ParameterType="bool" Selector="$Config/Default_IncludeVPNCalls$"/>
<OverrideableParameter ID="Default_IncludeExternalCalls" ParameterType="bool" Selector="$Config/Default_IncludeExternalCalls$"/>
<OverrideableParameter ID="AVConf_WarningAlertThresholdPercentage" ParameterType="double" Selector="$Config/AVConf_WarningAlertThresholdPercentage$"/>
<OverrideableParameter ID="AVConf_CriticalAlertThresholdPercentage" ParameterType="double" Selector="$Config/AVConf_CriticalAlertThresholdPercentage$"/>
<OverrideableParameter ID="AVConf_ExcludeInstances" ParameterType="string" Selector="$Config/AVConf_ExcludeInstances$"/>
<OverrideableParameter ID="MediationServer_WarningAlertThresholdPercentage" ParameterType="double" Selector="$Config/MediationServer_WarningAlertThresholdPercentage$"/>
<OverrideableParameter ID="MediationServer_CriticalAlertThresholdPercentage" ParameterType="double" Selector="$Config/MediationServer_CriticalAlertThresholdPercentage$"/>
<OverrideableParameter ID="MediationServer_ExcludeInstances" ParameterType="string" Selector="$Config/MediationServer_ExcludeInstances$"/>
<OverrideableParameter ID="GatewayMS_WarningAlertThresholdPercentage" ParameterType="double" Selector="$Config/GatewayMS_WarningAlertThresholdPercentage$"/>
<OverrideableParameter ID="GatewayMS_CriticalAlertThresholdPercentage" ParameterType="double" Selector="$Config/GatewayMS_CriticalAlertThresholdPercentage$"/>
<OverrideableParameter ID="GatewayMS_ExcludeInstances" ParameterType="string" Selector="$Config/GatewayMS_ExcludeInstances$"/>
<OverrideableParameter ID="GatewayClient_WarningAlertThresholdPercentage" ParameterType="double" Selector="$Config/GatewayClient_WarningAlertThresholdPercentage$"/>
<OverrideableParameter ID="GatewayClient_CriticalAlertThresholdPercentage" ParameterType="double" Selector="$Config/GatewayClient_CriticalAlertThresholdPercentage$"/>
<OverrideableParameter ID="GatewayClient_ExcludeInstances" ParameterType="string" Selector="$Config/GatewayClient_ExcludeInstances$"/>
<OverrideableParameter ID="InterRegion_WarningAlertThresholdPercentage" ParameterType="double" Selector="$Config/InterRegion_WarningAlertThresholdPercentage$"/>
<OverrideableParameter ID="InterRegion_CriticalAlertThresholdPercentage" ParameterType="double" Selector="$Config/InterRegion_CriticalAlertThresholdPercentage$"/>
<OverrideableParameter ID="InterRegion_ExcludeInstances" ParameterType="string" Selector="$Config/InterRegion_ExcludeInstances$"/>
<OverrideableParameter ID="InterUserSite_WarningAlertThresholdPercentage" ParameterType="double" Selector="$Config/InterUserSite_WarningAlertThresholdPercentage$"/>
<OverrideableParameter ID="InterUserSite_CriticalAlertThresholdPercentage" ParameterType="double" Selector="$Config/InterUserSite_CriticalAlertThresholdPercentage$"/>
<OverrideableParameter ID="InterUserSite_ExcludeInstances" ParameterType="string" Selector="$Config/InterUserSite_ExcludeInstances$"/>
<OverrideableParameter ID="IntraUserSite_WarningAlertThresholdPercentage" ParameterType="double" Selector="$Config/IntraUserSite_WarningAlertThresholdPercentage$"/>
<OverrideableParameter ID="IntraUserSite_CriticalAlertThresholdPercentage" ParameterType="double" Selector="$Config/IntraUserSite_CriticalAlertThresholdPercentage$"/>
<OverrideableParameter ID="IntraUserSite_ExcludeInstances" ParameterType="string" Selector="$Config/IntraUserSite_ExcludeInstances$"/>
<OverrideableParameter ID="Subnet_WarningAlertThresholdPercentage" ParameterType="double" Selector="$Config/Subnet_WarningAlertThresholdPercentage$"/>
<OverrideableParameter ID="Subnet_CriticalAlertThresholdPercentage" ParameterType="double" Selector="$Config/Subnet_CriticalAlertThresholdPercentage$"/>
<OverrideableParameter ID="Subnet_ExcludeInstances" ParameterType="string" Selector="$Config/Subnet_ExcludeInstances$"/>
</OverrideableParameters>
<ModuleImplementation Isolation="Any">
<Composite>
<MemberModules>
<DataSource ID="Microsoft.LS.2013.QoE.MonitoringNode.DiscoverProvider" TypeID="Microsoft.LS.2013.Discovery.Common.DS.ShellOut">
<IntervalSeconds>$Config/Default_Frequency$</IntervalSeconds>
<SyncTime>00:00</SyncTime>
<ScriptName>Lync Server MP QoE Alert Discovery</ScriptName>
<ScriptFileName>DiscoverAlertQoE.ps1</ScriptFileName>
<ScriptBody><Script>
[String] $ComputerName = '$Config/ComputerID$'
[String] $SQLInstance = '$Config/SQLInstance$'
[String] $MirrorSQLInstance = '$Config/MirrorSQLInstance$'
[String] $ReportingServiceHomePageUrl = '$Config/ReportingServiceUrl$'
[int] $Default_MinutesToQuery = $Config/Default_MinutesToQuery$
[int] $Default_MinCallsAffected = $Config/Default_MinCallsAffected$
[int] $Default_MinUsersAffected = $Config/Default_MinUsersAffected$
[double] $Default_WarningAlertThresholdPercentage = $Config/Default_WarningAlertThresholdPercentage$
[double] $Default_CriticalAlertThresholdPercentage = $Config/Default_CriticalAlertThresholdPercentage$
[string] $Default_IncludeWIFICalls = '$Config/Default_IncludeWIFICalls$'
[string] $Default_IncludeVPNCalls = '$Config/Default_IncludeVPNCalls$'
[string] $Default_IncludeExternalCalls = '$Config/Default_IncludeExternalCalls$'
[double] $AVConf_WarningAlertThresholdPercentage = $Config/AVConf_WarningAlertThresholdPercentage$
[double] $AVConf_CriticalAlertThresholdPercentage = $Config/AVConf_CriticalAlertThresholdPercentage$
[String] $AVConf_ExcludeInstances = '$Config/AVConf_ExcludeInstances$'
[double] $MediationServer_WarningAlertThresholdPercentage = $Config/MediationServer_WarningAlertThresholdPercentage$
[double] $MediationServer_CriticalAlertThresholdPercentage = $Config/MediationServer_CriticalAlertThresholdPercentage$
[String] $MediationServer_ExcludeInstances = '$Config/MediationServer_ExcludeInstances$'
[double] $GatewayMS_WarningAlertThresholdPercentage = $Config/GatewayMS_WarningAlertThresholdPercentage$
[double] $GatewayMS_CriticalAlertThresholdPercentage = $Config/GatewayMS_CriticalAlertThresholdPercentage$
[String] $GatewayMS_ExcludeInstances = '$Config/GatewayMS_ExcludeInstances$'
[double] $GatewayClient_WarningAlertThresholdPercentage = $Config/GatewayClient_WarningAlertThresholdPercentage$
[double] $GatewayClient_CriticalAlertThresholdPercentage = $Config/GatewayClient_CriticalAlertThresholdPercentage$
[String] $GatewayClient_ExcludeInstances = '$Config/GatewayClient_ExcludeInstances$'
[double] $InterRegion_WarningAlertThresholdPercentage = $Config/InterRegion_WarningAlertThresholdPercentage$
[double] $InterRegion_CriticalAlertThresholdPercentage = $Config/InterRegion_CriticalAlertThresholdPercentage$
[String] $InterRegion_ExcludeInstances = '$Config/InterRegion_ExcludeInstances$'
[double] $InterUserSite_WarningAlertThresholdPercentage = $Config/InterUserSite_WarningAlertThresholdPercentage$
[double] $InterUserSite_CriticalAlertThresholdPercentage = $Config/InterUserSite_CriticalAlertThresholdPercentage$
[String] $InterUserSite_ExcludeInstances = '$Config/InterUserSite_ExcludeInstances$'
[double] $IntraUserSite_WarningAlertThresholdPercentage = $Config/IntraUserSite_WarningAlertThresholdPercentage$
[double] $IntraUserSite_CriticalAlertThresholdPercentage = $Config/IntraUserSite_CriticalAlertThresholdPercentage$
[String] $IntraUserSite_ExcludeInstances = '$Config/IntraUserSite_ExcludeInstances$'
[double] $Subnet_WarningAlertThresholdPercentage = $Config/Subnet_WarningAlertThresholdPercentage$
[double] $Subnet_CriticalAlertThresholdPercentage = $Config/Subnet_CriticalAlertThresholdPercentage$
[String] $Subnet_ExcludeInstances = '$Config/Subnet_ExcludeInstances$'

#############################################################################
# 1. Discover problematic QoE instances. ( Healthy instances are not discovered at all )
# 2. For each QoE instance, set values of Attributes with alerting context.
# ( These will be used at Analysis step )
#
#############################################################################
# Parameters
#
# [ ComputerName ] - Computer (FQDN) that Lync Monitoring Server is hosted on
# [ SourceId ] - Source ID for the discovery
# [ ManagedEntityId ] - Management Entity ID for the discovery
#
#############################################################################
#############################################################################

#LOGS &amp; EXCEPTIONS###########################################################
$exceptionOccured = $false;

$LOG_REG_KEY = "HKLM:\Software\Microsoft\Real-Time Communications\Health"
$EVENT_ERROR = 1
$EVENT_WARNING = 2
$EVENT_INFORMATION = 4
$EVENT_ID = 225
$EVENT_SCRIPT_NAME = "DiscoverAlertQoE.ps1"

$DISCOVERY_EXCEPTION_MESSAGE = "An exception occurred during {0}, Exception : {1}`n"
#############################################################################

#############################################################################
#CONSTANTS###################################################################
#############################################################################

# These values should match with definitions such as CATEGORY_AVConf
# in dev\server\lcs\server\QoE\inc\qoedefs.h

# Categories
$CAT_MediationServer="MediationServer"
$CAT_AVConf ="AVConf"
$CAT_GatewayMS = "GatewayMS"
$CAT_GatewayClient = "GatewayClient"
$CAT_Subnet = "Subnet"
$CAT_IntraUserSite = "IntraUserSite"
$CAT_InterUserSite = "InterUserSite"
$CAT_InterRegion = "InterRegion"

# Mapping from (CategoryId from SCOM sproc) to (Category Name)
$CATEGORY_NAME_MAP =
@{
"1"=$CAT_MediationServer;
"2"=$CAT_AVConf;
"3"=$CAT_GatewayMS;
"4"=$CAT_GatewayClient;
"5"=$CAT_Subnet;
"6"=$CAT_IntraUserSite;
"7"=$CAT_InterUserSite;
"8"=$CAT_InterRegion;
}

# Mapping from Category Name to string to show as category in alert description
$CATEGORY_DISPLAY_NAME_MAP =
@{
$CAT_MediationServer="Mediation Server and client endpoint leg";
$CAT_AVConf ="A/V Conferencing Server";
$CAT_GatewayMS ="Gateway and Mediation Server leg";
$CAT_GatewayClient ="Gateway (Mediation Server bypass)";
$CAT_Subnet ="Calls from or to a subnet";
$CAT_IntraUserSite ="Calls within a site";
$CAT_InterUserSite ="Calls between sites";
$CAT_InterRegion ="Calls between regions";
}

# Mapping from Category Name to Warning Threshold
$CATEGORY_WARNING_THRESHOLD_MAP =
@{
$CAT_MediationServer=$MediationServer_WarningAlertThresholdPercentage;
$CAT_AVConf =$AVConf_WarningAlertThresholdPercentage;
$CAT_GatewayMS =$GatewayMS_WarningAlertThresholdPercentage;
$CAT_GatewayClient =$GatewayClient_WarningAlertThresholdPercentage;
$CAT_Subnet =$Subnet_WarningAlertThresholdPercentage;
$CAT_IntraUserSite =$IntraUserSite_WarningAlertThresholdPercentage;
$CAT_InterUserSite =$InterUserSite_WarningAlertThresholdPercentage;
$CAT_InterRegion =$InterRegion_WarningAlertThresholdPercentage;
}

# Mapping from Category Name to Critical Threshold
$CATEGORY_CRITICAL_THRESHOLD_MAP =
@{
$CAT_MediationServer=$MediationServer_CriticalAlertThresholdPercentage;
$CAT_AVConf =$AVConf_CriticalAlertThresholdPercentage;
$CAT_GatewayMS =$GatewayMS_CriticalAlertThresholdPercentage;
$CAT_GatewayClient =$GatewayClient_CriticalAlertThresholdPercentage;
$CAT_Subnet =$Subnet_CriticalAlertThresholdPercentage;
$CAT_IntraUserSite =$IntraUserSite_CriticalAlertThresholdPercentage;
$CAT_InterUserSite =$InterUserSite_CriticalAlertThresholdPercentage;
$CAT_InterRegion =$InterRegion_CriticalAlertThresholdPercentage;
}

# Mapping from Category Name to Critical Threshold
$CATEGORY_EXCLUDE_INSTANCES_MAP =
@{
$CAT_MediationServer=$MediationServer_ExcludeInstances;
$CAT_AVConf =$AVConf_ExcludeInstances;
$CAT_GatewayMS =$GatewayMS_ExcludeInstances;
$CAT_GatewayClient =$GatewayClient_ExcludeInstances;
$CAT_Subnet =$Subnet_ExcludeInstances;
$CAT_IntraUserSite =$IntraUserSite_ExcludeInstances;
$CAT_InterUserSite =$InterUserSite_ExcludeInstances;
$CAT_InterRegion =$InterRegion_ExcludeInstances;
}

# Column ID for each column of the result set of QoeGetScomInstance sproc
# See dev\server\qoe\aggregator\sql\perfcountergeneration.sql for the resultset of QoeGetScomInstance sproc
$SPROC_COLUMN_ID_MAP =
@{
"CategoryId"=0;
"Name"=1;
"ReportParam1"=2;
"ReportParam2"=3;
"ReportParam3"=4;
"ReportParam4"=5;
"TotalAudioCalls"=6;
"TotalPoorAudioCalls"=7;
"Degradation"=8;
"Jitter"=9;
"PacketLoss"=10;
"RoundTripTime"=11;
"Concealed"=12;
"Stretched"=13;
"Compressed"=14;
"HealthyInstanceList"=15
}


# Registry for keeping discovery signature of each problematic instance
$MICROSOFT_REG_KEY_PATH="Software\Microsoft"
$RTC_REG_KEY_PATH="Real-Time Communications"
$MONSRV_REG_KEY_PATH=$MICROSOFT_REG_KEY_PATH + "\" + $RTC_REG_KEY_PATH
$SCOM_INST_REG_KEY="SCOM"

$AUDIO_COUNTER_ALIAS = "PoorAudioCallPercentage",
"DegradationAverage",
"Jitter",
"PacketLoss",
"RoundTripTime",
"Concealed",
"Stretched",
"Compressed"

$AUDIO_COUNTER_SYMBOL = ,"A" #Audio Quality "," represents an array with one element


$ALERT_GOOD = 0
$ALERT_WARN = 1
$ALERT_ERROR = 2

$PERCENT_DIGITS = 2

$HEALTHY_INSTANCE_GROUP_NAME = "Healthy Instances"

$EVENT_ERROR = 1
$EVENT_WARN = 2

# Values to use in case input validation fails
$E_MinutesToQuery = 120
$E_MinCallsAffected = 50
$E_MinUsersAffected = 2
$E_WarningAlertThresholdPercentage = 14
$E_CriticalAlertThresholdPercentage = 20

#############################################################################
#GOBAL VARIABLES / OBJECTS###################################################
#############################################################################
$MOMapi = New-Object -comObject "MOM.ScriptApi"
$REPORT_URL_PREFIX=$null
$gReg = $null
#############################################################################

#############################################################################
#QoE Monitoring type-specific configuration
#All fields should be initialized based on the server role type and used in readonly mode
#############################################################################
$Global:gConfig_NodeType = ""

$Global:gConfig_QoENodeTypeId = ""
$Global:gConfig_AlertingContextTypeId = ""
$Global:gConfig_QoEMetricTypeIds = new-object object[] 1
$Global:gConfig_QoEMetricTypeCount = 0

#############################################################################
#Helper Functions############################################################
#############################################################################

# Get CategoryId from Category Name
function GetCategoryId($CategoryName)
{
$CategoryId = $null
# Lookup mapping from category id to category name
foreach( $category in $CATEGORY_NAME_MAP.GetEnumerator() )
{
if ( $CategoryName -eq $category.Value )
{
# Return category id
$CategoryId = $category.Name
}
}

if (!$CategoryId)
{
throw ("Unsupported category name : {0}" -f $CategoryName)
}
$CategoryId
}



function EncodeUrl([string] $url)
{
[System.Web.HttpUtility]::UrlEncode($url)
}

# Read registry entry. return $null it does not exist
function ReadRegValue( $sRegKey, $sRegEntryName )
{
$subKey = $gReg.OpenSubKey( $sRegKey )
if ($subKey -eq $null)
{
$null
}
else
{
$subKey.GetValue($sRegEntryName)
}
}

# Read DWORD registry entry and return the value as long type
# Return nDefaultValue if the key or entry does not exist
function ReadRegDWORD( $sRegKey, $sRegEntryName, $nDefaultValue )
{
$regValue = ReadRegValue $sRegKey `
$sRegEntryName

if ($regValue -eq $null )
{
$nDefaultValue
}
else
{
[long] ( [string] $regValue )
}
}

# Read String registry entry and return the value as string type
# Return sDefaultValue if the key or entry does not exist
function ReadRegString( $sRegKey, $sRegEntryName, $sDefaultValue )
{
$regValue = ReadRegValue $sRegKey `
$sRegEntryName
if ($regValue -eq $null )
{
$sDefaultValue
}
else
{
[string] $regValue
}
}

# return a string with two digits.
# make it start with "0" if the input string contains only one digit
function TwoDigits( $sDigits )
{
$s = "0" + $sDigits
if ( $s.length -lt 2 )
{
"00"
}
else
{
$s.substring($s.length-2, 2)
}
}

# return a string that formats the input number with a specific number of digits beyond the decimal point
# Ex&gt; FormatNumber(10.1, 3) =&gt; 10.100
# FormatNumber(10.1, 5) =&gt; 10.10000
function FormatNumber($Number, $DigitsBeyondDecimalPoint)
{
$formatString = "{0:N"+$DigitsBeyondDecimalPoint+"}"
$formatString -f $Number
}

#############################################################################
# verbose input parameters
#############################################################################

TRACE("+++ SCRIPT PARAMETERS +++")
TRACE("SourceId="+$SourceId)
TRACE("ManagedEntityId="+$ManagedEntityId)
TRACE("ComputerName="+$ComputerName)
TRACE("SQLInstance="+$SQLInstance)
TRACE("MirrorSQLInstance="+$MirrorSQLInstance)
#TRACE("ReportingServiceHomePageUrl="+$ReportingServiceHomePageUrl)
TRACE("Default_MinutesToQuery="+$Default_MinutesToQuery)
TRACE("Default_MinCallsAffected="+$Default_MinCallsAffected)
TRACE("Default_MinUsersAffected="+$Default_MinUsersAffected)
TRACE("Default_WarningAlertThresholdPercentage="+$Default_WarningAlertThresholdPercentage)
TRACE("Default_CriticalAlertThresholdPercentage="+$Default_CriticalAlertThresholdPercentage)
TRACE("Default_IncludeWIFICalls="+$Default_IncludeWIFICalls)
TRACE("Default_IncludeVPNCalls="+$Default_IncludeVPNCalls)
TRACE("Default_IncludeExternalCalls="+$Default_IncludeExternalCalls)
TRACE("AVConf_WarningAlertThresholdPercentage="+$AVConf_WarningAlertThresholdPercentage)
TRACE("AVConf_CriticalAlertThresholdPercentage="+$AVConf_CriticalAlertThresholdPercentage)
TRACE("AVConf_ExcludeInstances="+$AVConf_ExcludeInstances)
TRACE("MediationServer_WarningAlertThresholdPercentage="+$MediationServer_WarningAlertThresholdPercentage)
TRACE("MediationServer_CriticalAlertThresholdPercentage="+$MediationServer_CriticalAlertThresholdPercentage)
TRACE("MediationServer_ExcludeInstances="+$MediationServer_ExcludeInstances)
TRACE("GatewayMS_WarningAlertThresholdPercentage="+$GatewayMS_WarningAlertThresholdPercentage)
TRACE("GatewayMS_CriticalAlertThresholdPercentage="+$GatewayMS_CriticalAlertThresholdPercentage)
TRACE("GatewayMS_ExcludeInstances="+$GatewayMS_ExcludeInstances)
TRACE("GatewayClient_WarningAlertThresholdPercentage="+$GatewayClient_WarningAlertThresholdPercentage)
TRACE("GatewayClient_CriticalAlertThresholdPercentage="+$GatewayClient_CriticalAlertThresholdPercentage)
TRACE("GatewayClient_ExcludeInstances="+$GatewayClient_ExcludeInstances)
TRACE("InterRegion_WarningAlertThresholdPercentage="+$InterRegion_WarningAlertThresholdPercentage)
TRACE("InterRegion_CriticalAlertThresholdPercentage="+$InterRegion_CriticalAlertThresholdPercentage)
TRACE("InterRegion_ExcludeInstances="+$InterRegion_ExcludeInstances)
TRACE("InterUserSite_WarningAlertThresholdPercentage="+$InterUserSite_WarningAlertThresholdPercentage)
TRACE("InterUserSite_CriticalAlertThresholdPercentage="+$InterUserSite_CriticalAlertThresholdPercentage)
TRACE("InterUserSite_ExcludeInstances="+$InterUserSite_ExcludeInstances)
TRACE("IntraUserSite_WarningAlertThresholdPercentage="+$IntraUserSite_WarningAlertThresholdPercentage)
TRACE("IntraUserSite_CriticalAlertThresholdPercentage="+$IntraUserSite_CriticalAlertThresholdPercentage)
TRACE("IntraUserSite_ExcludeInstances="+$IntraUserSite_ExcludeInstances)
TRACE("Subnet_WarningAlertThresholdPercentage="+$Subnet_WarningAlertThresholdPercentage)
TRACE("Subnet_CriticalAlertThresholdPercentage="+$Subnet_CriticalAlertThresholdPercentage)
TRACE("Subnet_ExcludeInstances="+$Subnet_ExcludeInstances)
TRACE("--- SCRIPT PARAMETERS ---")


#############################################################################
# Functions for Database Connections
#############################################################################

# Open a query context with database instance and alert setting xml string
function DB_OpenQueryContext($DbInst, $MirrorDbInst, $AlertSettingsXML, $QueryStartUtcTime, $QueryEndUtcTime ) {
# Setup connection string, query string
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Server="+$DbInst+";Database=QoeMetrics;Integrated Security=True"
if(-not ([System.String]::IsNullOrEmpty($MirrorDbInst)))
{
$SqlConnection.ConnectionString += ";Failover Partner="+$MirrorDbInst
}
$SqlCmd = $SqlConnection.CreateCommand()
$SqlCmd.CommandTimeout = 100000
TRACE( "Sproc Execution : exec QoeGetScomInstances @AlertSettingsXML='"+$AlertSettingsXML.Replace("`r`n", '')+"'")
$SqlCmd.CommandText = "exec QoeGetScomInstances @AlertSettingsXML=@XML_ARG, @StartTimeUtc=@QueryStartTimeUtc, @EndTimeUtc=@QueryEndTimeUtc"

# Bind the AlertSetting parameter as Xml type
$SqlCmd.Parameters.Add("@XML_ARG", [System.Data.SqlDbType]::NVarChar ).Value = $AlertSettingsXML
$SqlCmd.Parameters.Add("@QueryStartTimeUtc", [System.Data.SqlDbType]::DateTime ).Value = $QueryStartUtcTime
$SqlCmd.Parameters.Add("@QueryEndTimeUtc", [System.Data.SqlDbType]::DateTime ).Value = $QueryEndUtcTime

# Execute Command
$SqlConnection.Open() &gt; $null
$SqlDataReader = $SqlCmd.ExecuteReader()

# Encapsulate connection and reader to query context.
$QueryContext = @( [ref]$SqlConnection, [ref]$SqlDataReader )
$QueryContext
}

# Close connection and reader in the query context.
function DB_CloseQueryContext($QueryContext ) {
$QueryContext[1].Value.Close() # SqlDataReader
$QueryContext[0].Value.Close() # SqlConnection
}

# Get sql data reader from the query context.
function DB_GetDataReaderReference($QueryContext ) {
$RefSqlDataReader = $QueryContext[1]
$RefSqlDataReader
}

# Get column value by column name from a data reader
function Reader_GetColumn( [ref]$dataReader, $columnName)
{
$dataReader.Value[ $SPROC_COLUMN_ID_MAP[ $columnName ] ]
}

# Get column value of string type
# - Sprocs run full outer join on audio metrics
# - with read uncommitted isolation level.
# - Return empty string in case the value from database is null
function Reader_GetStringColumn( [ref]$dataReader, $columnName)
{
$value = Reader_GetColumn $dataReader `
$columnName
if ( (!$value) -or ($value -is [DbNull]) ) {
[string] ""
}
else
{
[string] $value
}
}

# Get column value of 64 bit int type
# - Sprocs run full outer join on audio metrics
# - with read uncommitted isolation level.
# - Return 0 in case the value from database is null
function Reader_GetInt64Column( [ref]$dataReader, $columnName)
{
$value = Reader_GetColumn $dataReader `
$columnName
if ( (!$value) -or ($value -is [DbNull]) ) {
[long] 0
}
else
{
[long] $value
}
}


# Get column value of 64 bit int type
# - Sprocs run full outer join on audio metrics
# - with read uncommitted isolation level.
# - Return 0 in case the value from database is null
function Reader_GetDoubleColumn( [ref]$dataReader, $columnName)
{
$value = Reader_GetColumn $dataReader `
$columnName
if ( (!$value) -or ($value -is [DbNull]) ) {
[double] 0
}
else
{
[double] $value
}
}

function Reader_GetCategoryId( [ref]$dataReader)
{
Reader_GetStringColumn $dataReader `
"CategoryId"
}

function Reader_GetName( [ref]$dataReader)
{
Reader_GetStringColumn $dataReader `
"Name"
}

function Reader_GetReportParam1( [ref]$dataReader)
{
Reader_GetStringColumn $dataReader `
"ReportParam1"
}

function Reader_GetReportParam2( [ref]$dataReader)
{
Reader_GetStringColumn $dataReader `
"ReportParam2"
}

function Reader_GetReportParam3( [ref]$dataReader)
{
Reader_GetStringColumn $dataReader `
"ReportParam3"
}

function Reader_GetReportParam4( [ref]$dataReader)
{
Reader_GetStringColumn $dataReader `
"ReportParam4"
}

function Reader_GetTotalAudioCalls([ref] $dataReader)
{
Reader_GetInt64Column $dataReader `
"TotalAudioCalls"
}

function Reader_GetTotalPoorAudioCalls([ref] $dataReader)
{
Reader_GetInt64Column $dataReader `
"TotalPoorAudioCalls"
}

function Reader_GetDegradation([ref] $dataReader)
{
Reader_GetDoubleColumn $dataReader `
"Degradation"
}

function Reader_GetJitter([ref] $dataReader)
{
Reader_GetDoubleColumn $dataReader `
"Jitter"
}

function Reader_GetPacketLoss([ref] $dataReader)
{
Reader_GetDoubleColumn $dataReader `
"PacketLoss"
}

function Reader_GetRoundTripTime([ref] $dataReader)
{
Reader_GetDoubleColumn $dataReader `
"RoundTripTime"
}

function Reader_GetConcealed([ref] $dataReader)
{
Reader_GetDoubleColumn $dataReader `
"Concealed"
}

function Reader_GetStretched([ref] $dataReader)
{
Reader_GetDoubleColumn $dataReader `
"Stretched"
}

function Reader_GetCompressed([ref] $dataReader)
{
Reader_GetDoubleColumn $dataReader `
"Compressed"
}

function Reader_GetHealthyInstanceList([ref] $dataReader)
{
Reader_GetStringColumn $dataReader `
"HealthyInstanceList"
}


#############################################################################
# Functions for getting valid alerting parameters
#############################################################################

function GetMinutesToQuery() {
if ( $Default_MinutesToQuery -lt 1 ) {
$E_MinutesToQuery
} else {
$Default_MinutesToQuery
}
}

function GetMinCallsAffected() {
if ( $Default_MinCallsAffected -lt 1 ) {
$E_MinCallsAffected
} else {
$Default_MinCallsAffected
}
}

function GetMinUsersAffected() {
if ( $Default_MinUsersAffected -lt 1 ) {
$E_MinUsersAffected
} else {
$Default_MinUsersAffected
}
}

function GetIncludeWIFICalls() {
if ( $Default_IncludeWIFICalls -eq "true") {
1
} else {
0
}
}

function GetIncludeVPNCalls() {
if ( $Default_IncludeVPNCalls -eq "true") {
1
} else {
0
}
}

function GetIncludeExternalCalls() {
if ( $Default_IncludeExternalCalls -eq "true") {
1
} else {
0
}
}

function IsValidThreshold( [double]$Warning, [double]$Critical ) {
if ( $Critical -gt 0 -and
$Critical -le 100 -and
$Warning -gt 0 -and
$Warning -le 100 -and
$Warning -lt $Critical ) {
$TRUE
} else {
$FALSE
}
}

function GetDefaultWarningThreshold() {
if ( ( IsValidThreshold $Default_WarningAlertThresholdPercentage `
$Default_CriticalAlertThresholdPercentage ) ) {
$Default_WarningAlertThresholdPercentage
} else {
$E_WarningAlertThresholdPercentage
}
}

function GetDefaultCriticalThreshold() {
if ( ( IsValidThreshold $Default_WarningAlertThresholdPercentage `
$Default_CriticalAlertThresholdPercentage ) ) {
$Default_CriticalAlertThresholdPercentage
} else {
$E_CriticalAlertThresholdPercentage
}
}


function GetCategoryWarningThreshold([double]$Category_Warning, [double]$Category_Critical) {
if ( ( IsValidThreshold $Category_Warning `
$Category_Critical ) ) {
$Category_Warning
} else {
GetDefaultWarningThreshold
}
}


function GetCategoryCriticalThreshold([double]$Category_Warning, [double]$Category_Critical) {
if ( ( IsValidThreshold $Category_Warning `
$Category_Critical ) ) {
$Category_Critical
} else {
GetDefaultCriticalThreshold
}
}


function GetCategoryThresholds($CategoryName, [ref]$Category_Warning, [ref]$Category_Critical) {
$CategoryName = $CATEGORY_NAME_MAP[$CategoryId]

$Category_Warning.Value =
GetCategoryWarningThreshold $CATEGORY_WARNING_THRESHOLD_MAP[$CategoryName] `
$CATEGORY_CRITICAL_THRESHOLD_MAP[$CategoryName]
$Category_Critical.Value =
GetCategoryCriticalThreshold $CATEGORY_WARNING_THRESHOLD_MAP[$CategoryName] `
$CATEGORY_CRITICAL_THRESHOLD_MAP[$CategoryName]
}

#############################################################################
# Functions for parsing list of excluded instances separated by comma
#############################################################################
# Input :
# $InstanceList - list of instance names separated by comma
# Output :
# an hashtable containing each instance name as a key
#
# The format of InstanceList :
# It is standard CSV format used by excel
# (1) Instance names are separated by comma
# a,b
# =&gt; a
# b
# (2) In case comma(,) is included in the instance name, enclose it with quote(")
# "a,b", c
# =&gt; a,b
# c
# (3) In case quote(") is included in the instance name, write quote once more
# """a,b""",c
# =&gt; "a,b"
# c
#
# Implementation Details :
# Let's use ConvertFrom-CSV PS Cmdlet for parsing the input string.
# There is no need to reinvent the wheel. :)
# ConvertFrom-CSV requires at lease 1 header and 1 row.
# So we simply use the comma separated instance names both for header and row
function GetInstanceNameHash( $InstanceList ) {
$InstanceNameHash = @{}

if ( $InstanceList ) {

# Add as a row in CSV content
$csvContent = $InstanceList

$csvObject = $null

# crate header with 1000 columns. ( c1, c2, c3, ... , c1000 )
# 1000 columns are enough, because the maximum length of string SCOM Console can handle is 4000.
# (The length of an instance name should be greater than 4, so 1000 instances are enough)
$i = 1
$header = @()
while($i -le 1000) {
$header += ("c" + $i)
$i ++
}

# Create CSV object based on the content
$csvObject = $csvContent | ConvertFrom-Csv -Header $header

if ($csvObject -eq $null) {
# In case the format of the string is wrong :
# BUGBUG - raise an internal alert in SCOM
} else {
foreach ($csvColumn in (get-member -inputobject $csvObject | where {$_.MemberType -eq "NoteProperty"}) ) {
$InstanceName = $csvObject.($csvColumn.Name)
if ($InstanceName -ne $null) {
# escape single quote(') to avoide SQL injection
$InstanceName = $InstanceName.replace("'","''")
# Add to hash table only if the instance name does not exist.
if ( ! $InstanceNameHash.ContainsKey( $InstanceName ) )
{
# Add to Hash (Key : Instance Name, Value : null (not used))
$InstanceNameHash.Add( $InstanceName, $null )
}
}
}
}
}
$InstanceNameHash
}

#############################################################################
# Functions for getting alert setting XML / Parsing report home page URL
#############################################################################

# Get a list of CategoryThresholds
function GetCategoryThresholdXML()
{
foreach( $Category in $CATEGORY_NAME_MAP.GetEnumerator() )
{
# Category Id in QoE database
$CategoryId = $Category.Name
# Category name
$CategoryName = $Category.Value
$CategoryWarning = [double] -1.0
$CategoryCritical = [double] -1.0

GetCategoryThresholds $CategoryName `
([ref]$CategoryWarning) `
([ref]$CategoryCritical)
"
&lt;CategoryThreshold
Category=`""+$categoryName+"`"
CategoryId=`""+$categoryId+"`"
WarningThreshold=`""+$CategoryWarning+"`"
CriticalThreshold=`""+$CategoryCritical+"`"
/&gt;
"
}
}

# Get a list of CategoryThresholds
function GetExcludeInstancesXML()
{
foreach( $Category in $CATEGORY_NAME_MAP.GetEnumerator() )
{
$CategoryId = $Category.Name
# Category name
$CategoryName = $Category.Value
$InstanceListSeparatedByComma = $CATEGORY_EXCLUDE_INSTANCES_MAP[$CategoryName]
foreach ( $InstanceName in (GetInstanceNameHash $InstanceListSeparatedByComma).Keys ) {
TRACE("Read suppressed instance(Category="+$CategoryName+";InstanceName="+$InstanceName+")")
"
&lt;ExcludeInstance
Category=`""+$CategoryName+"`"
CategoryId=`""+$CategoryId+"`"
InstanceName=`""+$InstanceName+"`"
/&gt;
"
}
}
}


# Get the reporting service base URL which will become the prefix of troubleshooting URLs
function GetReportingServiceBaseUrl()
{
$BaseUrl = $ReportingServiceHomePageUrl
if($BaseUrl)
{
$url = $null
if([System.Uri]::TryCreate($BaseUrl, [System.UriKind]::RelativeOrAbsolute, [ref]$url))
{
if($url -ne $null -and $url.Segments.Length -gt 2)
{
$BaseUrl = $url.Scheme + "://" + $url.Authority + $url.Segments[0] + $url.Segments[1]

if($BaseUrl[$BaseUrl.Length - 1] -eq '/')
{
$BaseUrl = $BaseUrl.Remove($BaseUrl.Length - 1)
}
}
}
}

$BaseUrl
}



############################
# Get QoE alert settings.
# Parameters
# - [Out] AlertSettingXML - XML string that holds QoE alert settings,
# which will be passed to the sproc that queries QoE database
function GetAlertSettings([ref] $AlertSettingXML ) {
# The XML string that will be passed to sproc that queries QoE database
$AlertSettingXML.Value =
"
&lt;AlertSetting&gt;
&lt;Settings
MinCallsAffected=`""+(GetMinCallsAffected)+"`"
MinUsersAffected=`""+(GetMinUsersAffected)+"`"
IncludeWIFICalls=`""+(GetIncludeWIFICalls)+"`"
IncludeVPNCalls=`""+(GetIncludeVPNCalls)+"`"
IncludeExternalCalls=`""+(GetIncludeExternalCalls)+"`"
/&gt;
&lt;CategoryThresholds&gt;
"+(GetCategoryThresholdXML)+"
&lt;/CategoryThresholds&gt;
&lt;ExcludeInstances&gt;
"+(GetExcludeInstancesXML)+"
&lt;/ExcludeInstances&gt;
&lt;/AlertSetting&gt;
"
}



#############################################################################
# Methods for MonitoringNodeConfig
#############################################################################
function MNC_Initialize($MonitoringNodeType)
{
$Global:gConfig_NodeType = $MonitoringNodeType

#TRACE("In MNC_Initialize="+$MonitoringNodeType.ToUpper())

switch( $MonitoringNodeType.ToUpper() )
{
"AVConf"
{
# Variables used for the discovery
$Global:gConfig_QoENodeTypeId = "$MPElement[Name='Microsoft.LS.2013.QoE.MonitoringNode.AVConf']$"
$Global:gConfig_AlertingContextTypeId = "$MPElement[Name='Microsoft.LS.2013.QoE.AlertingContext.AVConf']$"
$Global:gConfig_QoEMetricTypeIds[0] = "$MPElement[Name='Microsoft.LS.2013.QoE.Metric.AudioQuality.AVConf']$"
$Global:gConfig_QoEMetricTypeCount = 1
$TRUE
}

"MediationServer"
{
# Variables used for the discovery
$Global:gConfig_QoENodeTypeId = "$MPElement[Name='Microsoft.LS.2013.QoE.MonitoringNode.MediationServer']$"
$Global:gConfig_AlertingContextTypeId = "$MPElement[Name='Microsoft.LS.2013.QoE.AlertingContext.MediationServer']$"

$Global:gConfig_QoEMetricTypeIds[0] = "$MPElement[Name='Microsoft.LS.2013.QoE.Metric.AudioQuality.MediationServer']$"
$Global:gConfig_QoEMetricTypeCount = 1
$TRUE
}

"GatewayMS"
{
# Variables used for the discovery
$Global:gConfig_QoENodeTypeId = "$MPElement[Name='Microsoft.LS.2013.QoE.MonitoringNode.GatewayMS']$"
$Global:gConfig_AlertingContextTypeId = "$MPElement[Name='Microsoft.LS.2013.QoE.AlertingContext.GatewayMS']$"

$Global:gConfig_QoEMetricTypeIds[0] = "$MPElement[Name='Microsoft.LS.2013.QoE.Metric.AudioQuality.GatewayMS']$"
$Global:gConfig_QoEMetricTypeCount = 1
$TRUE
}

"GatewayClient"
{
# Variables used for the discovery
$Global:gConfig_QoENodeTypeId = "$MPElement[Name='Microsoft.LS.2013.QoE.MonitoringNode.GatewayClient']$"
$Global:gConfig_AlertingContextTypeId = "$MPElement[Name='Microsoft.LS.2013.QoE.AlertingContext.GatewayClient']$"

$Global:gConfig_QoEMetricTypeIds[0] = "$MPElement[Name='Microsoft.LS.2013.QoE.Metric.AudioQuality.GatewayClient']$"
$Global:gConfig_QoEMetricTypeCount = 1
$TRUE
}

"Subnet"
{
# Variables used for the discovery
$Global:gConfig_QoENodeTypeId = "$MPElement[Name='Microsoft.LS.2013.QoE.MonitoringNode.Subnet']$"
$Global:gConfig_AlertingContextTypeId = "$MPElement[Name='Microsoft.LS.2013.QoE.AlertingContext.Subnet']$"

$Global:gConfig_QoEMetricTypeIds[0] = "$MPElement[Name='Microsoft.LS.2013.QoE.Metric.AudioQuality.Subnet']$"
$Global:gConfig_QoEMetricTypeCount = 1
$TRUE
}

"IntraUserSite"
{
# Variables used for the discovery
$Global:gConfig_QoENodeTypeId = "$MPElement[Name='Microsoft.LS.2013.QoE.MonitoringNode.IntraUserSite']$"
$Global:gConfig_AlertingContextTypeId = "$MPElement[Name='Microsoft.LS.2013.QoE.AlertingContext.IntraUserSite']$"

$Global:gConfig_QoEMetricTypeIds[0] = "$MPElement[Name='Microsoft.LS.2013.QoE.Metric.AudioQuality.IntraUserSite']$"
$Global:gConfig_QoEMetricTypeCount = 1
$TRUE
}
"InterUserSite"
{
# Variables used for the discovery
$Global:gConfig_QoENodeTypeId = "$MPElement[Name='Microsoft.LS.2013.QoE.MonitoringNode.InterUserSite']$"
$Global:gConfig_AlertingContextTypeId = "$MPElement[Name='Microsoft.LS.2013.QoE.AlertingContext.InterUserSite']$"

$Global:gConfig_QoEMetricTypeIds[0] = "$MPElement[Name='Microsoft.LS.2013.QoE.Metric.AudioQuality.InterUserSite']$"
$Global:gConfig_QoEMetricTypeCount = 1
$TRUE
}

"InterRegion"
{
# Variables used for the discovery
$Global:gConfig_QoENodeTypeId = "$MPElement[Name='Microsoft.LS.2013.QoE.MonitoringNode.InterRegion']$"
$Global:gConfig_AlertingContextTypeId = "$MPElement[Name='Microsoft.LS.2013.QoE.AlertingContext.InterRegion']$"

$Global:gConfig_QoEMetricTypeIds[0] = "$MPElement[Name='Microsoft.LS.2013.QoE.Metric.AudioQuality.InterRegion']$"
$Global:gConfig_QoEMetricTypeCount = 1
$TRUE
}

default
{
$FALSE
}
}
}

#############################################################################
# Methods for generating reporting URL
#############################################################################

# Format Example : 1997-07-16T19:20:30
function FormatUTCTime( $d )
{
$d.Year.ToString() + "-" + (TwoDigits $d.Month) + "-" + (TwoDigits $d.Day) + "T" +
(TwoDigits $d.Hour) + ":" + (TwoDigits $d.Minute) + ":" + (TwoDigits $d.Second)
}

# Add properties related to reporting URL to the hash table
function AddReportUrlProps( [ref] $RefDataReader, $QueryStartUTCTime, $QueryEndUTCTime, $outHash )
{
$outHash.Add("ReportUrlPeriodStart", (EncodeUrl (FormatUTCTime $QueryStartUTCTime) ) )
$outHash.Add("ReportUrlPeriodEnd", (EncodeUrl (FormatUTCTime $QueryEndUTCTime) ) )

# 1 means external calls are not included
$ReportUrlParamIncludeExternalCalls="1"
# 0 means calls via VPN are not included
$ReportUrlParamIncludeVPNCalls="0"
# 0 means calls via WIFI are not included
$ReportUrlParamIncludeWIFICalls="0"

if ( $Default_IncludeExternalCalls -eq "true" ) {
# 2 means external calls are included
$ReportUrlParamIncludeExternalCalls = "2"
}

if ( $Default_IncludeVPNCalls -eq "true" ) {
# 2 means calls via VPN are not included
$ReportUrlParamIncludeVPNCalls="2"
}

if ( $Default_IncludeWIFICalls -eq "true" ) {
# 2 means calls via WIFI are not included
$ReportUrlParamIncludeWIFICalls="2"
}

$outHash.Add("ReportUrlParamIncludeExternalCalls", (EncodeUrl $ReportUrlParamIncludeExternalCalls) )
$outHash.Add("ReportUrlParamIncludeVPNCalls", (EncodeUrl $ReportUrlParamIncludeVPNCalls) )
$outHash.Add("ReportUrlParamIncludeWIFICalls", (EncodeUrl $ReportUrlParamIncludeWIFICalls ) )

$ReportParam1 = Reader_GetReportParam1 $RefDataReader
$ReportParam2 = Reader_GetReportParam2 $RefDataReader
$ReportParam3 = Reader_GetReportParam3 $RefDataReader
$ReportParam4 = Reader_GetReportParam4 $RefDataReader

$outHash.Add("ReportUrlParam1", (EncodeUrl $ReportParam1) )
$outHash.Add("ReportUrlParam2", (EncodeUrl $ReportParam2) )
$outHash.Add("ReportUrlParam3", (EncodeUrl $ReportParam3) )
$outHash.Add("ReportUrlParam4", (EncodeUrl $ReportParam4) )


$ReportServiceUrl = ""
if ( $REPORT_URL_PREFIX )
{
$ReportServiceUrl = $REPORT_URL_PREFIX
}
else
{
$ReportServiceUrl = "http://(null)/ReportServer"
}

$outHash.Add("ReportServiceUrl", $ReportServiceUrl )
}

# Add AlertContent property to the hash table
# These will be printed in the alert description
function AddAlertContentProp( $QoEInstanceName, $hash )
{
$CategoryDisplayName = $CATEGORY_DISPLAY_NAME_MAP[ $Global:gConfig_NodeType ]

$WarningThreshold = [double] -1.0
$CriticalThreshold = [double] -1.0

GetCategoryThresholds $Global:gConfig_NodeType `
([ref]$WarningThreshold) `
([ref]$CriticalThreshold)

$ReportUrlMessage = ""
if ( $REPORT_URL_PREFIX )
{
$ReportUrlMessage = "For details about this alert, open the following Monitoring Server report in a browser:"
}
else
{
$ReportUrlMessage =
"Reporting URL is not available because Monitoring Server Reports are not deployed." +
"`nTo see reports that can assist with troubleshooting, deploy Monitoring Server Reports."
}

$AlertContentFormat =
"
Following are the details for this media quality alert:

Media quality alert for: {0} `"{1}`"
Total calls = {2}
Poor quality calls = {3}
Poor quality call percentage (%) = {4}

Following are the average values for call classification metrics:

Average network degradation = {5}
Average jitter (ms) = {6}
Average packet loss (%) = {7}
Average round trip time (ms) = {8}
Average concealed metric (%) = {9}
Average stretched metric (%) = {10}
Average compressed metric (%) = {11}

Following are the alert parameters:

Calls over VPN included = {12}
Calls over WIFI included = {13}
Calls from external users included = {14}
Media quality is checked for the past (min) = {15}
A critical alert is raised when poor quality calls percentage is greater than (%) = {16}
A warning alert is raised when poor quality calls percentage is greater than (%) = {17}
Minimum number of calls affected to raise an alert = {18}
Minimum number of users affected to raise an alert = {19}

{20}
"
$AlertContent =
$AlertContentFormat `
-f $CategoryDisplayName,
$QoEInstanceName,
$hash["AudioCalls"],
$hash["PoorAudioCalls"],
$hash["PoorAudioCallPercentage"],
$hash["DegradationAverage"],
$hash["Jitter"],
$hash["PacketLoss"],
$hash["RoundTripTime"],
$hash["Concealed"],
$hash["Stretched"],
$hash["Compressed"],
$Default_IncludeVPNCalls,
$Default_IncludeWIFICalls,
$Default_IncludeExternalCalls,
$Default_MinutesToQuery,
$CriticalThreshold,
$WarningThreshold,
$Default_MinCallsAffected,
$Default_MinUsersAffected,
$ReportUrlMessage

$hash.Add("AlertContent", $AlertContent )

$hash.Add("IncludeVPNCalls", $Default_IncludeVPNCalls)
$hash.Add("IncludeWIFICalls", $Default_IncludeWIFICalls)
$hash.Add("IncludeExternalCalls", $Default_IncludeExternalCalls)
$hash.Add("MinutesToQuery", $Default_MinutesToQuery)
$hash.Add("CriticalThreshold", $CriticalThreshold)
$hash.Add("WarningThreshold", $WarningThreshold)
$hash.Add("MinCallsAffected", $Default_MinCallsAffected)
$hash.Add("MinUsersAffected", $Default_MinUsersAffected)
}


###############################################################################
# Methods for discovering problematic QoE instances and analyzing health states
###############################################################################

# Calculate percentage of bad calls
function CalculatePercent($BadCalls, $TotalCalls)
{
if ($TotalCalls -le 0) {
0
}
else
{
$BadCalls * 100 / $TotalCalls
}
}

# Get alerting context for a healthy instance
function GetAlertingContextForHealthyInstance($outAttrHash)
{
# Alert summary information such as N0C0D0P0
$AlertSummary = ""
foreach ($s in $AUDIO_COUNTER_SYMBOL) {
$AlertSummary = $AlertSummary + $s + $ALERT_GOOD #Healthy
}

$outAttrHash.Add("Alert", $AlertSummary)
}

# Get alerting context for a healthy instance group
function GetAlertingContextForHealthyInstanceGroup([ref] $dataReader, $outAttrHash)
{
# Alert summary information such as N0C0D0P0
GetAlertingContextForHealthyInstance $outAttrHash

# List of names of healthy instances
# Ex&gt; inst1, inst2, ..., inst100, and 237 more.
$outAttrHash.Add( "HealthyInstanceList",
( Reader_GetHealthyInstanceList $dataReader ) )
}




# Get alerting context for a problematic instance
function GetAlertingContextForProblematicInstance(
[ref] $dataReader,
$MinimumCallVolume, $ErrorLevel, $WarningLevel,
$QueryStartUTCTime, $QueryEndUTCTime,
$outAttrHash)
{
$bHealthy = $TRUE

$InstanceName = (Reader_GetName $dataReader)

$AudioCallVolume = Reader_GetTotalAudioCalls $dataReader
$PoorAudioCallVolume = Reader_GetTotalPoorAudioCalls $dataReader

$AudioCounters= (CalculatePercent $PoorAudioCallVolume $AudioCallVolume),
(Reader_GetDegradation $dataReader),
(Reader_GetJitter $dataReader),
(Reader_GetPacketLoss $dataReader),
(Reader_GetRoundTripTime $dataReader),
(Reader_GetConcealed $dataReader),
(Reader_GetStretched $dataReader),
(Reader_GetCompressed $dataReader)

$bAlert = $FALSE
for ($i = 0; $i -lt $AUDIO_COUNTER_SYMBOL.Count; $i++ )
{
if ($AudioCallVolume -ge $MinimumCallVolume )
{
$sPercent = $AudioCounters[$i]

if ( $sPercent -lt $WarningLevel ) {
$sResult = $sResult + $AUDIO_COUNTER_SYMBOL[$i] + $ALERT_GOOD #Healthy
} elseif ( $sPercent -lt $ErrorLevel ) {
$bAlert = $TRUE
$sResult = $sResult + $AUDIO_COUNTER_SYMBOL[$i] + $ALERT_WARN #Warn
} else {
$bAlert = $TRUE
$sResult = $sResult + $AUDIO_COUNTER_SYMBOL[$i] + $ALERT_ERROR #Error
}
} else {
# Show instances as Healthy when the number of calls are below the min threshold.
#
# We cannot measure the Health State when the number of calls is below the threshold,
# because it could be incorrect dew to small number of samples.
# We can say this state as Unknown.
#
# We show the Unknown state as Healthy for passive monitoring like QoE Monitoring.
$sResult = $sResult + $AUDIO_COUNTER_SYMBOL[$i] + $ALERT_GOOD #Healthy
}
}

if ( $bAlert )
{
$bHealthy = $FALSE
$AudioCallVolumeValue = $AudioCallVolume
$PoorAudioCallVolumeValue = $PoorAudioCallVolume
}
else
{
# Need to set as empty string in case the instance is healthy.
# Otherwise the instance will have previous value for this attribute
$AudioCallVolumeValue = ""
$PoorAudioCallVolumeValue = ""
}

#Collect number of calls for Healthy/Warning/Critical
$outAttrHash.Add( "AudioCalls", $AudioCallVolumeValue )
$outAttrHash.Add( "PoorAudioCalls", $PoorAudioCallVolumeValue )

#Collect values of related audio perf counters which will be used to generate elaborate alert description
for ($i = 0; $i -lt $AudioCounters.Count; $i ++ )
{
if ( $bAlert )
{
$sPercent = FormatNumber $AudioCounters[$i] `
$PERCENT_DIGITS
}
else
{
# Need to set as empty string in case the instance is healthy.
# Otherwise the instance will have previous value for this attribute
$sPercent = ""
}
$outAttrHash.Add( $AUDIO_COUNTER_ALIAS[$i], $sPercent )
}

if ( ! $bHealthy )
{
#Generate property values related to the troubleshooting report URL for problematic instances ( = at least one Error/Warning QoE Metric)
AddReportUrlProps $RefDataReader `
$QueryStartUTCTime `
$QueryEndUTCTime `
$outAttrHash
AddAlertContentProp $InstanceName `
$outAttrHash
}

$outAttrHash.Add("Alert", $sResult)
}


# For a QoE instance, Add properties with alerting contexts
# $attrHash hold (Name, Value) pair for attributes which has alerting contexts
function AddAlertingContextProps( $QoEInstance, $sInstanceName, $attrHash )
{
$NotFoundAttrs = ""

foreach ($attr in $attrHash.GetEnumerator()) {

$attrName = $attr.Name
$attrValue = $attr.Value

# Common to Healthy and Problematic Instances
$commonAttrIdentityMap = @{
"HealthyInstanceList" = "$MPElement[Name='Microsoft.LS.2013.QoE.MonitoringNode']/HealthyInstanceList$"
}

# Only for Problematic Instances
$attrIdentityMapForProblematicInstances = @{
# Audio Attrs
"AudioCalls" = "$MPElement[Name='Microsoft.LS.2013.QoE.MonitoringNodeWithAudio']/AudioCalls$";
"PoorAudioCalls" = "$MPElement[Name='Microsoft.LS.2013.QoE.MonitoringNodeWithAudio']/PoorAudioCalls$";
"PoorAudioCallPercentage" = "$MPElement[Name='Microsoft.LS.2013.QoE.MonitoringNodeWithAudio']/PoorAudioCallPercentage$";
"DegradationAverage" = "$MPElement[Name='Microsoft.LS.2013.QoE.MonitoringNodeWithAudio']/DegradationAverage$";
"Jitter" = "$MPElement[Name='Microsoft.LS.2013.QoE.MonitoringNodeWithAudio']/Jitter$";
"PacketLoss" = "$MPElement[Name='Microsoft.LS.2013.QoE.MonitoringNodeWithAudio']/PacketLoss$";
"RoundTripTime" = "$MPElement[Name='Microsoft.LS.2013.QoE.MonitoringNodeWithAudio']/RoundTripTime$";
"Concealed" = "$MPElement[Name='Microsoft.LS.2013.QoE.MonitoringNodeWithAudio']/Concealed$";
"Stretched" = "$MPElement[Name='Microsoft.LS.2013.QoE.MonitoringNodeWithAudio']/Stretched$";
"Compressed" = "$MPElement[Name='Microsoft.LS.2013.QoE.MonitoringNodeWithAudio']/Compressed$";
"IncludeVPNCalls" = "$MPElement[Name='Microsoft.LS.2013.QoE.MonitoringNodeWithAudio']/IncludeVPNCalls$";
"IncludeWIFICalls"= "$MPElement[Name='Microsoft.LS.2013.QoE.MonitoringNodeWithAudio']/IncludeWIFICalls$";
"IncludeExternalCalls" = "$MPElement[Name='Microsoft.LS.2013.QoE.MonitoringNodeWithAudio']/IncludeExternalCalls$";
"MinutesToQuery" = "$MPElement[Name='Microsoft.LS.2013.QoE.MonitoringNodeWithAudio']/MinutesToQuery$";
"CriticalThreshold"= "$MPElement[Name='Microsoft.LS.2013.QoE.MonitoringNodeWithAudio']/CriticalThreshold$";
"WarningThreshold" = "$MPElement[Name='Microsoft.LS.2013.QoE.MonitoringNodeWithAudio']/WarningThreshold$";
"MinCallsAffected" = "$MPElement[Name='Microsoft.LS.2013.QoE.MonitoringNodeWithAudio']/MinCallsAffected$";
"MinUsersAffected" = "$MPElement[Name='Microsoft.LS.2013.QoE.MonitoringNodeWithAudio']/MinUsersAffected$";
}

if ( $sInstanceName -eq $HEALTHY_INSTANCE_GROUP_NAME )
{
$attrIdentiyMap = $commonAttrIdentityMap
}
else
{
$attrIdentiyMap = $commonAttrIdentityMap
$attrIdentiyMap += $attrIdentityMapForProblematicInstances
}

if ( $attrIdentiyMap.ContainsKey( $attrName ) )
{
$attrIdentity = $attrIdentiyMap[ $attrName ]
$QoEInstance.AddProperty($attrIdentity, $attrValue)
#TRACE ("GUID:Attr:"+$attrName+"="+$attrIdentity+"`n")
}
}
}


# For a QoE Metric instance, Add properties related to alerting parameters in the alert description
# $attrHash hold (Name, Value) pair for attributes which has alerting contexts
function AddAlertingParamProps( $MetricInstance, $attrHash )
{
$NotFoundAttrs = ""

foreach ($attr in $attrHash.GetEnumerator()) {

$attrName = $attr.Name
$attrValue = $attr.Value

# Only for Problematic Instances
$attrIdentiyMap = @{
# Audio Attrs
"Alert" = "$MPElement[Name='Microsoft.LS.2013.QoE.AlertingContext']/Alert$";
"AlertContent" = "$MPElement[Name='Microsoft.LS.2013.QoE.AlertingContext']/AlertContent$";
# Reporting URL Attrs
"ReportServiceUrl" = "$MPElement[Name='Microsoft.LS.2013.QoE.AlertingContext']/ReportServiceUrl$";
"ReportUrlParamIncludeExternalCalls" = "$MPElement[Name='Microsoft.LS.2013.QoE.AlertingContext']/ReportUrlParamIncludeExternalCalls$";
"ReportUrlParamIncludeVPNCalls" = "$MPElement[Name='Microsoft.LS.2013.QoE.AlertingContext']/ReportUrlParamIncludeVPNCalls$";
"ReportUrlParamIncludeWIFICalls" = "$MPElement[Name='Microsoft.LS.2013.QoE.AlertingContext']/ReportUrlParamIncludeWIFICalls$";
"ReportUrlPeriodStart" = "$MPElement[Name='Microsoft.LS.2013.QoE.AlertingContext']/ReportUrlPeriodStart$";
"ReportUrlPeriodEnd" = "$MPElement[Name='Microsoft.LS.2013.QoE.AlertingContext']/ReportUrlPeriodEnd$";
"ReportUrlParam1" = "$MPElement[Name='Microsoft.LS.2013.QoE.AlertingContext']/ReportUrlParam1$";
"ReportUrlParam2" = "$MPElement[Name='Microsoft.LS.2013.QoE.AlertingContext']/ReportUrlParam2$";
"ReportUrlParam3" = "$MPElement[Name='Microsoft.LS.2013.QoE.AlertingContext']/ReportUrlParam3$";
"ReportUrlParam4" = "$MPElement[Name='Microsoft.LS.2013.QoE.AlertingContext']/ReportUrlParam4$"
}

if ( $attrIdentiyMap.ContainsKey( $attrName ) )
{
$attrIdentity = $attrIdentiyMap[ $attrName ]
$MetricInstance.AddProperty($attrIdentity, $attrValue)
#TRACE ("GUID:Attr:"+$attrName+"="+$attrIdentity+"`n")
}
}
}


# Add a QoE instance
# $attrHash hold (Name, Value) pair for attributes which has alerting contexts
function AddInstance($sInstanceName, $attrHash)
{
#Add the instance of the QoE alerting context
$AlertingContext = $DiscoveryData.CreateClassInstance($Global:gConfig_AlertingContextTypeId)
#TRACE ("GUID:AlertingContext="+$Global:gConfig_AlertingContextTypeId+"`n")

$AlertingContext.AddProperty("$MPElement[Name='Windows!Microsoft.Windows.Computer']/PrincipalName$", $ComputerName)
$AlertingContext.AddProperty("$MPElement[Name='Microsoft.LS.2013.QoE.AlertingContext']/InstanceName$", $sInstanceName)
$AlertingContext.AddProperty("$MPElement[Name='System!System.Entity']/DisplayName$", "Alerting Context")

AddAlertingParamProps $AlertingContext `
$attrHash

$DiscoveryData.AddInstance($AlertingContext)


#Add the instance of the QoE monitoring node
$QoEInstance = $DiscoveryData.CreateClassInstance($Global:gConfig_QoENodeTypeId)
#TRACE ("GUID:QoE Node="+$Global:gConfig_QoENodeTypeId+"`n")

$QoEInstance.AddProperty("$MPElement[Name='Windows!Microsoft.Windows.Computer']/PrincipalName$", $ComputerName)
$QoEInstance.AddProperty("$MPElement[Name='Microsoft.LS.2013.QoE.AlertingContext']/InstanceName$", $sInstanceName)
$QoEInstance.AddProperty("$MPElement[Name='Microsoft.LS.2013.QoE.MonitoringNode']/InstanceName$", $sInstanceName)

AddAlertingContextProps $QoEInstance `
$sInstanceName `
$attrHash

$DiscoveryData.AddInstance($QoEInstance)

#Add instances of the QoE metrics hosted by this node
for ($i = 0; $i -lt $Global:gConfig_QoEMetricTypeCount; $i++)
{
$oQoEMetric = $DiscoveryData.CreateClassInstance($Global:gConfig_QoEMetricTypeIds[$i])
#TRACE ("GUID:QoE Metric="+$Global:gConfig_QoEMetricTypeIds[$i]+"`n")

$oQoEMetric.AddProperty("$MPElement[Name='Windows!Microsoft.Windows.Computer']/PrincipalName$", $ComputerName)
$oQoEMetric.AddProperty("$MPElement[Name='Microsoft.LS.2013.QoE.AlertingContext']/InstanceName$", $sInstanceName)
$oQoEMetric.AddProperty("$MPElement[Name='Microsoft.LS.2013.QoE.MonitoringNode']/InstanceName$", $sInstanceName)

$DiscoveryData.AddInstance($oQoEMetric)
}
}

function InitializeWithCategory( $CategoryId )
{
if ($CATEGORY_NAME_MAP.ContainsKey( $CategoryId ))
{
$MonitoringNodeType = $CATEGORY_NAME_MAP[$CategoryId]

MNC_Initialize $MonitoringNodeType
}
else
{
$FALSE
}
}

# Open registry key in R/W mode. Quit the script if it does not exist.
function OpenRegKey($RegKeyPath)
{
$subKey = $gReg.OpenSubKey( $RegKeyPath, $true )
if ($subKey -eq $null)
{
throw ("Registry key path is not created : {0}" -f $RegKeyPath)
}
$subKey
}

# Create the registry key if it does not exist
function EnsureRegKey($RegKeyPath, $RegKeyName)
{
$key = OpenRegKey $RegKeyPath
try {
$key.CreateSubKey( $RegKeyName ).Close()
} finally {
$key.Close()
}
}

# Update the registry entry w/ the specified value
# Create the registry entry if it does not exist
function EnsureRegStringEntry($RegKeyPath, $RegEntryName, $RegEntryValue)
{
$key = OpenRegKey $RegKeyPath
try {
$key.SetValue( $RegEntryName, $RegEntryValue )
} finally {
$key.Close()
}
}


# Get Registry key path for the discovery signature of a QoE category
function GetRegKeyPath($CategoryId)
{
$MONSRV_REG_KEY_PATH + "\" + $SCOM_INST_REG_KEY + "\" + $CategoryId
}


# Discover an instance as Healthy
function DiscoverHealthyInstance($CategoryId, $InstanceName)
{
$attrHash = $null
$attrHash = @{}

# Alert summary information such as N0C0D0P0
GetAlertingContextForHealthyInstance $attrHash

AddInstance $InstanceName `
$attrHash
}

# Update discovery signature for an instance in Reg
#
# Key : HKCU:\Software\Microsoft\Real-Time Communications\Scom\$CategoryId
# Entity
# Name : $InstanceName
# Type : String
# Value : $DiscoverySignature
#
# For the details, see the comment inside of DiscoverInstances about BUG-209236
function UpdateDiscoverySignature( $CategoryId, $InstanceName, $DiscoverySignature)
{
EnsureRegKey $MONSRV_REG_KEY_PATH `
$SCOM_INST_REG_KEY

EnsureRegKey ($MONSRV_REG_KEY_PATH + "\" + $SCOM_INST_REG_KEY) `
$CategoryId

$RegKeyPath = GetRegKeyPath( $CategoryId )

EnsureRegStringEntry $RegKeyPath `
$InstanceName `
$DiscoverySignature
}

# Iterate each discovered instance in registry
# Discover as the instance healthy and remove it from the registry if the
# discovery signature does not match with the one of the current discovery.
#
# For the details, see the comment inside of DiscoverInstances about BUG-209236
function DiscoverHealthyInstanceIfNotMatchDiscoverySignature($DiscoverySignature)
{
foreach( $category in $CATEGORY_NAME_MAP.GetEnumerator() )
{
# get category id
$CategoryId = $category.Name

$IsKnownCategory = InitializeWithCategory $CategoryId

if ($IsKnownCategory)
{

$RegKeyPath = GetRegKeyPath( $CategoryId )

$key = $gReg.OpenSubKey($RegKeyPath, $true)

if ($key -ne $null)
{
TRACE("Opened registry key path for discovery signature check : "+$RegKeyPath)
try {

foreach( $InstanceName in $key.GetValueNames() )
{
# The value of the registry entry is the discovery signature for this instance
$InstDiscoverySignature = $key.GetValue($InstanceName)
if ($InstDiscoverySignature -ne $DiscoverySignature) {

$key.DeleteValue( $InstanceName )

DiscoverHealthyInstance $CategoryId `
$InstanceName
TRACE("["+$InstanceName+"]DISCOVERED Healthy Instance whose Discovery Signature is different" )
}
}
} finally {
$key.Close()
}
}
}
else
{
TRACE("Unknown Category : "+$CategoryId)
}
}
}

# Query QoE DB to get all problematic instances and healthy instance groups
# Discover Instances (for each category)
# 1 healthy instance group for all healthy QoE instances
# 1 SCOM instance for each problematic(=warning or critical) QoE instance
function DiscoverInstances()
{

# Fix BUG-209236 [QoE SCOM] Active Alerts never get resolved
#
# SCOM does not auto resolve alerts if a problematic instance is not
# discovered in the next discovery.
#
# We should a discover healthy instance right before a problematic
# instance is undiscovered to auto-resolve alerts for it.
#
# For each run of this discovery script, we will get a GUID and call it as discovery
# signature. All instances discovered in an execution of discovery will have the
# same discovery signature and we will update the discovery signature for each
# discovered instance in Registry.
#
# After the discovery is finished, the script will iterate each
# instance in the registry. If the discovery signature is
# different from the one of the current discovery, we can assume
# that the instance was discovered in the previous discovery.
# In this case we can simply discover the instance as healthy so that all
# alerts are auto-resolved.
#
# Of course, we should remove the instance from the registry so that it is not discovered as
# healthy again in the next discovery.
#
$DiscoverySignature = [guid]::NewGuid().ToString()

$AlertSettingsXML = ""
# Aet threshold Settings from input parameters
GetAlertSettings ([ref]$AlertSettingsXML)
TRACE("AlertSettingsXML="+$AlertSettingsXML)

$MinimumCallVolume = GetMinCallsAffected
TRACE("MinimumCallVolume="+$MinimumCallVolume)

TRACE("SQLInstance="+$SQLInstance)
TRACE("MirrorSQLInstance="+$MirrorSQLInstance)

$QueryEndUTCTime = [System.DateTime]::UtcNow
# Subtract 15 sec from the endtime to avoid collisions with calls that are being inserted currently.
$QueryEndUTCTime = $QueryEndUTCTime.AddSeconds(-15)
$QueryStartUTCTime = $QueryEndUTCTime.AddMinutes( -1 * $Default_MinutesToQuery )

TRACE("gQueryStartUTCTime ="+$QueryStartUTCTime)
TRACE("gQueryEndUTCTime ="+$QueryEndUTCTime)

$QueryContext = DB_OpenQueryContext $SQLInstance `
$MirrorSQLInstance `
$AlertSettingsXML `
$QueryStartUTCTime `
$QueryEndUTCTime


# Cuation : Should use [ref] type for SqlDataReader.
# Otherwise SqlDataReader reads all records from resultset once
# (1)it is assigned to a varible or
# (2)passed as an argument to a function
$RefDataReader = DB_GetDataReaderReference $QueryContext

do {
while( $RefDataReader.Value.Read() ) {
$CategoryId = Reader_GetCategoryId( $RefDataReader )
$IsKnownCategory = InitializeWithCategory $CategoryId

$InstanceName = Reader_GetName( $RefDataReader )
TRACE("Read Row (CategoryId:"+$CategoryId+";Name="+$InstanceName+")")

if ($IsKnownCategory)
{
$WarningLevel = [double] -1.0
$ErrorLevel = [double] -1.0

$CategoryName = $CATEGORY_NAME_MAP[$CategoryId]
GetCategoryThresholds $CategoryName `
([ref]$WarningLevel) `
([ref]$ErrorLevel)

$attrHash = $null
$attrHash = @{}

if ( $InstanceName -eq $HEALTHY_INSTANCE_GROUP_NAME )
{
GetAlertingContextForHealthyInstanceGroup $RefDataReader `
$attrHash
}
else
{
GetAlertingContextForProblematicInstance $RefDataReader `
$MinimumCallVolume `
$ErrorLevel `
$WarningLevel `
$QueryStartUTCTime `
$QueryEndUTCTime `
$attrHash

UpdateDiscoverySignature $CategoryId `
$InstanceName `
$DiscoverySignature
}

AddInstance $InstanceName `
$attrHash

TRACE ( "Discovered ("+$CATEGORY_NAME_MAP[$CategoryId]+")" + $InstanceName )
}
else
{
TRACE ( "Unkown Category : "+$CategoryId+", Instance Name : " + $InstanceName )
}
}
} while ($RefDataReader.Value.NextResult())

DB_CloseQueryContext $QueryContext

DiscoverHealthyInstanceIfNotMatchDiscoverySignature $DiscoverySignature
}



###################################################################################################
# Main Routine
###################################################################################################
TRACE "`n=============================================================`n"
[System.Reflection.Assembly]::LoadWithPartialName("System.Web") &gt; $null

# Open remote registry
$gReg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::CurrentUser, $ComputerName )

# Ensure RTC registry key exists
EnsureRegKey $MICROSOFT_REG_KEY_PATH `
$RTC_REG_KEY_PATH

# If no registry is found for the report URL, set $REPORT_URL_PREFIX to $null
$REPORT_URL_PREFIX = GetReportingServiceBaseUrl
if ( $REPORT_URL_PREFIX ) {
TRACE("Detected Reporting URL =&gt; " + $REPORT_URL_PREFIX )
}

$DiscoveryData = GetDiscoveryData
TRACE ("Successfully initialize discovery data.");

# Discover instances
DiscoverInstances $SourceId `
$ManagedEntityId

TRACE "=============================================================`n"
TRACE "Finished Discovery for QoE instances`n"

</Script></ScriptBody>
<TimeoutSeconds>$Config/TimeoutSeconds$</TimeoutSeconds>
<EventId>225</EventId>
<TargetComputer>$Target/Host/Property[Type="Windows!Microsoft.Windows.Computer"]/PrincipalName$</TargetComputer>
</DataSource>
</MemberModules>
<Composition>
<Node ID="Microsoft.LS.2013.QoE.MonitoringNode.DiscoverProvider"/>
</Composition>
</Composite>
</ModuleImplementation>
<OutputType>System!System.Discovery.Data</OutputType>
</DataSourceModuleType>