Discover all types of SQL Server Appliance

Microsoft.SQLServerAppliance.GenericAppliance.Discovery (Discovery)

This object discovery detects all types of SQL Server Appliances and related servers. This is a unified common discovery that provides generic properties for any type of SQL Server Appliance.

Knowledge Base article:

Summary

This object discovery detects SQL Server Appliances and related servers. It collects information about SQL Server Appliance 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.DefaultActionProfile

Source Code:

<Discovery ID="Microsoft.SQLServerAppliance.GenericAppliance.Discovery" Enabled="true" Target="SSALibrary!Microsoft.SQLServerAppliance.ApplianceSeed" ConfirmDelivery="false" Remotable="true" Priority="Normal">
<Category>Discovery</Category>
<DiscoveryTypes>
<DiscoveryClass TypeID="SSALibrary!Microsoft.SQLServerAppliance.Server">
<Property TypeID="System!System.Entity" PropertyID="DisplayName"/>
<Property TypeID="Windows!Microsoft.Windows.Computer" PropertyID="PrincipalName"/>
<Property TypeID="SSALibrary!Microsoft.SQLServerAppliance.Server" PropertyID="Role"/>
<Property TypeID="SSALibrary!Microsoft.SQLServerAppliance.Server" PropertyID="ClusterName"/>
</DiscoveryClass>
<DiscoveryClass TypeID="Microsoft.SQLServerAppliance.GenericAppliance"/>
</DiscoveryTypes>
<DataSource ID="DS" TypeID="Windows!Microsoft.Windows.TimedScript.DiscoveryProvider" RunAs="SSALibrary!Microsoft.SQLServerAppliance.DefaultActionProfile">
<IntervalSeconds>14400</IntervalSeconds>
<SyncTime/>
<ScriptName>GenericApplianceDiscovery.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\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\GenericApplianceDiscovery.vbs

On Error Resume Next

Const ID_MISSED_EVENT_ID = 4202
Const PROPERTY_MISSED_OR_EMPTY_EVENT_ID = 4203

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 MODEL_KEY = "Model"
Const MANUFACTURER_KEY = "Vendor"

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 is missing or empty: "
Const MSG_INCORRECT_KEY = "Registry entry is incorrect: "
Const MSG_CANNOT_READ_FROM_REG_KEY = "Cannot read value from registry key"
Const MSG_WMI_ACCESS_DENIED = "Access Denied to WMI on "
Const QUOTE = """"

CONST GET_CLUSTER_NAME = "select name from MSCluster_Cluster"
CONST GET_COMPUTER_DOMAIN = "select domain from WIN32_computersystem"

Dim SourceID
Dim ManagedEntityId

Dim PrincipalName

Dim oAPI
Dim oRegistry
Dim logger

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
logger.LogError(MSG_WMI_ACCESS_DENIED &amp; PrincipalName)
End If

Call HandleErrorInDiscovery(MSG_CANNOT_READ_FROM_REG_KEY, oDiscoveryData, oAPI)

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

'Detects if specific discovery has been executed to prevent base discovery from execution
Function IsSpecificDiscovered()
Dim registryKey, specificDiscovery, lResult
registryKey = oAPI.GetScriptStateKeyPath(SQL_SERVER_APPLIANCE) &amp; BACKSLASH
On Error Resume Next
specificDiscovery = oRegistry.ReadStringValue(registryKey, SPECIFIC_DISCOVERY_KEY, lResult)

If lResult &lt;&gt; 0 Then
IsSpecificDiscovered = false
Exit Function
Else
Call HandleDebugMessage("Is Specific Discovery:" &amp; specificDiscovery)
if specificDiscovery = "true" then
IsSpecificDiscovered = true
else
IsSpecificDiscovered = false
end if
End If
End Function

'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 " &amp; PrincipalName)

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
If (not IsSpecificDiscovered()) Then
Call DiscoverApplianceServer(oDiscoveryData)
Call HandleErrorInDiscovery("Cannot discover Appliance", oDiscoveryData, oAPI)
End If
End If

Call oAPI.Return(oDiscoveryData)
Call HandleDebugMessage("Discovery successfully complete " &amp; PrincipalName)
End Sub

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

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

Set logger = new ScriptLogger

applianceID = TryReadValue(ID_KEY)

If applianceID = EMPTY Or Trim(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

applianceType = TryReadValue(TYPE_KEY)
If applianceType = EMPTY Or Trim(applianceType) = "" Then
Call logger.LogWarning(MSG_MISSED_OR_EMPTY_KEY &amp; QUOTE &amp; TYPE_KEY &amp; QUOTE, PROPERTY_MISSED_OR_EMPTY_EVENT_ID)
End If

applianceName = TryReadValue(NAME_KEY)

If applianceName = EMPTY Or Trim(applianceName) = "" Then
applianceName = PrincipalName
End If

applianceVersion = TryReadValue(VERSION_KEY)
If applianceVersion = EMPTY Or Trim(applianceVersion) = "" Then
Call logger.LogWarning(MSG_MISSED_OR_EMPTY_KEY &amp; QUOTE &amp; VERSION_KEY &amp; QUOTE, PROPERTY_MISSED_OR_EMPTY_EVENT_ID)
End If

computerRole = TryReadValue(ROLE_KEY)
If computerRole = EMPTY Or Trim(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

applianceModel = TryReadValue(MODEL_KEY)
If applianceModel = EMPTY Or Trim(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

applianceManufacturer = TryReadValue(MANUFACTURER_KEY)
If applianceManufacturer = EMPTY Or Trim(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

Dim oAppliance
Dim oRelationship

Dim oServer
Dim oCluster
Dim oClusterRelationship
Dim clusterName

clusterName = ""

Set oServer = oDiscoveryData.CreateClassInstance("$MPElement[Name='SSALibrary!Microsoft.SQLServerAppliance.Server']$")
Call oServer.AddProperty("$MPElement[Name='Windows!Microsoft.Windows.Computer']/PrincipalName$", PrincipalName)

Set oAppliance = oDiscoveryData.CreateClassInstance("$MPElement[Name='Microsoft.SQLServerAppliance.GenericAppliance']$")
Call oAppliance.AddProperty("$MPElement[Name='SSALibrary!Microsoft.SQLServerAppliance.Appliance']/ApplianceID$", applianceID)

If Not IsClusterNode(PrincipalName) then
'DiscoverRelationShip
Set oRelationship = oDiscoveryData.CreateRelationshipInstance("$MPElement[Name='SSALibrary!Microsoft.SQLServerAppliance.ApplianceContainsServer']$")

oRelationship.Source = oAppliance
oRelationship.Target = oServer

oDiscoveryData.AddInstance(oRelationship)
Else
clusterName = GetClusterName(PrincipalName,oDiscoveryData)
Set oCluster = oDiscoveryData.CreateClassInstance("$MPElement[Name='Microsoft.SQLServerAppliance.GenericCluster']$")
Call oCluster.AddProperty("$MPElement[Name='System!System.Entity']/DisplayName$", clusterName)
Call oCluster.AddProperty("$MPElement[Name='SSALibrary!Microsoft.SQLServerAppliance.Cluster']/ClusterName$", clusterName)
Call oCluster.AddProperty("$MPElement[Name='SSALibrary!Microsoft.SQLServerAppliance.Cluster']/ApplianceName$", applianceName)

'Discover Relations between Cluster and Appliance and Server
Set oRelationship = oDiscoveryData.CreateRelationshipInstance("$MPElement[Name='SSALibrary!Microsoft.SQLServerAppliance.ApplianceContainsCluster']$")
oRelationship.Source = oAppliance
oRelationship.Target = oCluster
oDiscoveryData.AddInstance(oRelationship)

Set oClusterRelationship = oDiscoveryData.CreateRelationshipInstance("$MPElement[Name='SSALibrary!Microsoft.SQLServerAppliance.ClusterContainsServer']$")
oClusterRelationship.Source = oCluster
oClusterRelationship.Target = oServer
oDiscoveryData.AddInstance(oClusterRelationship)

oDiscoveryData.AddInstance(oCluster)
End If

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$", applianceType)
Call oServer.AddProperty("$MPElement[Name='SSALibrary!Microsoft.SQLServerAppliance.Server']/ClusterName$", clusterName)

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']/ApplianceVersion$", applianceVersion)
Call oAppliance.AddProperty("$MPElement[Name='SSALibrary!Microsoft.SQLServerAppliance.Appliance']/ApplianceType$", applianceType)
Call oAppliance.AddProperty("$MPElement[Name='SSALibrary!Microsoft.SQLServerAppliance.Appliance']/ApplianceModel$", applianceModel)
Call oAppliance.AddProperty("$MPElement[Name='SSALibrary!Microsoft.SQLServerAppliance.Appliance']/ApplianceManufacturer$", applianceManufacturer)

oDiscoveryData.AddInstance(oAppliance)


End Sub

Function IsClusterNode(ByVal sHostName)
Dim oCluster, oColl, oItem, clusterName
IsClusterNode = false
On Error Resume Next
Set oCluster = GetObject("winmgmts://" &amp; sHostName &amp; "/root/mscluster")
If Err.number &lt;&gt; 0 Then
Err.Clear
Exit Function
End If

Set oColl = oCluster.ExecQuery(GET_CLUSTER_NAME)
'If collection is empty when read from it error raised - need reset it.
For Each oItem in oColl
If Err.number &lt;&gt; 0 Then
Err.Clear
Exit Function
Else
clusterName = oItem.Name
Exit For
End If
Next
If clusterName &lt;&gt; "" or clusterName &lt;&gt; Empty Then
IsClusterNode = true
End If
End Function

'Get cluster principal name
Public Function GetClusterName(ByVal sHostName, ByRef oDiscoveryData)
Dim oCluster, oColl, oItem, clusterName, domainName
On Error Resume Next
Set oCluster = GetObject("winmgmts://" &amp; sHostName &amp; "/root/mscluster")

Call HandleErrorInDiscovery("Cannot connect to WMI on " &amp; sHostName, oDiscoveryData, oAPI)

Set oColl = oCluster.ExecQuery(GET_CLUSTER_NAME)
Call HandleErrorInDiscovery("Cannot connect to WMI on " &amp; sHostName, oDiscoveryData, oAPI)


For Each oItem in oColl
clusterName = oItem.Name
Exit For
Next

domainName = GetComputerDomain(sHostName, oDiscoveryData)
If domainName &lt;&gt; "" Then
GetClusterName = clusterName &amp; "." &amp; domainName
Else GetClusterName = clusterName
End if

End Function

'Get domain name from local computer
Public Function GetComputerDomain(ByVal sHostName, ByRef oDiscoveryData)
Dim oComputer, oColl, oItem
On Error Resume Next

Set oComputer = GetObject("winmgmts://" &amp; sHostName &amp; "/root/cimv2")

Call HandleErrorInDiscovery("Cannot connect to WMI on " &amp; sHostName, oDiscoveryData, oAPI)

Set oColl = oComputer.ExecQuery(GET_COMPUTER_DOMAIN)
Call HandleErrorInDiscovery("Cannot connect to WMI on " &amp; sHostName, oDiscoveryData, oAPI)


For Each oItem in oColl
GetComputerDomain = oItem.Domain
Exit Function
Next

End Function
</Script></ScriptBody>
<TimeoutSeconds>360</TimeoutSeconds>
</DataSource>
</Discovery>