SCOM 2012 VSAE – Extend Microsoft.Windows.Computer Class with Powershell and Active Directory
Introduction
A lot of usable information like Location, Company or responsible Department are often already stored in Active Directory. So why not using that data to extend the "Microsoft.Windows.Computer" Class by a Custom Class and PowerShell Discovery?
In this example i will you how to create a class with the new properties and also a sample script to discovery data from Active Directory and return it to SCOM.
Note: I will not cover the basics of VSAE, Class design or PowerShell Discovery Script. For that please refer to following articles:
- Visual Studio Authoring Extensions for System Center 2012 - Operations Manager from Brian Wren
- Operations Manager Management Pack Authoring - Classes and Relationships from Brian Wren
- Operations Manager Management Pack Authoring - Discovery Scripts from Brian Wren
Create a Class
First of all we have to create a new class which will contain the properties we discover from Active Directory. As we are extending the "Microsoft.Windows.Computer" Class we use it as base Class for out new one.
In this example we use the following attributes from Active Directory for our Class:
- Location
- Department
- Company
- Division
- Description
Below you can find the Class Management Pack Fragment:
<ManagementPackFragment SchemaVersion="2.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<TypeDefinitions>
<EntityTypes>
<ClassTypes>
<ClassType ID="Windows.Computer_Extended" Accessibility="Public" Abstract="false" Base="Windows!Microsoft.Windows.Computer" Hosted="false" Singleton="false" Extension="false">
<Property ID="Description" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false" Scale="0" />
<Property ID="Company" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false" Scale="0" />
<Property ID="Division" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false" Scale="0" />
<Property ID="Department" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false" Scale="0" />
<Property ID="Location" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false" Scale="0" />
</ClassType>
</ClassTypes>
</EntityTypes>
</TypeDefinitions>
<LanguagePacks>
<LanguagePack ID="ENU" IsDefault="true">
<DisplayStrings>
<DisplayString ElementID="Windows.Computer_Extended">
<Name>Windows.Computer_Extended</Name>
<Description />
</DisplayString>
<DisplayString ElementID="Windows.Computer_Extended" SubElementID="Description">
<Name>Description</Name>
<Description />
</DisplayString>
<DisplayString ElementID="Windows.Computer_Extended" SubElementID="Company">
<Name>Company</Name>
<Description />
</DisplayString>
<DisplayString ElementID="Windows.Computer_Extended" SubElementID="Division">
<Name>Division</Name>
<Description />
</DisplayString>
<DisplayString ElementID="Windows.Computer_Extended" SubElementID="Department">
<Name>Department</Name>
<Description />
</DisplayString>
<DisplayString ElementID="Windows.Computer_Extended" SubElementID="Location">
<Name>Location</Name>
<Description />
</DisplayString>
</DisplayStrings>
</LanguagePack>
</LanguagePacks>
</ManagementPackFragment>
Create the PowerShell Script
We will use the following .NET Class in this the Discovery Script for Active Directory information:
The Script Discover the Computer in Active Directory based on the Domain where the object is a member of and create a new Instance of our custom Class with the new values from Active Directory.
Script Structure
param($sourceId, $managedEntityId, $computerName)
$api = new-object -comObject 'MOM.ScriptAPI'
$DiscoveryData = $api.CreateDiscoveryData(0, $sourceId, $managedEntityId)
$api.LogScriptEvent('ADValuesDiscovery.ps1',20,4,"Starting Active Directory Discovery for Computer: " + $env:ComputerName)
$objDomain = New-Object System.DirectoryServices.DirectoryEntry
$strFilter = "(&(objectCategory=Computer)(Name=$env:ComputerName))"
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
$objSearcher.SearchRoot = $objDomain
$objSearcher.Filter = $strFilter
$colResults = $objSearcher.FindAll()
$ComputerResult = $colResults.Item(0)
$ComputerItem = $ComputerResult.GetDirectoryEntry()
$Description= ($ComputerItem.description).ToString()
$Location = ($ComputerItem.location).ToString()
$Company = ($ComputerItem.company).ToString()
$Department = ($ComputerItem.department).ToString()
$Division = ($ComputerItem.division).ToString()
$instance = $DiscoveryData.CreateClassInstance("$MPElement[Name='Windows.Computer_Extended']$")
$instance.AddProperty(“$MPElement[Name='Windows!Microsoft.Windows.Computer']/PrincipalName$”, $computerName)
$instance.AddProperty("$MPElement[Name='Windows.Computer_Extended']/Description$",$Description)
$instance.AddProperty("$MPElement[Name='Windows.Computer_Extended']/Location$",$Location)
$instance.AddProperty("$MPElement[Name='Windows.Computer_Extended']/Company$",$Company)
$instance.AddProperty("$MPElement[Name='Windows.Computer_Extended']/Department$",$Department)
$instance.AddProperty("$MPElement[Name='Windows.Computer_Extended']/Division$",$Division)
$DiscoveryData.AddInstance($instance)
$DiscoveryData
Script Explanation
In the first section of the script we create the domain object and also the filter we want to use for the search object.
$objDomain = New-Object System.DirectoryServices.DirectoryEntry
$strFilter = "(&(objectCategory=Computer)(Name=$env:ComputerName))"
Followed by this we create a searcher object and configure the settings for it.
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
$objSearcher.SearchRoot = $objDomain
$objSearcher.Filter = $strFilter
In the next step we search for the Computer object and return all values.
$colResults = $objSearcher.FindAll()
$ComputerResult = $colResults.Item(0)
$ComputerItem = $ComputerResult.GetDirectoryEntry()
Now we parse each single values to a variable and convert it to a string.
$Description``=`` ``(``$ComputerItem``.``description``)``.``ToString``(``)
$Location`` ``=`` ``(``$ComputerItem``.``location``)``.``ToString``(``)
$Company`` ``=`` `` (``$ComputerItem``.``company``)``.``ToString``(``)
$Department`` ``=`` ``(``$ComputerItem``.``department``)``.``ToString``(``)
$Division`` ``=`` ``(``$ComputerItem``.``division``)``.``ToString``(``)``
In the last step we create a instance of our class and fill the properties.
$instance = $DiscoveryData.CreateClassInstance("$MPElement[Name='Windows.Computer_Extended']$")
$instance.AddProperty(“$MPElement[Name='Windows!Microsoft.Windows.Computer']/PrincipalName$”, $computerName)
$instance.AddProperty("$MPElement[Name='Windows.Computer_Extended']/Description$",$Description)
$instance.AddProperty("$MPElement[Name='Windows.Computer_Extended']/Location$",$Location)
$instance.AddProperty("$MPElement[Name='Windows.Computer_Extended']/Company$",$Company)
$instance.AddProperty("$MPElement[Name='Windows.Computer_Extended']/Department$",$Department)
$instance.AddProperty("$MPElement[Name='Windows.Computer_Extended']/Division$",$Division)
$DiscoveryData.AddInstance($instance)
$DiscoveryData
Create the Discovery
As we have finished our script we no can create our discovery by using following Data Source:
***"Microsoft.Windows.TimedPowerShell.DiscoveryProvider"
Below you can find the Discovery Management Pack Fragment:
<ManagementPackFragment SchemaVersion="2.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Monitoring>
<Discoveries>
<Discovery ID="Windows.Computer_Extended.Discovery" Enabled="true" Target="Windows!Microsoft.Windows.Computer" ConfirmDelivery="false" Remotable="true" Priority="Normal">
<Category>Discovery</Category>
<DiscoveryTypes>
<DiscoveryClass TypeID="Windows.Computer_Extended">
<Property PropertyID="Description" />
<Property PropertyID="Location" />
<Property PropertyID="Company" />
<Property PropertyID="Department" />
<Property PropertyID="Division" />
<Property TypeID="System!System.ConfigItem" PropertyID="ObjectStatus" />
<Property TypeID="System!System.ConfigItem" PropertyID="AssetStatus" />
<Property TypeID="System!System.ConfigItem" PropertyID="Notes" />
<Property TypeID="System!System.Entity" PropertyID="DisplayName" />
</DiscoveryClass>
</DiscoveryTypes>
<DataSource ID="DS" TypeID="Windows!Microsoft.Windows.TimedPowerShell.DiscoveryProvider">
<IntervalSeconds>600</IntervalSeconds>
<SyncTime />
<ScriptName>TestDiscovery.ps1</ScriptName>
<ScriptBody>
<![CDATA[
param($sourceId, $managedEntityId, $computerName)
$api = new-object -comObject 'MOM.ScriptAPI'
$DiscoveryData = $api.CreateDiscoveryData(0, $sourceId, $managedEntityId)
$api.LogScriptEvent('ADValuesDiscovery.ps1',20,4,"Starting Active Directory Discovery for Computer: " + $env:ComputerName)
$objDomain = New-Object System.DirectoryServices.DirectoryEntry
$strFilter = "(&(objectCategory=Computer)(Name=$env:ComputerName))"
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
$objSearcher.SearchRoot = $objDomain
$objSearcher.Filter = $strFilter
$colResults = $objSearcher.FindAll()
$ComputerResult = $colResults.Item(0)
$ComputerItem = $ComputerResult.GetDirectoryEntry()
$Description = ($ComputerItem.description).ToString()
$Location = ($ComputerItem.location).ToString()
$Company = ($ComputerItem.company).ToString()
$Department = ($ComputerItem.department).ToString()
$Division = ($ComputerItem.division).ToString()
$instance = $DiscoveryData.CreateClassInstance("$MPElement[Name='Windows.Computer_Extended']$")
$instance.AddProperty(“$MPElement[Name='Windows!Microsoft.Windows.Computer']/PrincipalName$”, $computerName)
$instance.AddProperty("$MPElement[Name='Windows.Computer_Extended']/Description$",$Description)
$instance.AddProperty("$MPElement[Name='Windows.Computer_Extended']/Location$",$Location)
$instance.AddProperty("$MPElement[Name='Windows.Computer_Extended']/Company$",$Company)
$instance.AddProperty("$MPElement[Name='Windows.Computer_Extended']/Department$",$Department)
$instance.AddProperty("$MPElement[Name='Windows.Computer_Extended']/Division$",$Division)
$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/Property[Type="Windows!Microsoft.Windows.Computer"]/PrincipalName$</Value>
</Parameter>
</Parameters>
<TimeoutSeconds>300</TimeoutSeconds>
<StrictErrorHandling>false</StrictErrorHandling>
</DataSource>
</Discovery>
</Discoveries>
</Monitoring>
<LanguagePacks>
<LanguagePack ID="ENU" IsDefault="true">
<DisplayStrings>
<DisplayString ElementID="Windows.Computer_Extended.Discovery">
<Name>Windows.Computer_Extended - Discovery</Name>
<Description />
</DisplayString>
</DisplayStrings>
</LanguagePack>
</LanguagePacks>
</ManagementPackFragment>
Build and Import the Management Pack
After you imported the Management Pack you can check if our new Class is discovered successfully under Monitoring – Discovered Inventory by changing the target type to the correct Class.
Download XML and Visual Studio 2012 Solution here.