JAMA00

SCOM 2007 R2

Archive for the ‘grouping and scoping’ Category

Microsoft Exchange Server 2013 discovery.

Posted by rob1974 on May 13, 2014

The Microsoft Exchange Server management pack has again some big changes. Loads of people already blogged about that. Personally i like the simplicity of it and the fact that Exchange Admins are in control a.k.a. they don’t bother me anymore. However, Windows servers admins started to bother me with script errors related to the Exchange Server 2013 Discovery on servers that didn’t run Exchange 2013 in the first place.

After some investigation it turns out the discovery for Microsoft Exchange 2013 is a powershell script, which is targeted to the windows computer class. This is where it goes wrong. When you are monitoring Windows 2003 or Windows 2008 server chances are you don’t have powershell installed on those servers. Furthermore, why is the Exchange 2013 Discovery running on those servers as it’s a not supported OS for Exchange Server 2013.

So easy enough, i decided to override the discovery for Windows 2003. Simply choosing override for a group, select the Windows 2003 computer group and set the “enabled” value to false. Job done.

Now I wanted to disable the discovery for the Windows 2008 servers as, but not for the Windows 2008 R2 computer. Windows 2008 R2 is a supported OS for Exchange 2013, besides powershell is installed by default so there’s no issue here. The discovery will run and return nothing (or not an exchange server) if exchange isn’t installed. It won’t return a script error because there’s no powershell.

The Windows 2008 computer group in the Windows Server 2008 (discovery) management pack contains also the Windows 2008 R2 computers, so it’s not so easy as with Windows Server 2003. I needed to create a Windows Server 2008 Group which doesn’t contain Windows 2008 R2 server.

Luckily I remembered a blogpost by Kevin Holman about creating a group with computers not containing computers in another group (btw glad he’s back on the support front, I really missed those deep dives in SCOM). I created a new group, edited the xml and set the override. The only difference between my group exclusion and Kevin Holman’s group is I use a reference to another MP in the “notcontained” section as i use the “Microsoft Windows Server 2008 R2 Computer Group” which already exists in the Windows Server 2008 (discovery) mp. This means the reference to that mp needs to be included in the xml below.

The result is here. Save this as ValueBlueOverrideMicrosoft.Exchange.Server.xml (remove the “windows server 2003” override and reference to “Microsoft.Windows.Server.2003” if you don’t run this anymore):

<?xml version=”1.0″ encoding=”utf-8″?><ManagementPack ContentReadable=”true” xmlns:xsd=”http://www.w3.org/2001/XMLSchema” xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”>
<Manifest>
<Identity>
<ID>ValueBlueOverrideMicrosoft.Exchange.Server</ID>
<Version>1.0.1.0</Version>
</Identity>
<Name>ValueBlueOverrideMicrosoft Exchange Server 2013</Name>
<References>
<Reference Alias=”Exchange”>
<ID>Microsoft.Exchange.15</ID>
<Version>15.0.620.18</Version>
<PublicKeyToken>31bf3856ad364e35</PublicKeyToken>
</Reference>
<Reference Alias=”MicrosoftSystemCenterInstanceGroupLibrary6172210″>
<ID>Microsoft.SystemCenter.InstanceGroup.Library</ID>
<Version>6.1.7221.0</Version>
<PublicKeyToken>31bf3856ad364e35</PublicKeyToken>
</Reference>
<Reference Alias=”SystemCenter”>
<ID>Microsoft.SystemCenter.Library</ID>
<Version>6.1.7221.81</Version>
<PublicKeyToken>31bf3856ad364e35</PublicKeyToken>
</Reference>
<Reference Alias=”MicrosoftWindowsServer2008Discovery6066670″>
<ID>Microsoft.Windows.Server.2008.Discovery</ID>
<Version>6.0.6667.0</Version>
<PublicKeyToken>31bf3856ad364e35</PublicKeyToken>
</Reference>
<Reference Alias=”Windows”>
<ID>Microsoft.Windows.Server.2003</ID>
<Version>6.0.6667.0</Version>
<PublicKeyToken>31bf3856ad364e35</PublicKeyToken>
</Reference>
</References>
</Manifest>
<TypeDefinitions>
<EntityTypes>
<ClassTypes>
<ClassType ID=”ValueBlue.Microsoft.Server.2008.Only.Group” Accessibility=”Public” Abstract=”false” Base=”MicrosoftSystemCenterInstanceGroupLibrary6172210!Microsoft.SystemCenter.InstanceGroup” Hosted=”false” Singleton=”true” />
</ClassTypes>
</EntityTypes>
</TypeDefinitions>
<Monitoring>
<Discoveries>
<Discovery ID=”ValueBlue.Microsoft.Server.2008.Only.Group.DiscoveryRule” Enabled=”true” Target=”ValueBlue.Microsoft.Server.2008.Only.Group” ConfirmDelivery=”false” Remotable=”true” Priority=”Normal”>
<Category>Discovery</Category>
<DiscoveryTypes>
<DiscoveryRelationship TypeID=”MicrosoftSystemCenterInstanceGroupLibrary6172210!Microsoft.SystemCenter.InstanceGroupContainsEntities” />
</DiscoveryTypes>
<DataSource ID=”GroupPopulationDataSource” TypeID=”SystemCenter!Microsoft.SystemCenter.GroupPopulator”>
<RuleId>$MPElement$</RuleId>
<GroupInstanceId>$MPElement[Name=”ValueBlue.Microsoft.Server.2008.Only.Group”]$</GroupInstanceId>
<MembershipRules>
<MembershipRule>
<MonitoringClass>$MPElement[Name=”MicrosoftWindowsServer2008Discovery6066670!Microsoft.Windows.Server.2008.Computer”]$</MonitoringClass>
<RelationshipClass>$MPElement[Name=”MicrosoftSystemCenterInstanceGroupLibrary6172210!Microsoft.SystemCenter.InstanceGroupContainsEntities”]$</RelationshipClass>
<Expression>
<NotContained>
              <MonitoringClass>$MPElement[Name=”MicrosoftWindowsServer2008Discovery6066670!Microsoft.Windows.Server.2008.R2.ComputerGroup”]$</MonitoringClass>
</NotContained>
</Expression>
</MembershipRule>
</MembershipRules>
</DataSource>
</Discovery>
</Discoveries>
<Overrides>
<DiscoveryPropertyOverride ID=”OverrideForDiscoveryMicrosoftExchange15ServerDiscoveryRuleForContextMicrosoftWindowsServer2003ComputerGroupd5787b8329934b19ba24ca637d805307″ Context=”Windows!Microsoft.Windows.Server.2003.ComputerGroup” ContextInstance=”cb87057c-606b-43e7-e861-8e5a0df201f6″ Enforced=”false” Discovery=”Exchange!Microsoft.Exchange.15.Server.DiscoveryRule” Property=”Enabled”>
<Value>false</Value>
</DiscoveryPropertyOverride>
<DiscoveryPropertyOverride ID=”OverrideForDiscoveryMicrosoftExchange15ServerDiscoveryRuleForContextValueBlue.Microsoft.Server.2008.OnlyGroup4700b619d2184cb2af12302026deee09″ Context=”ValueBlue.Microsoft.Server.2008.Only.Group” ContextInstance=”4cc2c8a2-918a-2ec3-f05e-fa1042b4a4db” Enforced=”false” Discovery=”Exchange!Microsoft.Exchange.15.Server.DiscoveryRule” Property=”Enabled”>
<Value>false</Value>
</DiscoveryPropertyOverride>
</Overrides>
</Monitoring>
<LanguagePacks>
<LanguagePack ID=”NLD” IsDefault=”false”>
<DisplayStrings>
<DisplayString ElementID=”ValueBlueOverrideMicrosoft.Exchange.Server”>
<Name>ValueBlueOverrideMicrosoft Exchange Server 2013</Name>
</DisplayString>
<DisplayString ElementID=”ValueBlue.Microsoft.Server.2008.Only.Group”>
<Name>ValueBlueWindows 2008 servers only (no windows 2008 r2 servers)</Name>
</DisplayString>
<DisplayString ElementID=”ValueBlue.Microsoft.Server.2008.Only.Group.DiscoveryRule”>
<Name>Populate ValueBlueWindows 2008 servers only (no windows 2008 r2 servers)</Name>
<Description>This discovery rule populates the group ‘ValueBlueWindows 2008 servers only (no windows 2008 r2 servers)'</Description>
</DisplayString>
</DisplayStrings>
</LanguagePack>
<LanguagePack ID=”ENU” IsDefault=”false”>
<DisplayStrings>
<DisplayString ElementID=”ValueBlueOverrideMicrosoft.Exchange.Server”>
<Name>ValueBlueOverrideMicrosoft Exchange Server 2013</Name>
</DisplayString>
<DisplayString ElementID=”ValueBlue.Microsoft.Server.2008.Only.Group”>
<Name>ValueBlueWindows 2008 servers only (no windows 2008 r2 servers)</Name>
</DisplayString>
<DisplayString ElementID=”ValueBlue.Microsoft.Server.2008.Only.Group.DiscoveryRule”>
<Name>Populate ValueBlueWindows 2008 servers only (no windows 2008 r2 servers)</Name>
<Description>This discovery rule populates the group ‘ValueBlueWindows 2008 servers only (no windows 2008 r2 servers)'</Description>
</DisplayString>
</DisplayStrings>
</LanguagePack>
</LanguagePacks>
</ManagementPack>

Posted in grouping and scoping, management packs, troubleshooting | Tagged: , , , | 2 Comments »

Targeting rules and customers

Posted by MarcKlaver on May 6, 2010

We are managing several customers with one management group and one management environment. Some rules are very customer specific and we wanted to prevent rules specific for customer A being visible at customer B (and all other customers).

To accomplish this, we tried this:

  • We targeted a disabled rule to the windows computer object and enabled the rule for targets specific for customer A (using our computer attributes we also use for customer group population, see also this link). Unfortunately, this didn’t work. The rule ended up at all windows computer targets, regardless of the rule being enabled or disabled for the target. 
  • Next we tried a new management pack, with only disabled rules. Again the rules were distributed over all customers.

So our conclusion is that the agent itself determines if a rule is enabled/disabled, not the RMS. The RMS only checks if the agent has a target for a rule/mp.

To prevent the rules from being distributed to customer B (and all other customers), we needed to be able to target only to computers for customer A. The only option we found was to create a class to target computers at a customer level. Again we used our registry key we also use for the group population. But this time it is used to discover a class, specific to the customer.

We decided that it wasn’t a problem that a customer specific rule was available on all servers of a single customer, so we created one class for each customer:

image 

or in XML:

<TypeDefinitions>
  <EntityTypes>
    <ClassTypes>
      <ClassType ID="jama.CustomerId.CustomerA" Accessibility="Public" Abstract="false" Base="Windows!Microsoft.Windows.ComputerRole" Hosted="true" Singleton="false"/>
      <ClassType ID="jama.CustomerId.CustomerB" Accessibility="Public" Abstract="false" Base="Windows!Microsoft.Windows.ComputerRole" Hosted="true" Singleton="false"/>
      <ClassType ID="jama.CustomerId.CustomerC" Accessibility="Public" Abstract="false" Base="Windows!Microsoft.Windows.ComputerRole" Hosted="true" Singleton="false"/>
      <ClassType ID="jama.CustomerId.CustomerD" Accessibility="Public" Abstract="false" Base="Windows!Microsoft.Windows.ComputerRole" Hosted="true" Singleton="false"/>
    </ClassTypes>
  </EntityTypes>
</TypeDefinitions>

Next we created a discovery, to discover the “customer” classes:

image

The discovery will discover any customer class we have defined:

image

or in XML:

<DiscoveryTypes>
  <DiscoveryClass TypeID="jama.CustomerId.CustomerA" />
  <DiscoveryClass TypeID="jama.CustomerId.CustomerB" />
  <DiscoveryClass TypeID="jama.CustomerId.CustomerC" />
  <DiscoveryClass TypeID="jama.CustomerId.CustomerD" />
</DiscoveryTypes>

The discovery itself is done with a script. Originally I wanted to create this code:

strCustomerId = ucase(jamaGetAttribute(STR_ATTRIBUTE_CUSTOMER_ID)) ‘ retrieve customer id
if(strCustomerId <> "") then
    set g_objClass = g_objDiscoveryData.CreateClassInstance("$MPElement[Name=’jama.CustomerId." & strCustomerId & "’]$")
end if

But the authoring console didn’t like that (never checked it with the operator console, but I assume the import will fail also), because it didn’t know anything about the element “jama.CustomerId." & strCustomerId” 🙂 When checking the management pack, it also wants to resolve all “MPElement” names in the scripts, but it didn’t use the variables.

So I now created a switch statement, for all customer names in the management pack:

strCustomerId = ucase(jamaGetAttribute(STR_ATTRIBUTE_CUSTOMER_ID))

if(strCustomerId <> "") then 
     select case strCustomerId
         case "CUSTOMERA" set g_objClass = g_objDiscoveryData.CreateClassInstance("$MPElement[Name=’jama.CustomerId.CustomerA’]$")
         case "CUSTOMERB" set g_objClass = g_objDiscoveryData.CreateClassInstance("$MPElement[Name=’jama.CustomerId.CustomerB’]$")
         case "CUSTOMERC" set g_objClass = g_objDiscoveryData.CreateClassInstance("$MPElement[Name=’jama.CustomerId.CustomerC’]$")
         case "CUSTOMERD" set g_objClass = g_objDiscoveryData.CreateClassInstance("$MPElement[Name=’jama.CustomerId.CustomerD’]$")
     end select

    g_objClass.AddProperty "$MPElement[Name=’Windows!Microsoft.Windows.Computer’]/PrincipalName$", "$Target/Property[Type=’Windows!Microsoft.Windows.Computer’]/PrincipalName$"

    g_objClass.AddProperty "$MPElement[Name=’System!System.Entity’]/DisplayName$", "$Target/Property[Type=’Windows!Microsoft.Windows.Computer’]/PrincipalName$"
     g_objDiscoveryData.AddInstance(g_objClass)
end if

So when all combined, the final xml file will result in this:

<?xml version="1.0" encoding="utf-8"?><ManagementPack ContentReadable="true" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <Manifest>
    <Identity>
      <ID>jamaClass</ID>
      <Version>1.0.0.0</Version>
    </Identity>
    <Name>jamaClass</Name>
    <References>
      <Reference Alias="System">
        <ID>System.Library</ID>
        <Version>6.0.6278.0</Version>
        <PublicKeyToken>31bf3856ad364e35</PublicKeyToken>
      </Reference>
      <Reference Alias="Windows">
        <ID>Microsoft.Windows.Library</ID>
        <Version>6.0.6278.0</Version>
        <PublicKeyToken>31bf3856ad364e35</PublicKeyToken>
      </Reference>
    </References>
  </Manifest>
  <TypeDefinitions>
    <EntityTypes>
      <ClassTypes>
        <ClassType ID="jama.CustomerId.CustomerA" Accessibility="Public" Abstract="false" Base="Windows!Microsoft.Windows.ComputerRole" Hosted="true" Singleton="false"/>
        <ClassType ID="jama.CustomerId.CustomerB" Accessibility="Public" Abstract="false" Base="Windows!Microsoft.Windows.ComputerRole" Hosted="true" Singleton="false"/>
        <ClassType ID="jama.CustomerId.CustomerC" Accessibility="Public" Abstract="false" Base="Windows!Microsoft.Windows.ComputerRole" Hosted="true" Singleton="false"/>
        <ClassType ID="jama.CustomerId.CustomerD" Accessibility="Public" Abstract="false" Base="Windows!Microsoft.Windows.ComputerRole" Hosted="true" Singleton="false"/>
      </ClassTypes>
    </EntityTypes>
  </TypeDefinitions>
  <Monitoring>
    <Discoveries>
       <Discovery ID="jama.CustomerId.Server.Discovery" Enabled="true" Target="Windows!Microsoft.Windows.Server.Computer" ConfirmDelivery="false" Remotable="true" Priority="Normal">
         <Category>Discovery</Category>
           <DiscoveryTypes>
             <DiscoveryClass TypeID="jama.CustomerId.CustomerA" />
             <DiscoveryClass TypeID="jama.CustomerId.CustomerB" />
             <DiscoveryClass TypeID="jama.CustomerId.CustomerC" />
             <DiscoveryClass TypeID="jama.CustomerId.CustomerD" />
           </DiscoveryTypes>
           <DataSource ID="DS" TypeID="Windows!Microsoft.Windows.TimedScript.DiscoveryProvider">
             <IntervalSeconds>86400</IntervalSeconds>
             <SyncTime />
             <ScriptName>jama.CustomerId.Server.Discovery.vbs</ScriptName>
             <Arguments />
             <ScriptBody><![CDATA[‘
option explicit
setLocale("en-us")
on error goto 0

const EVENT_ID_FOUND_CLASS   = 110

const STR_ATTRIBUTE_CUSTOMER_ID = "customer_id"
const HKLM                      = &H80000002
const STR_ATTRIBUTE_KEY         = "SOFTWARE\jama00\OpsMgr2007\attributes"

dim g_objAPI
dim g_objClass
dim g_objDiscoveryData

jamaInitialize()
jamaCreateDiscovery()
g_objAPI.Return(g_objDiscoveryData)

function jamaInitialize()
    set g_objAPI           = CreateObject("MOM.ScriptAPI")
    set g_objDiscoveryData = g_objAPI.CreateDiscoveryData(0, "$MPElement$", "$Target/Id$")
end function

function jamaCreateDiscovery()
    dim strCustomerId

   strCustomerId = ucase(jamaGetAttribute(STR_ATTRIBUTE_CUSTOMER_ID))
    if(strCustomerId <> "") then
        jamaWriteLog EVENT_ID_FOUND_CLASS, "Discoverd class: jama.CustomerId." & strCustomerId
        select case strCustomerId
            case "CUSTOMERA" set g_objClass = g_objDiscoveryData.CreateClassInstance("$MPElement[Name=’jama.CustomerId.CustomerA’]$")
            case "CUSTOMERB" set g_objClass = g_objDiscoveryData.CreateClassInstance("$MPElement[Name=’jama.CustomerId.CustomerB’]$")
            case "CUSTOMERC" set g_objClass = g_objDiscoveryData.CreateClassInstance("$MPElement[Name=’jama.CustomerId.CustomerC’]$")
            case "CUSTOMERD" set g_objClass = g_objDiscoveryData.CreateClassInstance("$MPElement[Name=’jama.CustomerId.CustomerD’]$")
        end select
        g_objClass.AddProperty "$MPElement[Name=’Windows!Microsoft.Windows.Computer’]/PrincipalName$", "$Target/Property[Type=’Windows!Microsoft.Windows.Computer’]/PrincipalName$"
        g_objClass.AddProperty "$MPElement[Name=’System!System.Entity’]/DisplayName$", "$Target/Property[Type=’Windows!Microsoft.Windows.Computer’]/PrincipalName$"
        g_objDiscoveryData.AddInstance(g_objClass)
    end if
end function

function jamaGetAttribute(byval strAttribute)
    dim strResult
    dim objReg

    strResult = ""
    on error resume next
        err.clear
        set objReg=GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\default:StdRegProv")
        if(err.number <> 0) then
            strResult = ""
        else
            objReg.GetStringValue HKLM, STR_ATTRIBUTE_KEY, strAttribute, strResult
            if(err.number <> 0) then
                strResult = ""
            elseif(isnull(strResult)) then
                strResult = ""
            end if
        end if
    on error goto 0

    jamaGetAttribute = strResult
end function

function jamaWriteLog(iEventId, strMsg)
    dim bEnabled : bEnabled = true      ‘ Set to false to disable logging.

    if(bEnabled) then
        dim objAPI
        dim strTemp

        set objAPI = CreateObject("MOM.ScriptAPI")
        if((iEventId >= 100) and (iEventId < 20000)) then
            call objAPI.LogScriptEvent(wscript.scriptname, iEventId, 4, strMsg)
        else
            strTemp = "Out of range event id found: " & cstr(iEventId) & ". Original message: " & strMsg
            call objAPI.LogScriptEvent(wscript.scriptname, 0, 4, strTemp)
        end if
    end if
end function
]]></ScriptBody>
          <TimeoutSeconds>300</TimeoutSeconds>
        </DataSource>
      </Discovery>
    </Discoveries>
  </Monitoring>
  <LanguagePacks>
    <LanguagePack ID="ENU" IsDefault="false">
      <DisplayStrings>
        <DisplayString ElementID="jamaClass">
          <Name>jamaClass</Name>
          <Description>Agent version: 3.1.0  build 442 </Description>
        </DisplayString>
        <DisplayString ElementID="jama.CustomerId.Server.Discovery">
          <Name>jama.CustomerId.Server.Discovery (name)</Name>
          <Description>jama.CustomerId.Server.Discovery (description)</Description>
        </DisplayString>
        <DisplayString ElementID="jama.CustomerId.CustomerA">
          <Name>jama.CustomerId.CustomerA (name)</Name>
          <Description>jama.CustomerId.CustomerA (description)</Description>
        </DisplayString>
        <DisplayString ElementID="jama.CustomerId.CustomerB">
          <Name>jama.CustomerId.CustomerB (name)</Name>
          <Description>jama.CustomerId.CustomerB (description)</Description>
        </DisplayString>
        <DisplayString ElementID="jama.CustomerId.CustomerC">
          <Name>jama.CustomerId.CustomerC (name)</Name>
          <Description>jama.CustomerId.CustomerC (description)</Description>
        </DisplayString>
        <DisplayString ElementID="jama.CustomerId.CustomerD">
          <Name>jama.CustomerId.CustomerD (name)</Name>
          <Description>jama.CustomerId.CustomerD (description)</Description>
        </DisplayString>
      </DisplayStrings>
    </LanguagePack>
  </LanguagePacks>
</ManagementPack>

And this must be adjusted for every new customer we define in our environment and yes this is fully scripted 🙂 The last thing to do is to seal the management pack, so we can use it in other management packs. This is done with the mpseal.exe from the installation CD:

 

MPSeal.exe jamaClass.xml /I .\mp /keyfile privatekey_JAMA.snk /company "JAMA" /outdir .\sealed

Note the “/I .\mp” option. I have created a subdirectory with all referenced management packs called “mp”. You must supply the mpseal.exe with the referenced management packs before it is able to seal the management pack. The file “privatekey_JAMA.snk” contains our private key we use for sealing all our management packs. The final result will be stored in the “sealed” sub directory.

 

So now I finally can select a target that only exists at a customer site:

image

Default disabling the rule, will give you the possibility to override the rule for one or just a few servers within the customer targeted (CustomerC in this example) without distributing the rule(s) to other customers.

Posted in grouping and scoping, management packs | 2 Comments »

Creating computergroups Part 2

Posted by rob1974 on April 21, 2010

In this article i described how to generate the xml code for groups based on a registry property. Allthough it works fine we did run into some new insights. Previously we decided not to seal the mp and we didn’t have nesting of groups because we thought it wasn’t possible with generating the xml.

You probably need to read the previous article as well to understand how to group based on a reg property and then script this. This is just a supplement on that part.

Sealing the group xml

The primary reason for not sealing the xml was the ability to remove groups with upgrades of the xml. Once you seal it and a group can be removed an upgrade will just fail as some id doesn’t exist anymore. This is actually the strength of sealing, but in our case not very handy.

However, leaving the xml unsealed we aren’t able to set overrides based on those groups (e.g. for 1 customer only), unless we would save the override in the same mp. That won’t be very handy as we generate the xml again when a new group is needed and of course you don’t want hundreds of overrides to different monitors and rules in the same mp.

The alternative would be to recreate a group manually in the same mp as the override. But this could create a lot of groups that already exist. Besides group discovery does impact the rms, so cutting down on groups where possible is something to strife for.

The solution for unneeded groups is not to delete them but set the discovery to disabled. We’ve also removed the xml code for fulling the group. So even if it was to be enabled it would be an empty group.

<Discovery ID=”jamaGroupCUSTOMERSUB1SUB2.Group.DiscoveryRule” Enabled=”false” Target=”jamaGroupCUSTOMERSUB1SUB2.Group” ConfirmDelivery=”false” Remotable=”true” Priority=”Normal”>
  <Category>Discovery</Category>
  <DiscoveryTypes>
    <DiscoveryRelationship TypeID=”MSSCIGL!Microsoft.SystemCenter.InstanceGroupContainsEntities” />
  </DiscoveryTypes>
  <DataSource ID=”GroupPopulationDataSource” TypeID=”MSSCL!Microsoft.SystemCenter.GroupPopulator”>
    <RuleId>$MPElement$</RuleId>
    <GroupInstanceId>$MPElement[Name=”jamaGroupCUSTOMERSUB1SUB2.Group”]$</GroupInstanceId>
    <MembershipRules>
      <MembershipRule Comment=”Obsolete_Group_CUSTOMERSUB1SUB2″>
        <MonitoringClass>$MPElement[Name=”MSSCL!Microsoft.SystemCenter.AllComputersGroup”]$</MonitoringClass>
        <RelationshipClass>$MPElement[Name=”MSSCIGL!Microsoft.SystemCenter.InstanceGroupContainsEntities”]$</RelationshipClass>
        <Expression>
          <SimpleExpression>
            <ValueExpression>
              <Value>True</Value>
            </ValueExpression>
            <Operator>Equal</Operator>
            <ValueExpression>
              <Value>False</Value>
            </ValueExpression>
          </SimpleExpression>
        </Expression>
      </MembershipRule>
    </MembershipRules>
  </DataSource>
</Discovery>

The red “false” is to disable group discovery. in your console you’ll see this as an unchecked box:

nodiscovery

In blue “true equals false” is the code to generate an empty group. It still strikes me a bit as funny, but it is effective.

The rest of the necessary xml parts should remain the same. But you can change the displayname in the languagepacks part.

<DisplayString ElementID=”jamaGroupCUSTOMERSUB1SUB2.Group”>
  <Name>obsolete group customer-sub1-sub2</Name>
</DisplayString>

The display name can be set to whatever you like, but changing the elementid would result in a failure to upgrade the mp.

Sealing of the generated xml isn’t hard. Either use the commandline tool or load your creating xml in the (r2) authoring console and save as > sealed. Just remember the path to the sealkey can’t have a space in it or it will fail :).

Adding nested groups

As explained in the previous article you can’t use nested groups when you generate the xml code. The reason was that nesting works with an id and the id isn’t known before importing the xml. But, there’s another way.

The trick is to include the groups as dynamic members and not to use subgroups (the dns mp uses the same principle).

image

When you’ll go to the xml it will look something like the code below. For each subgroup you’ll add in this way, you’ll need to repeat the membershiprule in blue.

<Discovery ID=”jamaGroupCUSTOMER.Group.DiscoveryRule” Enabled=”true” Target=”jamaGroupCUSTOMER.Group” ConfirmDelivery=”false” Remotable=”true” Priority=”Normal”>
  <Category>Discovery</Category>
  <DiscoveryTypes>
    <DiscoveryRelationship TypeID=”MSSCIGL!Microsoft.SystemCenter.InstanceGroupContainsEntities” />
  </DiscoveryTypes>
  <DataSource ID=”GroupPopulationDataSource” TypeID=”MSSCL!Microsoft.SystemCenter.GroupPopulator”>
    <RuleId>$MPElement$</RuleId>
    <GroupInstanceId>$MPElement[Name=”jamaGroupCUSTOMER.Group”]$</GroupInstanceId>
    <MembershipRules>
      <MembershipRule>
        <MonitoringClass>$MPElement[Name=”jamaGroupCUSTOMERSUB1.Group”]$</MonitoringClass>
          <RelationshipClass>$MPElement[Name=”MSSCIGL!Microsoft.SystemCenter.InstanceGroupContainsEntities”]$</RelationshipClass>
            <IncludeList>
              <MonitoringObjectId>$MPElement[Name=”jamaGroupCUSTOMERSUB1.Group”]$</MonitoringObjectId>
            </IncludeList>
       </MembershipRule>
    </MembershipRules>
  </DataSource>
</Discovery>

 

Create the xml

Unfortunately i can’t give the full vbs code to generate the xml. allthough it’s probably needs adjusting anyway. But here’s my main function which hopefully helps a bit when you need to create your own script.

sub main
‘ calls several functions to find the appropriate groupnames to be generated.
‘ calls several functions to actually write the xml code

    Dim arrRawGroups
    Dim arrTopGroups
    Dim arrSubGroups
    Dim arrEndGroups
    Dim arrObsoleteGroups
    Dim strINIFile
    dim strObsoleteGroup

    strINIFile = findini(“ini”)
    arrRawGroups = readINIFile(strINIFile)
    arrTopGroups = findTopLevelGroups(arrRawGroups)
    arrSubGroups = findSubLevelGroups(arrRawGroups)
    arrEndGroups = findEndLevelGroups(arrRawGroups)

    arrObsoleteGroups = findObsoleteGroups(strINIFile, arrTopGroups, arrSubGroups)

    createMP
    createManifest INTALIASQUANTITY
    createTypeDefinitions arrTopGroups, arrSubGroups, arrEndGroups, arrObsoleteGroups
    createmonitoring arrTopGroups, arrSubGroups, arrEndGroups, arrObsoleteGroups
    createlanguagepacks arrTopGroups, arrSubGroups, arrEndGroups, arrObsoleteGroups
    closemp
    wscript.echo “DONE!”
end sub

Posted in grouping and scoping | 1 Comment »

Creating computergroups

Posted by rob1974 on December 9, 2009

Our environment consist of 60+ forests over 60+ customers. These customers need to be seperated from each other as the administrators of these customers are in different teams. This post explains our approach to automate as much as possible.

We started off with scripting an manual installed agent. The manual installation will take care of all the settings we need to create groups based on a registry setting as well as the certificate and the connection to the management servers itself.

During installation an engineer would have to supply a customer string. The customer string is of the form customer_subgroup1[_subgroup2] where Subgroup2 is optional. This string is placed in a registrykey (called customername) as well as a customer value derived from the supply string (called customerid). The customer id was created to give the reporting guys the option to find all servers from 1 customer easily, but it has use in our approach of creating groups too.

creating discoveries

First of all, we created discoveries for the registry keys we needed. We just creating this from the console as properties inherited from windowscomputer. After creation we exported the management pack and sealed it and reimported it.  The management pack has been called jamaAttributes. Sealing is needed for using the discovered values in another management pack (sealing can be done from within the R2 authoring console).

This is what the discovery looks like for customername:

<Discovery ID="jamaAD_MicrosoftWindowsComputer_CustomerNameDiscovery" Enabled="true" Target="MicrosoftWindowsLibrary6172210!Microsoft.Windows.Computer" ConfirmDelivery="false" Remotable="true" Priority="Normal"> 
<Category>PerformanceCollection</Category> 
<DiscoveryTypes> 
<DiscoveryClass TypeID="jamaType_WindowsComputer_jamaAttributes"> 
<Property TypeID="jamaType_WindowsComputer_jamaAttributes" PropertyID="jamaProperty_JamaTypeWindowsComputerJamaAttributes_CustomerName" />  </DiscoveryClass> 
</DiscoveryTypes> 
<DataSource ID="AttributeDiscoveryGeneratedByUI828e02077e6d480b9f104cd757ece492" TypeID="MicrosoftWindowsLibrary6172210!Microsoft.Windows.RegistryDiscoverySingleProvider">
    <ComputerName>$Target/Property[Type="MicrosoftWindowsLibrary6172210!Microsoft.Windows.Computer"]/NetworkName$</ComputerName>

<AttributeName>AttributeDiscoveryRulef5afc3f6856842e69c6b078c940f7f25</AttributeName>     <Path>SOFTWARE\jama\OpsMgr2007\attributes\customer_name</Path> 
<PathType>1</PathType> 
<AttributeType>1</AttributeType> 
<Frequency>86400</Frequency> 
<ClassId>$MPElement[Name="jamaType_WindowsComputer_jamaAttributes"]$</ClassId> 
<InstanceSettings> 
<Settings> 
<Setting> 
<Name>$MPElement[Name="jamaType_WindowsComputer_jamaAttributes"]

/jamaProperty_JamaTypeWindowsComputerJamaAttributes_CustomerName$</Name> 
<Value>$Data/Values/AttributeDiscoveryRulef5afc3f6856842e69c6b078c940f7f25$</Value> 
</Setting> 
<Setting> 
<Name>$MPElement[Name="MicrosoftWindowsLibrary6172210!Microsoft.Windows.Computer"]/PrincipalName$</Name> 
<Value>$Target/Property[Type="MicrosoftWindowsLibrary6172210!Microsoft.Windows.Computer"]/PrincipalName$</Value> 
</Setting> 
</Settings> 
</InstanceSettings> 
</DataSource>
</Discovery>

The jamaStrings are the names we given to this later on. If you only use the console it will be some unreadable guid. We still have a few of those in our xml, but we don’t use these later on and therefore did not replace them.

creating groups

Our first approach was just to use the console to create a group. As jamaAttributes has been sealed we can easily create a server based discovery based on the customername/customerid properties. We have a property that is seperated with underscores and this we’ll use to create serveral groups for our customers.

The groups were saved to an unsealed mp called jamaGroups

Group1 = customer (property name = customerid). This group contains all servers from 1 customer. This can be achieved by adding the all the subgroups for this customer (creating nested groups) or by adding all the servers directly. We aren’t using nested groups for reasons i will explain later on.

ScreenHunter_131

Subgroup1 = logical unit for a customer. The engineers will think of this groups to whatever they think is needed for that customer and make management for them more easily. However we recommend to use Prod(uction), Accept(ion), etc. We also recommend to use as little as possible subgroups, even add a bogus prod group for small customers. The reason is for this is DNS, which will be another post in the future.

image

Subgroup2 = another logical unit for a customer. This one is optional and doesn’t have many restrictions other then naming conventions. Most used subgroups here are "application" or "location" groups e.g. domaincontrollers, exchange servers, or location Amsterdam, Paris, New York, etc.

ScreenHunter_134

However, we soon realised we missed out of a lot of alerts as SCOM doesn’t monitor from computers as the toplevel entity anymore. The 2 classes we needed to add to our groups were Virtual Servers (clusters) and the Health Watcher.

adding virtual servers

Virtual servers we also missing when we scoped on a group. However the same registry keys are available to use when we extend the property to virtual servers as well. Now the groupdiscovery includes virtual server and computer objects with 2 discoveries.

The xml of extending virtual servers with our customername property:

<Discovery ID="jamaAD_MicrosoftWindowsClusterVirtualServer_CustomerNameDiscovery" Enabled="true" Target="MicrosoftWindowsClusterLibrary6172210!Microsoft.Windows.Cluster.VirtualServer" ConfirmDelivery="false" Remotable="true" Priority="Normal">
<Category>PerformanceCollection</Category>
<DiscoveryTypes>
<DiscoveryClass TypeID="jamaType_VirtualServer_jamaAttributes">
<Property TypeID="jamaType_VirtualServer_jamaAttributes" PropertyID="jamaProperty_JamaTypeVirtualServerJamaAttributes_CustomerName" />
</DiscoveryClass>
</DiscoveryTypes>
<DataSource ID="AttributeDiscoveryGeneratedByUI246f3178580348039635fc6d8409da94" TypeID="MicrosoftWindowsLibrary6172210!Microsoft.Windows.RegistryDiscoverySingleProvider">
<ComputerName>$Target/Property[Type="MicrosoftWindowsLibrary6172210!Microsoft.Windows.Computer"]/NetworkName$</ComputerName>
<AttributeName>AttributeDiscoveryRule4195ea8a61b9497f9083c85d315a3a03</AttributeName>
<Path>SOFTWARE\Getronics\jama\OpsMgr2007\attributes\customer_name</Path>
<PathType>1</PathType>
<AttributeType>1</AttributeType>
<Frequency>86400</Frequency>
<ClassId>$MPElement[Name="jamaType_VirtualServer_jamaAttributes"]$</ClassId>
<InstanceSettings>
<Settings>
<Setting>
<Name>$MPElement[Name="jamaType_VirtualServer_jamaAttributes"]

/jamaProperty_JamaTypeVirtualServerJamaAttributes_CustomerName$</Name>
<Value>$Data/Values/AttributeDiscoveryRule4195ea8a61b9497f9083c85d315a3a03$</Value>
</Setting>
<Setting>
<Name>$MPElement[Name="MicrosoftWindowsLibrary6172210!Microsoft.Windows.Computer"]/PrincipalName$</Name>
<Value>$Target/Property[Type="MicrosoftWindowsLibrary6172210!Microsoft.Windows.Computer"]/PrincipalName$</Value>
</Setting>
</Settings>
</InstanceSettings>
</DataSource>
</Discovery>

The group in the console would become:

ScreenHunter_136

adding health watcher

The health watcher monitors whether a server has a heartbeat with the management servers. And when the heartbeat hasn’t been received 3 (or any configured number) consecutive times by a management server, the management server will try to ping the server to check whether the server is up or just the agent has a problem. When there’s no heartbeat it will raise a heartbeat failure alert. When also the ping fails it will also raise a "computer unreachable" alert. So basically it will alert you when a server is down.

However since we’re using computergroups to scope our operators, they won’t see these alerts. And what is the use of monitoring when you don’t even see when a server is down?

An msdn blog post by Steve Rachui gave the answer to this question.

But as the post already suggest, this is were things get tough. We have to leave our clickable console and start to understand what the xml looks like so we can modify it to what we need. We would need to add the healthwatcher’s discovery to each group’s discoveries and make sure the references are correct.

Looking at the group mp xml

If you’re quite happy with just manually editing the xml the blog of Steve Rachui will be enough to figure it out. However we wanted to go 1 step further as the manual edits are prone to mistake and the most important reason we are lazy and don’t want to do this 600 times.

This management pack contains 4 top levels in the xml, Manifest, TypeDefinitions, Monitoring and LanguagePacks (reading xml files is best with internet explorer as it allows to collapse parts you won’t be looking at, for editing notepad++ with the xml addon helps a lot).

The Manifest contains the name of the management pack and its version. As well as the references to other management packs. The microsoft.windows.library, Microsoft.SystemCenter.Library, Microsoft.SystemCenter.InstanceGroup.Library and our jamaAttributes all with version number and public key token as well as an alias to use in the rest of the xml. As long as the public key token doesn’t change the referenced management packs can be upgraded without changing the refences here.

<Manifest>
  <Identity>
    <ID>jamaGroups</ID>
    <Version>1.0.0.4</Version>
  </Identity>
  <Name>jamaGroups</Name>
  <References>
   +<Reference Alias="jamaAttributes1530">
    <Reference Alias="MicrosoftSystemCenterInstanceGroupLibrary6172210">
      <ID>Microsoft.SystemCenter.InstanceGroup.Library</ID>
      <Version>6.1.7221.0</Version>
      <PublicKeyToken>31bf3856ad364e35</PublicKeyToken>
    </Reference>
   +<Reference Alias="SystemCenter">
   +<Reference Alias="MicrosoftWindowsLibrary6172210">
  </References>
</Manifest>

I’ll jump to LanguagePacks now as the other 2 parts contains UINAMEunreadablerandomstring which i don’t understand. But in the LanguagePacks it becomes clear. This is where a that random string get paired with something readable.

By replacing the random strings by something more readable will help you to understand the xml a lot better. One thing is important though, this string needs to be unique for your management group. To achieve this we start with the name of the management pack and add a description that make sense  and is unique for this management pack (we used the groupname without spaces, hyphens or underscores). Replace all the strings in the entire management pack with the new name.

We’ve also added a description to the management pack name. The agent version is our scripted agent and helps us identify whether we are running the correct group mp version or not.

<DisplayStrings>
  <DisplayString ElementID="jamaGroups">
    <Name>jamaGroups</Name>
    <Description>Agent version: 2.5.0 build 396</Description>
  </DisplayString>
  <DisplayString ElementID="jamaGroupsCustomerSub1Sub2.Group">
    <Name>Customer-Sub1-Sub2</Name>
  </DisplayString>
  <DisplayString ElementID="jamaGroupsCustomerSub1Sub2.Group.DiscoveryRule">
    <Name>Populate Customer-Sub1-Sub2</Name>
    <Description>This discovery rule populates the group ‘Customer-Sub1-Sub2‘</Description>
  </DisplayString>
</DisplayStrings>

So back to TypeDefinitions, this gives information about what kind of class your group is. And since we just replaced the random strings we can actually read this without going back and forth to the displaystrings part to find out what name we are looking at. Groups are default a non-hosted singleton class, which is fine for what we’re trying to do.

<ClassTypes>
  <ClassType ID="jamaGroupsCustomerSub1Sub2.Group" Accessibility="Public" Abstract="false" Base="MicrosoftSystemCenterInstanceGroupLibrary6172210!Microsoft.SystemCenter.InstanceGroup" Hosted="false" Singleton="true" />
</ClassTypes>

So now to the actual group definitions. The groups consist of 3 membership rules. 1 to add computers to the group, 1 to add virtual servers to the group and 1 to add healthwatcher objects of the windows computers (already contained in this group) to this group.

<Discovery ID="jamaGroupsCustomerSub1Sub2.Group.DiscoveryRule" Enabled="true" Target="jamaGroupsCustomerSub1Sub2.Group" ConfirmDelivery="false" Remotable="true" Priority="Normal">
  <Category>Discovery</Category>
  <DiscoveryTypes>
    <DiscoveryRelationship TypeID="MicrosoftSystemCenterInstanceGroupLibrary6172210!Microsoft.SystemCenter.InstanceGroupContainsEntities" />
  </DiscoveryTypes>
  <DataSource ID="GroupPopulationDataSource" TypeID="SystemCenter!Microsoft.SystemCenter.GroupPopulator">
    <RuleId>$MPElement$</RuleId>
    <GroupInstanceId>$MPElement[Name="jamaGroupsCustomerSub1Sub2.Group"]$</GroupInstanceId>
    <MembershipRules>
      <MembershipRule>
        <MonitoringClass>$MPElement[Name="jamaAttributes1530!jamaType_WindowsComputer_jamaAttributes"]$</MonitoringClass>
        <RelationshipClass>$MPElement[Name="MicrosoftSystemCenterInstanceGroupLibrary6172210!Microsoft.SystemCenter.InstanceGroupContainsEntities"]$</RelationshipClass>
        <Expression>
          <SimpleExpression>
            <ValueExpression>
              <Property>$MPElement[Name="jamaAttributes1530!jamaType_WindowsComputer_jamaAttributes"]

/jamaProperty_JamaTypeWindowsComputerJamaAttributes_CustomerName$</Property>
            </ValueExpression>
            <Operator>Equal</Operator>
            <ValueExpression>
              <Value>customer_sub1_sub2</Value>
            </ValueExpression>
          </SimpleExpression>
        </Expression>
      </MembershipRule>
      <MembershipRule>
        <MonitoringClass>$MPElement[Name="SystemCenter!Microsoft.SystemCenter.HealthServiceWatcher"]$</MonitoringClass>
        <RelationshipClass>$MPElement[Name="MicrosoftSystemCenterInstanceGroupLibrary6172210!Microsoft.SystemCenter.InstanceGroupContainsEntities"]$</RelationshipClass>
        <Expression>
          <Contains>
            <MonitoringClass>$MPElement[Name="SystemCenter!Microsoft.SystemCenter.HealthService"]$</MonitoringClass>
            <Expression>
              <Contained>
                <MonitoringClass>$MPElement[Name="MicrosoftWindowsLibrary6172210!Microsoft.Windows.Computer"]$</MonitoringClass>
                <Expression>
                  <Contained>
                    <MonitoringClass>$Target/Id$</MonitoringClass>
                  </Contained>
                </Expression>
              </Contained>
            </Expression>
          </Contains>
        </Expression>
      </MembershipRule>
      <MembershipRule>
        <MonitoringClass>$MPElement[Name="jamaAttributes1530!jamaType_VirtualServer_jamaAttributes"]$</MonitoringClass>
        <RelationshipClass>$MPElement[Name="MicrosoftSystemCenterInstanceGroupLibrary6172210!Microsoft.SystemCenter.InstanceGroupContainsEntities"]$</RelationshipClass>
        <Expression>
          <SimpleExpression>
            <ValueExpression>
              <Property>$MPElement[Name="jamaAttributes1530!jamaType_VirtualServer_jamaAttributes"]/jamaProperty_JamaTypeVirtualServerJamaAttributes_CustomerName$

</Property>
            </ValueExpression>
            <Operator>Equal</Operator>
            <ValueExpression>
              <Value>customer_sub1_sub2</Value>
            </ValueExpression>
          </SimpleExpression>
        </Expression>
      </MembershipRule>
    </MembershipRules>
  </DataSource>
</Discovery>

Script the XML

Even when you don’t understand the xml above, you’ll see the only thing that will change is the part in blue when you add more groups using the same criteria.

Our original plan was to create nested groups. However, you can’t script this as a group will get a unique ID upon import of the management pack and nested groups are created based on this UID. As this is a random ID, it’s the reason why we fill the groups with just all the computers in the groups. Also, the operators wouldn’t see a difference when the click the “scope” button, it would just list all availabe groups to them whether they are nested or not.

So we have actually 3 different groupcriteria/expessions for customer, customer_sub1 and customer_sub1_sub2 (example above).

expression for customer:

<Expression>
  <SimpleExpression>
    <ValueExpression>
      <Property>$MPElement[Name="jamaAttributes1530!jamaType_WindowsComputer_jamaAttributes"]

/jamaProperty_JamaTypeWindowsComputerJamaAttributes_CustomerId$</Property>
    </ValueExpression>
    <Operator>Equal</Operator>
    <ValueExpression>
      <Value>customer</Value>
    </ValueExpression>
  </SimpleExpression>
</Expression>

expression for customer_sub1:

<Expression>
  <RegExExpression>
    <ValueExpression>
      <Property>$MPElement[Name="jamaAttributes1530!jamaType_WindowsComputer_jamaAttributes"]

/jamaProperty_JamaTypeWindowsComputerJamaAttributes_CustomerName$</Property>
    </ValueExpression>
    <Operator>MatchesRegularExpression</Operator>
    <Pattern>^customer_sub1_.*$</Pattern>
  </RegExExpression>
</Expression>

All the different groups can be derived from 1 string which we set upon agent install. When we create a new agent we have to edit an ini file with all the possible customername string allowed and the agent install fails when this string is incorrect. This is because we don’t want too much groups to be created.

Because we have the customername string and the rest doesn’t change in the xml, we can script the xml. I’ve used vbs as i’ve done lots of scripting with vbs in the past. I probably should have used the xmldom object, but i only found out of this object when i was nearly finished :). A simple writeline function to a file did the trick as well. Just use any scripting language you are comfortable with…

So first i read the ini and put the all possible strings in an array (arrRawGroups). Then i created the 3 different kind of groups i would need and put them in an array as well. Below is an example of one of the functions i used to create the customer groups.

function findTopLevelGroups(arrRawGroups)
‘ function finds the "customer names" and creates the toplevelgroups based on this name.
‘ returns array of "customer: " groups to create.
    Dim strRawGroup
    Dim arrHelper
    Dim strTopGroup
    Dim strTgroup
    Dim arrTopGroups()
    Dim bExist
    Dim intCount

intCount = 0
    Redim arrTopGroups(intCount)

wscript.echo "Finding Top Level groups."

for each strRawGroup in arrRawGroups
        arrHelper = Split(strRawGroup, "_", -1, 1)
        strTopGroup = arrHelper(0)
        if arrTopGroups(0) = "" then
            arrTopGroups(0) = strTopGroup
            intCount = intCount + 1
        else
            bExist = FALSE
            for each strTgroup in arrTopGroups
                if strTgroup = strTopGroup then
                    bExist = TRUE
                end if
            next
            if not bExist then
                Redim Preserve arrTopGroups(intCount)
                arrTopGroups(intCount) = strTopGroup
                intCount = intCount + 1
            end if
        end if
    next

findTopLevelGroups = arrTopGroups

end function

After that it’s just echo-ing the manifest, typedefinitions, monitoring and languagepacks to an xml file and replacing the “blue” strings with the appropriate groupnames. I’ve found only the monitoring part a bit challenging. Not that the coding is diffucult, but the xml parts are quite large and a simple typo makes all the difference in the world. Last but not least, when you automatically increase the version number of the management pack file it allows you to upgrade the existing management pack, whenever a new group needs to be created.

There is another approach to all this, Mike Eisenstein created a non-singleton group which creates instances of any subgroup. For us this had the drawback that it would create the groups of any name in the registry. But some might like his approach as well. See case 11 on his blog.

Posted in grouping and scoping | 1 Comment »