MDW Appliance v2 Discovery

Microsoft.SQLServerAppliance.MDW2.Appliance.Discovery (Discovery)

This object discovery detects all types of Microsoft Data Warehouse Appliances v2 and related servers.

Knowledge Base article:

Summary

This object discovery detects Microsoft Data Warehouse Appliances v2 and related servers. It collects information about the Microsoft Data Warehouse Appliance v2 type, name, and other common properties.

Element properties:

TargetMicrosoft.SQLServerAppliance.ApplianceSeed
EnabledTrue
Frequency14400
RemotableFalse

Object Discovery Details:

Discovered Classes and their attribuets:

Member Modules:

ID Module Type TypeId RunAs 
DS DataSource Microsoft.Windows.TimedScript.DiscoveryProvider Microsoft.SQLServerAppliance.MDW2.DiscoveryProfile

Source Code:

<Discovery ID="Microsoft.SQLServerAppliance.MDW2.Appliance.Discovery" Enabled="true" Target="SSALibrary!Microsoft.SQLServerAppliance.ApplianceSeed" ConfirmDelivery="false" Remotable="true" Priority="Normal">
<Category>Discovery</Category>
<DiscoveryTypes>
<DiscoveryClass TypeID="Microsoft.SQLServerAppliance.MDW2.Server"/>
<DiscoveryClass TypeID="Microsoft.SQLServerAppliance.MDW2.Appliance"/>
</DiscoveryTypes>
<DataSource RunAs="Microsoft.SQLServerAppliance.MDW2.DiscoveryProfile" ID="DS" TypeID="Windows!Microsoft.Windows.TimedScript.DiscoveryProvider">
<IntervalSeconds>14400</IntervalSeconds>
<SyncTime/>
<ScriptName>MDWApplianceDiscovery.vbs</ScriptName>
<Arguments>"$MPElement$" "$Target/Id$" "$Target/Host/Property[Type="Windows!Microsoft.Windows.Computer"]/PrincipalName$"</Arguments>
<ScriptBody><Script>
' ##### ..\Scripts\Common\Common.vbs
Option Explicit
SetLocale("en-us")

Dim USE_EVENT_LOG, USE_ERROR_LOG

Const DEBUG_MODE = false
Const DEBUG_SCRIPT_EVENT_ID = 4201

Const SCRIPT_EVENT_ID = 4200

USE_EVENT_LOG = true
USE_ERROR_LOG = false


Const ManagementGroupName = "$Target/ManagementGroup/Name$"


Sub HandleError(ByVal customMessage)
Dim logger
If Err.number &lt;&gt; 0 Then
Set logger = new ScriptLogger
logger.LogError(customMessage)
If Not USE_ERROR_LOG Then
Wscript.Quit 0
Else
Wscript.Quit 1
End If
End If
End Sub

Sub HandleErrorInMonitoring(ByVal customMessage, ByRef oBag, ByRef oAPI)
Dim logger
If Err.number &lt;&gt; 0 Then
Set logger = new ScriptLogger
logger.LogError(customMessage)
oAPI.Return(oBag)
Wscript.Quit 0
End If
End Sub

Sub HandleErrorInDiscovery(ByVal customMessage, ByRef oDiscoveryData, ByRef oAPI)
Dim logger
If Err.number &lt;&gt; 0 Then
Set logger = new ScriptLogger
logger.LogError(customMessage)
oDiscoveryData.IsSnapshot = false
HandleDebugMessage("Discovery: IsSnapshot = false")
oAPI.Return(oDiscoveryData)
Wscript.Quit 0
End If
End Sub

Sub HandleDebugMessage(ByVal debugMessage)
Dim logger
Set logger = new ScriptLogger
logger.LogDebug(debugMessage)
End Sub


Const ERROR_EVENT_TYPE = 1
Const WARNING_EVENT_TYPE = 2
Const INFO_EVENT_TYPE = 4

Class ScriptLogger
Dim sourceLogEvent
Dim oAPI

Private Sub Class_Initialize()
sourceLogEvent = "Managegement Group: " + ManagementGroupName + ". Script: " + WScript.ScriptName
Set oAPI = CreateObject("MOM.ScriptAPI")
End Sub

Private Function LogEvent(ByVal message, ByVal eventType, ByVal scriptEventID)
On Error Resume Next
Call oAPI.LogScriptEvent(sourceLogEvent, scriptEventID, eventType, message)
End Function

Public Function LogDebug(ByVal message)
if DEBUG_MODE Then
WScript.StdOut.WriteLine message
Call oAPI.LogScriptEvent(sourceLogEvent, DEBUG_SCRIPT_EVENT_ID, INFO_EVENT_TYPE, message)
End If
End Function

Public Function LogWarning(ByVal message, ByVal eventID)
if DEBUG_MODE Then
WScript.StdOut.WriteLine message
End If
LogEvent message, WARNING_EVENT_TYPE, eventID
End Function

Public Function LogError(ByVal customMessage)
Dim message
If Err.number &lt;&gt; 0 Then
message = Replace(" Error Number: #P1# " &amp; VbCrLf &amp; " Description: #P2# ", "#P1#", CStr(Err.number and 65535) )
message = Replace(message, "#P2#", Err.Description )
message = customMessage &amp; VbCrLf &amp; message &amp; VbCrLf
if DEBUG_MODE Then
WScript.StdOut.WriteLine message
End If

If USE_EVENT_LOG Then
LogEvent message, ERROR_EVENT_TYPE, SCRIPT_EVENT_ID
End If
If USE_ERROR_LOG Then
WScript.StdErr.WriteLine message
End If
End If
End Function

End Class

Function IsStringEmpty(ByVal sValue)
If sValue = EMPTY Or Trim(sValue) = "" Then
IsStringEmpty = true
Else
IsStringEmpty = false
End If
End Function
' ##### ..\Scripts\Common\AdoDB.vbs

'This class is wrapper for ADODB typical operations (Connect and Run SQL query)
Class SQLQuery
PRIVATE adodbConnection

Private Sub Class_Initialize()
On Error Resume Next
Set adodbConnection = CreateObject("ADODB.Connection")
Call HandleError("Cannot create ADODB.Connection")
End Sub

Private Sub Class_Terminate()
On Error Resume Next

'adodbConnection.State = 0 - Closed
If adodbConnection.State &gt; 0 Then
Call adodbConnection.Close()
End If

Call HandleError("Cannot close ADODB.Connection")
End Sub
'This method create and open connection to sql Server
'&lt;connectionString&gt; - name of the server and instance, e.g. "server1.mydomain.com\Instance1";
'use only server name to connect to default instance
Public Sub Connect(ByVal connectionString, ByVal databaseName)
On Error Resume Next
Dim providerString
providerString = "Provider=SQLOLEDB;Server=" &amp; EscapeConnStringValue(connectionString) &amp; ";Database="&amp; EscapeConnStringValue(databaseName) &amp;";Trusted_Connection=yes"
Call adodbConnection.Open(providerString)
End Sub

'This function execute &lt;query&gt; with &lt;parameters&gt; and returns ADODB.RecordSet
'&lt;parameters&gt; must be array, and must be with the same order as sql query &lt;?&gt; paremetrs
Public Function ExecuteQueryToRecordSet(ByVal query, ByVal parameters)
Dim adodbCommand, oResults
Set adodbCommand = CreateObject("ADODB.Command")
Call HandleError("Cannot create ADODB.Command")
adodbCommand.ActiveConnection = adodbConnection
adodbCommand.CommandText = query
Dim i, dataType, dataSize, parameter, length
length = UBound(parameters)
'Put parameters in to ADODB.Command
For i=0 To length
parameter = parameters(i)
dataType = GetDataTypeEnum(parameter)
dataSize = GetDataTypeSize(parameter)
adodbCommand.Parameters.Append(adodbCommand.CreateParameter("p"&amp;i, dataType, 1, dataSize, parameter)) '1 - this is adParamInput in ADODB. see http://www.w3schools.com/ado/met_comm_createparameter.asp#parameterdirenum
Next
Set ExecuteQueryToRecordSet = adodbCommand.Execute()
End Function

'returns size of string or 0 for others types
Private Function GetDataTypeSize(ByVal vValue)
If TypeName(vValue)="String" Then
GetDataTypeSize = Len(vValue)
Else
GetDataTypeSize = 0
End If
End Function

'this function returns int value of some DataTypeEnum
'values of adodb types see on http://www.w3schools.com/ado/met_comm_createparameter.asp#datatypeenum
Private Function GetDataTypeEnum(ByVal vValue)
If TypeName(vValue) = "Byte" Then
GetDataTypeEnum = 2 ' adSmallInt=2
End If
If TypeName(vValue)="Integer" or TypeName(vValue)="Long" Then
GetDataTypeEnum = 3 ' adInteger=3
End If
If TypeName(vValue)="Double" Then
GetDataTypeEnum = 5 ' adDouble=5
End If
If TypeName(vValue)="String" Then
GetDataTypeEnum = 8 ' adBSTR = 8
End If
End Function

'This function escapes string value for connection string
Private Function EscapeConnStringValue (ByVal strValue)
On Error Resume Next
EscapeConnStringValue = "{" + Replace(strValue, "}", "}}") + "}"
End Function

End Class
' ##### ..\Scripts\Common\WMIRegestry.vbs
'' Provides Regestry access via WMI
''
'' Important : This vbs module requires Common.vbs!!!

'This class can connect to wmi and call StdRegProv methods
Class Registry
'Standard Hives
Public HKEY_CLASSES_ROOT
Public HKEY_CURRENT_USER
Public HKEY_LOCAL_MACHINE
Public HKEY_USERS
Public HKEY_CURRENT_CONFIG


'Results of calling some StdRegProv methods
Public ERROR_ACCESS_DENIED
Public ERROR_KEY_NOT_FOUND
Public ERROR_VALUE_NOT_FOUND
Public SUCCESS

Private oReg
Private m_lHive

Private Sub Class_Initialize()
HKEY_CLASSES_ROOT = &amp;H80000000
HKEY_CURRENT_USER = &amp;H80000001
HKEY_LOCAL_MACHINE = &amp;H80000002
HKEY_USERS = &amp;H80000003
HKEY_CURRENT_CONFIG = &amp;H80000005

ERROR_ACCESS_DENIED = 5
ERROR_KEY_NOT_FOUND = 2
ERROR_VALUE_NOT_FOUND = 1
SUCCESS = 0

m_lHive = HKEY_LOCAL_MACHINE
End Sub

'connect to WMI
Public Sub Connect(ByVal sHostName)
On Error Resume Next
Set oReg = GetObject("winmgmts://" &amp; sHostName &amp; "/root/default:StdRegProv")
Call HandleError("Cannot connect to WMI on " &amp; sHostName)
End Sub

'Getter of Hive property
' Hive is root folder in Regestry (for example HKLM\)
Public Property Get Hive()
Hive = m_lHive
End Property
'Setter Hive
Public Property Let Hive(ByVal lHive)
m_lHive = lHive
End Property

'This method reads all subkeys and returns it
Public Function EnumKey(ByVal sKeyPath, ByRef lResult)
Dim sNames
lResult = oReg.EnumKey(m_lHive, sKeyPath, sNames)
EnumKey = sNames
End Function

'This method reads all key values and returns it
Public Function EnumValues(ByVal sKeyPath, ByRef lResult)
Dim aNames
Dim aTypes
lResult = oReg.EnumValues(m_lHive, sKeyPath, aNames, aTypes)
EnumValues = aNames
End Function


Public Function ReadStringValue(ByVal sKeyPath, ByVal sValueName, ByRef lResult)
Dim sValue
lResult = oReg.GetStringValue(m_lHive, sKeyPath, sValueName, sValue)
ReadStringValue = sValue
End Function

Public Function WriteStringValue(ByVal sKeyPath, ByVal sValueName, ByVal sValue)
WriteStringValue = oReg.SetStringValue(m_lHive, sKeyPath, sValueName, sValue)
End Function

'This method cheks what regestry key with [sKeyPath] is exists
Public Function KeyExists(ByVal sKeyPath)
On Error Resume Next
Dim aSubfolders
Dim result
result = oReg.EnumKey(m_lHive, sKeyPath, aSubfolders)
if result = SUCCESS Then
KeyExists = true
Else
KeyExists = false
End If
End Function

End Class
' ##### Scripts\MDWApplianceDiscovery.vbs
On Error Resume Next

Const ID_MISSED_EVENT_ID = 4211
Const PROPERTY_MISSED_OR_EMPTY_EVENT_ID = 4212

Const SQL_SERVER_APPLIANCE = "SQL Server Appliance"
Const BACKSLASH = "\"
Const REGISTRY_ROOT = "HKLM\"

Const SPECIFIC_DISCOVERY_KEY = "Specific Discovery"
Const ROLE_KEY = "Role"
Const VERSION_KEY = "Version"
Const TYPE_KEY = "Type"
Const ID_KEY = "Id"
Const NAME_KEY = "Name"
Const MANUFACTURER_KEY = "Vendor"
Const MODEL_KEY = "Model"

Const MDW_TYPE = "MDW"
Const APPLIANCE_TYPE_ALIASE = "MDW"

Dim SQL_SERVER_APPLIANCE_PATH
SQL_SERVER_APPLIANCE_PATH = SQL_SERVER_APPLIANCE &amp; BACKSLASH

Dim APPLIANCE_PATH
APPLIANCE_PATH = "Software\Microsoft\" &amp; SQL_SERVER_APPLIANCE_PATH

Const MSG_MISSED_OR_EMPTY_KEY = "Registry entry doesn't exist or empty: "
Const MSG_CANNOT_READ_FROM_REG_KEY = "Cannot read value from registry key"
Const QUOTE = """"

Dim SourceID
Dim ManagedEntityId

Dim PrincipalName

Dim oShell
Dim oRegistry
Dim oAPI
Dim logger

Set oShell = CreateObject("WScript.Shell")
Call HandleError("Cannot create WScript.Shell object")

Set oAPI = CreateObject("MOM.ScriptAPI")
Call HandleError("Cannot create MOM.ScriptAPI object")

Set oRegistry = new Registry
Set logger = new ScriptLogger


Dim oDiscoveryData

Call Main()

'This function try read key from registry
Function TryReadValue(ByVal key)
On Error Resume Next
Dim lResult
TryReadValue = oRegistry.ReadStringValue(APPLIANCE_PATH, key, lResult)

If lResult = oRegistry.ERROR_KEY_NOT_FOUND or lResult = oRegistry.ERROR_VALUE_NOT_FOUND Then
TryReadValue = empty
End If

If lResult = oRegistry.ERROR_ACCESS_DENIED Then
Call HandleErrorInDiscovery("Access Denied to WMI on " &amp; PrincipalName, oDiscoveryData, oAPI)
Else
Call HandleErrorInDiscovery(MSG_CANNOT_READ_FROM_REG_KEY, oDiscoveryData, oAPI)
End If
End Function

'Detect if computer is Appliance Server by registry Path
'Return True if path is found, False if not
Function IsAppliance()
On Error Resume Next
IsAppliance = oRegistry.KeyExists(APPLIANCE_PATH)
Call HandleErrorInDiscovery("Cannot access appliance registry key", oDiscoveryData, oAPI)
End Function

'Set specific discovery property in MOM regKey for exlude this Server form GenericDiscovery
Sub SetSpecificDiscovered()
On Error Resume Next

Dim registryKey, specificDiscovery
registryKey = oAPI.GetScriptStateKeyPath(SQL_SERVER_APPLIANCE)' &amp; BACKSLASH &amp; SPECIFIC_DISCOVERY_KEY

specificDiscovery = oRegistry.WriteStringValue(registryKey, SPECIFIC_DISCOVERY_KEY, "true")
Call HandleErrorInDiscovery("Cannot write in registry Specific Discovered key", oDiscoveryData, oAPI)
End Sub

'Main method
Sub Main()
On Error Resume Next
Dim oParams
Set oParams = WScript.Arguments

SourceID = oParams(0)
Call HandleError("Cannot read SourceID from params")

ManagedEntityId = oParams(1)
Call HandleError("Cannot read ManagedEntityId from params")

PrincipalName = oParams(2)
Call HandleError("Cannot read PrincipalName from params")

Set oParams = Nothing

oRegistry.Connect(PrincipalName)'Connect to WMI service

Call DoServiceDiscovery()

End Sub

'Method for discovery data
Sub DoServiceDiscovery()
On Error Resume Next
Call HandleDebugMessage("Begin discovery")

Set oDiscoveryData = oAPI.CreateDiscoveryData(0, SourceId, ManagedEntityId)
Call HandleError("Cannot create Discovery Data object")

'If this machine is appliance and not need special discovery
If IsAppliance() Then
Call DiscoverApplianceServer(oDiscoveryData)
Call HandleErrorInDiscovery("Cannot discover Appliance", oDiscoveryData, oAPI)
End If

Call oAPI.Return(oDiscoveryData)
Call HandleDebugMessage("Discovery successfully complete")
End Sub

Function GetSQLMemoryMaxSize(ConnectionString)
On Error Resume Next
Const EnableAdvancedOptions = "EXEC sp_configure 'show advanced options', 1;RECONFIGURE"
Const GetMemoryMaxSize = "EXEC sp_configure 'max server memory (MB)'"
Const DisableAdvancedOptions = "EXEC sp_configure 'show advanced options', 0;RECONFIGURE"

Dim sql : Set sql = new SQLQuery
Call sql.Connect(ConnectionString, "master")
Call HandleErrorInDiscovery("Cannot connect to SQL Server with connection string: " &amp; ConnectionString, oDiscoveryData, oAPI)

Call sql.ExecuteQueryToRecordSet(EnableAdvancedOptions, Array())
Dim recordSet : Set recordSet = sql.ExecuteQueryToRecordSet(GetMemoryMaxSize, Array())
Call sql.ExecuteQueryToRecordSet(DisableAdvancedOptions, Array())
Call HandleErrorInDiscovery("Cannot get memory max value for " &amp; ConnectionString, oDiscoveryData, oAPI)

GetSQLMemoryMaxSize = recordSet(4)
End Function

'Method discovered appliance and servers
Sub DiscoverApplianceServer(ByRef oDiscoveryData)

Dim applianceID, applianceType, applianceName, applianceVersion
Dim computerRole, applianceManufacturer, applianceModel
Dim path

applianceType = TryReadValue(TYPE_KEY)

'If Type is not defined in Registry - this is not correctly configuration
' and we fire event in event log and finish discovery
If IsStringEmpty(applianceType) Then
Call logger.LogWarning(MSG_MISSED_OR_EMPTY_KEY &amp; QUOTE &amp; TYPE_KEY &amp; QUOTE, PROPERTY_MISSED_OR_EMPTY_EVENT_ID)
oDiscoveryData.IsSnapshot = false
Exit Sub
End If

'If type of Server where we reag from registry is not MDW appliance, just exit from this disco
If applianceType &lt;&gt; MDW_TYPE Then
Exit Sub
End If

applianceVersion = TryReadValue(VERSION_KEY)
If IsStringEmpty(applianceVersion) Then
Call logger.LogWarning(MSG_MISSED_OR_EMPTY_KEY &amp; QUOTE &amp; VERSION_KEY &amp; QUOTE, PROPERTY_MISSED_OR_EMPTY_EVENT_ID)
oDiscoveryData.IsSnapshot = false
Exit Sub
End If

'If Minor and Majour version of MDW Appliance is not equal 1.0. or 2.0.
'we do not discover this appliance
Dim minorMajorVersion
minorMajorVersion = Mid(applianceVersion, 1, 4)
If minorMajorVersion &lt;&gt; "2.0." Then
Call HandleDebugMessage("Version of MDW is not 2.0")
Exit Sub
End If

applianceID = TryReadValue(ID_KEY)

If IsStringEmpty(applianceID) Then
oDiscoveryData.IsSnapshot = false
Call logger.LogWarning(MSG_MISSED_OR_EMPTY_KEY &amp; QUOTE &amp; ID_KEY &amp; QUOTE, ID_MISSED_EVENT_ID)
Exit Sub
End If

applianceName = PrincipalName

computerRole = TryReadValue(ROLE_KEY)
If IsStringEmpty(computerRole) Then
Call logger.LogWarning(MSG_MISSED_OR_EMPTY_KEY &amp; QUOTE &amp; ROLE_KEY &amp; QUOTE, PROPERTY_MISSED_OR_EMPTY_EVENT_ID)
End If

applianceManufacturer = TryReadValue(MANUFACTURER_KEY)
If IsStringEmpty(applianceManufacturer) Then
Call logger.LogWarning(MSG_MISSED_OR_EMPTY_KEY &amp; QUOTE &amp; MANUFACTURER_KEY &amp; QUOTE, PROPERTY_MISSED_OR_EMPTY_EVENT_ID)
End If

applianceModel = TryReadValue(MODEL_KEY)
If IsStringEmpty(applianceModel) Then
Call logger.LogWarning(MSG_MISSED_OR_EMPTY_KEY &amp; QUOTE &amp; MODEL_KEY &amp; QUOTE, PROPERTY_MISSED_OR_EMPTY_EVENT_ID)
End If

Dim oAppliance
Dim oRelationship

Dim sqlMemoryMaxSize : sqlMemoryMaxSize = GetSQLMemoryMaxSize(applianceName)

Dim oServer
Set oServer = oDiscoveryData.CreateClassInstance("$MPElement[Name='Microsoft.SQLServerAppliance.MDW2.Server']$")
Set oAppliance = oDiscoveryData.CreateClassInstance("$MPElement[Name='Microsoft.SQLServerAppliance.MDW2.Appliance']$")

'DiscoverRelationShip
Call oServer.AddProperty("$MPElement[Name='Windows!Microsoft.Windows.Computer']/PrincipalName$", PrincipalName)
Call oAppliance.AddProperty("$MPElement[Name='SSALibrary!Microsoft.SQLServerAppliance.Appliance']/ApplianceID$", applianceID)

Set oRelationship = oDiscoveryData.CreateRelationshipInstance("$MPElement[Name='SSALibrary!Microsoft.SQLServerAppliance.ApplianceContainsServer']$")

oRelationship.Source = oAppliance
oRelationship.Target = oServer

oDiscoveryData.AddInstance(oRelationship)

Call oServer.AddProperty("$MPElement[Name='SSALibrary!Microsoft.SQLServerAppliance.Server']/Role$", computerRole)
Call oServer.AddProperty("$MPElement[Name='SSALibrary!Microsoft.SQLServerAppliance.Server']/ApplianceName$", applianceName)
Call oServer.AddProperty("$MPElement[Name='SSALibrary!Microsoft.SQLServerAppliance.Server']/ApplianceType$", APPLIANCE_TYPE_ALIASE)

oDiscoveryData.AddInstance(oServer)

Call oAppliance.AddProperty("$MPElement[Name='System!System.Entity']/DisplayName$", applianceName)
Call oAppliance.AddProperty("$MPElement[Name='SSALibrary!Microsoft.SQLServerAppliance.Appliance']/ApplianceName$", applianceName)
Call oAppliance.AddProperty("$MPElement[Name='SSALibrary!Microsoft.SQLServerAppliance.Appliance']/ApplianceModel$", applianceModel)
Call oAppliance.AddProperty("$MPElement[Name='SSALibrary!Microsoft.SQLServerAppliance.Appliance']/ApplianceVersion$", applianceVersion)
Call oAppliance.AddProperty("$MPElement[Name='SSALibrary!Microsoft.SQLServerAppliance.Appliance']/ApplianceType$", APPLIANCE_TYPE_ALIASE)
Call oAppliance.AddProperty("$MPElement[Name='SSALibrary!Microsoft.SQLServerAppliance.Appliance']/ApplianceManufacturer$", applianceManufacturer)
Call oAppliance.AddProperty("$MPElement[Name='Microsoft.SQLServerAppliance.MDW2.Appliance']/SQLMemoryMaxSize$", sqlMemoryMaxSize)

oDiscoveryData.AddInstance(oAppliance)

Call SetSpecificDiscovered()

End Sub

</Script></ScriptBody>
<TimeoutSeconds>300</TimeoutSeconds>
</DataSource>
</Discovery>