SQL DM Instance Discovery (PowerShell)

Idera.SQLDM.SqlInstance.Discovery (Discovery)

This object discovers SQL Server Instances which are currently being monitored by a SQL DM installations. The discovery runs on machines with SQL DM installations and examines the SQL DM database to identify managed instances.

Knowledge Base article:

Summary

This object discovers SQL Server Instances which are currently being monitored by a SQL DM installations. The discovery runs on machines with SQL DM installations and examines the SQL DM database to identify managed instances.

Element properties:

TargetIdera.SQLDM.DMservice
EnabledTrue
Frequency180
RemotableFalse

Object Discovery Details:

Discovered Classes and their attribuets:

Member Modules:

ID Module Type TypeId RunAs 
Microsoft.Windows.TimedPowerShell.DiscoveryProvider DataSource Microsoft.Windows.TimedPowerShell.DiscoveryProvider Default

Source Code:

<Discovery ID="Idera.SQLDM.SqlInstance.Discovery" Enabled="true" Target="Idera.SQLDM.DMservice" ConfirmDelivery="true" Remotable="true" Priority="Normal">
<Category>Discovery</Category>
<DiscoveryTypes>
<DiscoveryClass TypeID="Idera.SQLDM.SqlInstance"/>
</DiscoveryTypes>
<DataSource ID="Microsoft.Windows.TimedPowerShell.DiscoveryProvider" TypeID="Windows!Microsoft.Windows.TimedPowerShell.DiscoveryProvider">
<IntervalSeconds>180</IntervalSeconds>
<SyncTime>00:01</SyncTime>
<ScriptName>SqlDmInstanceDiscovery.ps1</ScriptName>
<ScriptBody><Script>
#==================================================================================
# Script: SqlDmInstanceDiscovery.ps1
# Date: 1/31/13
# Author: Martin Dey, Xtreme Consulting
# Purpose: SQL Instances being managed by a local SQL Diagnoastic Manager service
#==================================================================================
param($sourceId, $managedEntityId, $dmDb, $computerName, $dmPath)

#Constants used for event logging
$debug = $true
$SCRIPT_NAME = 'SqlDmInstanceDiscovery.ps1'
$EVENT_LEVEL_ERROR = 1
$EVENT_LEVEL_WARNING = 2
$EVENT_LEVEL_INFO = 4
$SCRIPT_STARTED = 811
$CLASS_CREATED = 812
$SCRIPT_ENDED = 815
$SCRIPT_FAILED = 819
$DUMMY_PROPERTYBAG = 833

#==================================================================================
# Sub: Log-TxtFile
# Purpose: Logs an informational event to TXT log file, only
# if Debug key is true in Windows Registry &gt;&gt; SQLdm
#==================================================================================
function Log-TxtFile {
param($logPath,$message)

# review the key on server instances too.
# if (Test-Path -Path 'HKLM:\SOFTWARE\Idera\SQLdm') { # MPverbose
$currentDate = Get-Date;
$logFile = $currentDate.ToString("yyyy-MM-dd")
$logFile = "\Logs\SQLdm_MP_" + $logFile + ".log"
if (Test-Path ($logPath + $logFile)) {
Add-Content ($logPath + $logFile) ("`n" + $currentDate + " - " + $SCRIPT_NAME + " - " + $message)
}
else {
Set-Content ($logPath + $logFile) ("`n" + $currentDate + " - " + $SCRIPT_NAME + " - " + $message)
}
# } else {
# Create the key with false value
# }
}

#==================================================================================
# Sub: LogDebugEvent
# Purpose: Logs an informational event to the Operations Manager event log
# only if Debug argument is true
#==================================================================================

function Log-DebugEvent
{
param($eventNo,$message,$logPath)

if ($debugTxt -eq $true){
Log-TxtFile $logPath $message
}

if ($debug = $true)
{
$message = "`n" + $message
$api.LogScriptEvent($SCRIPT_NAME,$eventNo,$EVENT_LEVEL_INFO,$message)
}
}

#==================================================================================
# Sub: Get-SqlQuery
# Purpose: Runs a SQL Query using the SqlClient data library
# Returns: A powershell variation on a data table - an object array with a count,
# followed by a series of DataRow objects.
#==================================================================================
function Get-SqlQuery( $connstr, $querydm ) {
# Action of connecting to the Database and executing the query and returning results if there were any.
$conn = New-Object System.Data.SqlClient.SQLConnection
$conn.ConnectionString = $connstr
$conn.Open()
$cmd = New-Object System.Data.SqlClient.SqlCommand($querydm, $conn)
$cmd.CommandTimeout = 120
$dset = New-Object System.Data.DataSet
$da = New-Object System.Data.SqlClient.SqlDataAdapter($cmd)
[void]$da.fill($dset)
$conn.Close()
return $dset
}

#==================================================================================
# Sub: Get-DmServerInstance
# Purpose: Read 'SQLdmManagementService.exe.Config' file to get DM server instance
# Returns: The serverInstance as a string
#==================================================================================
function Get-DmServerInstance( $dmPath ) {
[xml]$dmxml = (Get-Content ($dmPath + "SQLdmManagementService.exe.Config"))
$serverInstance = $dmxml.SelectSingleNode("/configuration/Idera.SQLdm/Services/ManagementService/@repositoryServer").Value
return $serverInstance
}

#DEBUG MODE
$debugTxt = (Get-ItemProperty "HKLM:\SOFTWARE\Idera\SQLdm").FlagLog

# GET MOM SCRIPTING OBJECT
# Start by setting up API object and creating a discovery data object.
$api = New-Object -comObject 'MOM.ScriptAPI'
Log-DebugEvent $EVENT_LEVEL_INFO 'Creating MOMScriptAPI object: This object is the top-level object for the scripting API.' $dmPath

# GET INFORMATIONAL PROPERTIES
$messageParam = @"
Input parameters
Computer Name : {0}
SQLdm - Database name : {1}
SQLdm - Installation path: {2}
Source ID : {3}
Managed entity ID : {4}
"@
Log-DebugEvent $SCRIPT_STARTED ($messageParam -f $computerName, $dmDb, $dmPath, $sourceId, $managedEntityId) $dmPath

$machineName = [Environment]::MachineName
$osVersion = [Environment]::OSVersion
$processorCount = [Environment]::ProcessorCount
$userDomainName = [Environment]::UserDomainName
$powerShellVersion = [Environment]::Version
$commandLine = [Environment]::CommandLine
$systemDirectory = [Environment]::SystemDirectory
$currentDirectory = [Environment]::CurrentDirectory
$messageOS = @"
Windows Server properties
MachineName : {0}
OSVersion : {1}
ProcessorCount : {2}
UserDomainName : {3}
Version : {4}
CommandLine : {5}
SystemDirectory : {6}
CurrentDirectory : {7}
"@
$msgOS = $messageOS -f $machineName, $osVersion, $processorCount, $userDomainName, $powerShellVersion, $commandLine, $systemDirectory, $currentDirectory
Log-DebugEvent $EVENT_LEVEL_INFO $msgOS $dmPath

if (Test-Path -Path 'HKLM:\SOFTWARE\Microsoft\Microsoft Operations Manager\3.0\Setup') {
$scomProperties = (get-itemproperty 'HKLM:\SOFTWARE\Microsoft\Microsoft Operations Manager\3.0\Setup')
$properties = ""
foreach ($property in $scomProperties) {
$properties = $properties + ' ' + $property
}
$messageProperties = 'SCOM settings' + "`n" + `
' InstalledInstances: ' + $properties
Log-DebugEvent $EVENT_LEVEL_INFO $messageProperties $dmPath
} else {
Log-DebugEvent $EVENT_LEVEL_WARNING 'No data to display for SCOM settings' $dmPath
}

if (Test-Path -Path 'HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server') {
$sqlInstances = (get-itemproperty 'HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server').InstalledInstances
$instances = ""
foreach ($instance in $sqlInstances) {
$instances = $instances + ' . ' + $instance
}
$messageInstances = 'SQL Server instances' + "`n" + `
' InstalledInstances: ' + $instances
Log-DebugEvent $EVENT_LEVEL_INFO $messageInstances $dmPath
} else {
Log-DebugEvent $EVENT_LEVEL_WARNING 'No data to display for SQL Server instances' $dmPath
}

# DEFINE TRAP TO CAPTURE ERRORS
# Trap statement will catch any Exceptions and send details to the event log before exiting
# We always send direct to the event log - overriding the $debug flag - so we always log errors
trap {
$tmsg = "SQL DM Instance Discovery Script experienced an exception:`n`n$_"
Log-DebugEvent $SCRIPT_FAILED $tmsg $dmPath
$api.LogScriptEvent($SCRIPT_NAME,$SCRIPT_FAILED,$EVENT_LEVEL_ERROR,$tmsg)
exit
}

#Discovery data object requires the MPElement and Target/ID variables. The first argument in the method is always 0.
Log-DebugEvent $EVENT_LEVEL_INFO 'Creating MOMDiscoveryData object' $dmPath
$discoveryData = $api.CreateDiscoveryData(0, $sourceId, $managedEntityId)

$dmServerInstance = (Get-DmServerInstance $dmPath)
$connstr = "Server={0};Database={1};Integrated Security=True" -f $dmServerInstance, $dmDb
# Log a message that script is starting
$message = 'Script started' + "`n" + `
' Connection String: ' + $connstr

Log-DebugEvent $EVENT_LEVEL_INFO $message $dmPath

#Log a message that script is starting only if Debug argument is True
$message = 'Script started' + "`n" + `
'Source ID: ' + $sourceId + "`n" + `
'Managed Entity ID: ' + $managedEntityId + "`n" + `
'SQLDM DB: ' + $dmDb + "`n" + `
'Computer Name: ' + $computerName + "`n" + `
'Connection String: ' + $connstr

Log-DebugEvent $SCRIPT_STARTED $message $dmPath

# Read server table from SQLdm database
$qrystr = @"
SELECT TOP 300 [InstanceName]
,isnull([RealServerName], 'N/A') as [RealServerName]
,isnull([ServerVersion], 'N/A') as [ServerVersion]
,isnull([ServerEdition], 'N/A') as [ServerEdition]
,isnull([MaintenanceModeEnabled], 'N/A') as [MaintenanceModeEnabled]
FROM [dbo].[MonitoredSQLServers]
WHERE [Active] = 1
ORDER BY [InstanceName]
"@

$instances = (Get-SqlQuery $connstr $qrystr)
$messinst = 'SQL Server instances started' + "`n" +
' SQL Instances: ' + $instances.Tables[0].Rows.count

Log-DebugEvent $EVENT_LEVEL_INFO $messinst $dmPath

if ($instances.Tables[0].Rows.count -lt 1) {
# No instances found so don't create a Property Instanceound"
Log-DebugEvent $DUMMY_PROPERTYBAG 'No monitored SQL Server instances found' $dmPath
exit
} else {
# LOOP ROUND EACH MONITORED SQL INSTANCE ROW

foreach ($row in $instances.Tables[0].Rows)
{
# Logging row values to know if these is or not NULL
# In NULL case, the exception message for MOMClassInstance object is:
# ID: ComMethodTargetInvocation, Message: Exception has been thrown by the target of an invocation.
$message = 'Created SqlInstance class instance' + "`n" + `
' Computer Name : ' + $computerName + "`n" + `
' Instance Name : ' + $row.InstanceName + "`n" + `
' Real Server Name: ' + $row.RealServerName + "`n" + `
' Server Version : ' + $row.ServerVersion + "`n" + `
' Server Edition : ' + $row.ServerEdition + "`n" + `
' Maintenance Mode: ' + $row.MaintenanceModeEnabled

Log-DebugEvent $CLASS_CREATED $message $dmPath


#Create a SqlInstance class instance for each monitored SQL server row we found in the DB.
Log-DebugEvent $EVENT_LEVEL_INFO 'CreateClassInstance: Creates a new monitoring class instance.' $dmPath
$instance = $discoveryData.CreateClassInstance("$MPElement[Name='Idera.SQLDM.SqlInstance']$")

#Since the Idera.SQLDM.SqlInstance class is hosted, we need to provide it's key property and the key properties of any parents.
#The key property of SqlInstance is InstanceName.
#The hosting class for SqlInstance is DmService which has no key property.
#The hosting class for DmService is Windows Computer which has a key property of PrincipalName.
$instance.AddProperty("$MPElement[Name='Windows!Microsoft.Windows.Computer']/PrincipalName$", $computerName)
$instance.AddProperty("$MPElement[Name='Idera.SQLDM.SqlInstance']/InstanceName$", $row.InstanceName)
$instance.AddProperty("$MPElement[Name='Idera.SQLDM.SqlInstance']/RealServerName$", $row.RealServerName)
$instance.AddProperty("$MPElement[Name='Idera.SQLDM.SqlInstance']/ServerVersion$", $row.ServerVersion)
$instance.AddProperty("$MPElement[Name='Idera.SQLDM.SqlInstance']/ServerEdition$", $row.ServerEdition)
$instance.AddProperty("$MPElement[Name='Idera.SQLDM.SqlInstance']/MaintenanceModeEnabled$", $row.MaintenanceModeEnabled)

Log-DebugEvent $EVENT_LEVEL_INFO 'AddInstance: Adds an item to the collection of discovery data.' $dmPath
$discoveryData.AddInstance($instance)
}
}

#Return the discovery data.
$discoveryData

$nams = $instances.Tables[0].Rows | % {$_.InstanceName}
$namstr = $nams -join ', '

# WRAP UP NOW
$message = 'Script ended' + "`n" + `
'Rows found: ' + $instances.Tables[0].Rows.count + "`n" + `
'Instance names: ' + $namstr
Log-DebugEvent $SCRIPT_ENDED $message $dmPath
</Script></ScriptBody>
<Parameters>
<Parameter>
<Name>sourceID</Name>
<Value>$MPElement$</Value>
</Parameter>
<Parameter>
<Name>managedEntityID</Name>
<Value>$Target/Id$</Value>
</Parameter>
<Parameter>
<Name>dmDb</Name>
<Value>$Target/Property[Type="Idera.SQLDM.DMservice"]/DmDatabase$</Value>
</Parameter>
<Parameter>
<Name>computerName</Name>
<Value>$Target/Host/Property[Type="Windows!Microsoft.Windows.Computer"]/PrincipalName$</Value>
</Parameter>
<Parameter>
<Name>dmPath</Name>
<Value>$Target/Property[Type="Idera.SQLDM.DMservice"]/DmPath$</Value>
</Parameter>
</Parameters>
<TimeoutSeconds>120</TimeoutSeconds>
</DataSource>
</Discovery>