Using System Center Operations Manager and PowerShell to monitor…anything–part 3: Discoveries with PowerShell

Add a comment September 6th, 2011

In this part, I’ll describe how to perform discoveries using PowerShell. This is fairly easy, and if you’re able to construct a PowerShell script that will return what you need to discover inside the PowerShell console, then you’re pretty much good to go. Implementing the script in SCOM is pretty straightforward. Note that the native PowerShell support we’re using here was introduced in SCOM2007R2, so if you are still on 2007SP1 (for some reason), then you need to upgrade.

In the previous post in the series I descried the service model of this management pack. Time has come to discover the “Weather Data Location” class. This class will have multiple instances, so I’ve added a few properties to it, marking the attribute “FilePath” as unique.

image

If you don’t have any unique attributes in your multi-instance classes, then SCOM will only discover the first one. As far as I know, DisplayName won’t work, since this attribute is derived from a parent class (System.Entity). You need a unique attribute defined directly on the class itself. The two attributes XMLAddress and FilePath will be picked up by the discovery script. FilePath is pretty much the filename of each file minus the file extension, and XMLAddress is the content of each file.

So, onto the discovery. We’ll construct a “Custom Discovery” in the Authoring Console, using the data source Microsoft.Windows.TimedPowerShell.DiscoveryProvider.

All PowerShell discovery scripts share similar first lines:

<IntervalSeconds>3600</IntervalSeconds>
<SyncTime></SyncTime>
<ScriptName>DiscoverWeatherDataLocations.ps1</ScriptName>
<ScriptBody>
  param($sourceId,$managedEntityId,$computerName,$FilePath)

IntervalSeconds control how often the discovery runs, and SyncTime can be used for “resetting” the interval at a certain time of the day. Scriptname can be pretty much anything, but it should end with .ps1. You should probably also make sure you don’t reuse script names inside the same Management Pack.

Scriptbody is the XML block that will contain the script itself. You NEED at least two parameters, SourceID and ManagedEntityID. SCOM will populate these itself at runtime, and they’re used to keep track of the discovery. In addition I’m putting computername and filepath as parameters. ComputerName is the name of the computer where the discovery runs, and FilePath is derived from the FilePath of the “WeatherData Base” class that we discovered in part 2 of this blog series.

The PowerShell script itself comes next:

<ScriptBody>
   param($sourceId,$managedEntityId,$computerName,$FilePath)

   $api = new-object -comObject ‘MOM.ScriptAPI’
   $discoveryData = $api.CreateDiscoveryData(0, $SourceId, $ManagedEntityId)

   $Files = @()
   $Location = $FilePath
   If (Test-Path $Location)
   {
   $a = Get-ChildItem $Location *.txt
   $Files += $a

   Foreach ($File in $Files)
   {

   $Name = $File.Name
   $DisplayName = $name.TrimEnd(".txt")
   $XMLFile = Get-Content "$Location\$name"
   $FileFullName = $File.Fullname

   $instance = $discoveryData.CreateClassInstance
("$MPElement[Name='TrondMP.WeatherData.AppComponentWeatherLocation']$")
   $instance.AddProperty("$MPElement[Name='Windows!Microsoft.Windows.Computer']/PrincipalName$", $computerName)
   $instance.AddProperty("$MPElement[Name='TrondMP.WeatherData.AppComponentWeatherLocation']/XMLAddress$", $XMLFile)
   $instance.AddProperty("$MPElement[Name='TrondMP.WeatherData.AppComponentWeatherLocation']/FilePath$", $FileFullName)
   $instance.AddProperty("$MPElement[Name="System!System.Entity"]/DisplayName$", $DisplayName)

   $discoveryData.AddInstance($instance)
   }
   }

   $discoveryData

</ScriptBody>

 

As you can see, it’s a reasonably simple script. List all files in the location, and for each file get its content. We need to return the discovered data in a way SCOM understands, and we’re using SCOM’s COM objects for this. Basically, for each instance of the class (each file) we discover, we’re returning a bunch of Properties and a class instance. We’re collecting all these class instances in the variable $discoverydata, which we return to SCOM. That’s pretty much it. Notice that I force an array ($Files) so that even If the folder only contains one file it’s still returned as an array. Otherwise I would have problems doing a foreach loop, and the discovery would probably fail.

Below the ScriptBody tags, I specify how SCOM should send parameters to the script:

  </ScriptBody>
  <Parameters>
    <Parameter>
      <Name>sourceID</Name>
      <Value>$MPElement$</Value>
    </Parameter>
    <Parameter>
      <Name>managedEntityID</Name>
      <Value>$Target/Id$</Value>
    </Parameter>
    <Parameter>
      <Name>computerName</Name>
      <Value>$Target/Host/Property[Type="Windows!Microsoft.Windows.Computer"]/PrincipalName$</Value>
    </Parameter>
    <Parameter>
      <Name>FilePath</Name>
      <Value>$Target/Property[Type="TrondMP.WeatherData.LocalApp.WeatherDataBase"]/FilePath$</Value>
    </Parameter>
  </Parameters>
  <TimeoutSeconds>300</TimeoutSeconds>
</Configuration>

As I noted above, SourceID and ManagedENtityID are handled within SCOM, so you can pretty much copy/paste the above parameters for these, they will not change from discovery to discovery. Next, I also send ComputerName and FilePath as parameters. I would probably not need ComputerName here, but I figured the script might change later on and I might need it for later.

TimeOutSeconds is controls how long SCOM will let the script run before it’s killed. This one should never take more than 1-2 seconds, so 300 might be overly pessimistic, but that’s something I’ll tune down later on.

Also note the Target of the discovery: The “WeatherData Base class”

image

This triggers the discovery for each instance of the WeatherData Base Class found, and lets us send the FilePath of the base class into the discovery script.

You also need to specify the attributes you plan to discover in the script:

image

Here’s the entire discovery class for the “Weather Data Location” class (Copy it into notepad and you should be able to grab the whole thing):

<Discovery ID="TrondMP.WeatherData.Discovery.DiscoverWeatherLocationData" Enabled="true" Target="TrondMP.WeatherData.LocalApp.WeatherDataBase" ConfirmDelivery="true" Remotable="true" Priority="Normal">
        <Category>Discovery</Category>
        <DiscoveryTypes>
          <DiscoveryClass TypeID="TrondMP.WeatherData.AppComponentWeatherLocation">
            <Property TypeID="TrondMP.WeatherData.AppComponentWeatherLocation" PropertyID="XMLAddress" />
            <Property TypeID="TrondMP.WeatherData.AppComponentWeatherLocation" PropertyID="FilePath" />
            <Property TypeID="System!System.Entity" PropertyID="DisplayName" />
          </DiscoveryClass>
        </DiscoveryTypes>
        <DataSource ID="PS" TypeID="Windows!Microsoft.Windows.TimedPowerShell.DiscoveryProvider">
          <IntervalSeconds>3600</IntervalSeconds>
          <SyncTime />
          <ScriptName>DiscoverWeatherDataLocations.ps1</ScriptName>
          <ScriptBody><![CDATA[
    param($sourceId,$managedEntityId,$computerName,$FilePath)
 
    $api = new-object -comObject 'MOM.ScriptAPI'
    $discoveryData = $api.CreateDiscoveryData(0, $SourceId, $ManagedEntityId)
 
    $Files = @()
    $Location = $FilePath
    If (Test-Path $Location)
    {
    $a = Get-ChildItem $Location *.txt
    $Files += $a
 
    Foreach ($File in $Files)
    {
 
    $Name = $File.Name
    $DisplayName = $name.TrimEnd(".txt")
    $XMLFile = Get-Content "$Location\$name"
    $FileFullName = $File.Fullname
 
    $instance = $discoveryData.CreateClassInstance("$MPElement[Name='TrondMP.WeatherData.AppComponentWeatherLocation']$")
    $instance.AddProperty("$MPElement[Name='Windows!Microsoft.Windows.Computer']/PrincipalName$", $computerName)
    $instance.AddProperty("$MPElement[Name='TrondMP.WeatherData.AppComponentWeatherLocation']/XMLAddress$", $XMLFile)
    $instance.AddProperty("$MPElement[Name='TrondMP.WeatherData.AppComponentWeatherLocation']/FilePath$", $FileFullName)
    $instance.AddProperty("$MPElement[Name="System!System.Entity"]/DisplayName$", $DisplayName)
 
    $discoveryData.AddInstance($instance)
    }
    }
 
    $discoveryData
 
 
  ]]></ScriptBody>
          <Parameters>
            <Parameter>
              <Name>sourceID</Name>
              <Value>$MPElement$</Value>
            </Parameter>
            <Parameter>
              <Name>managedEntityID</Name>
              <Value>$Target/Id$</Value>
            </Parameter>
            <Parameter>
              <Name>computerName</Name>
              <Value>$Target/Host/Property[Type="Windows!Microsoft.Windows.Computer"]/PrincipalName$</Value>
            </Parameter>
            <Parameter>
              <Name>FilePath</Name>
              <Value>$Target/Property[Type="TrondMP.WeatherData.LocalApp.WeatherDataBase"]/FilePath$</Value>
            </Parameter>
          </Parameters>
          <TimeoutSeconds>300</TimeoutSeconds>
        </DataSource>
      </Discovery>
  1. No comments yet.Be the first ?
  1. No trackbacks yet.
Comments feed