Populating groups from a text file – step by step

Kevin recently posted a blog entry on how to use a SQL database to determine and populate group membership – his post is available here.   But what if you have your data in some other location – such as a text file?  How can you use that text file to do the same thing?  It’s actually very easy – let’s walk through how to set it up.  I include the complete MP and sample input file at the bottom of this post for reference.

In my lab I have configured a simple comma delimited text file for input that describes systems in my environment and their location.  For my test I will call this file extract.txt since it’s contents were extracted from a database.

Sample Input File
LocationCode,host,model,State,Region
111,dc01.opsmgr.net,HP,Texas,Southeast
206,sql1cln1.opsmgr.net,IBM,California,West
273,VS4.opsmgr.net,HP,California,West
007,VS2.opsmgr.net,Dell,Texas,Central
090,OMDB.opsmgr.net,Dell,Texas,Central

From this data I want to build groups of all systems in the Central, West and Southeast regions.  To do this I just have to build out some simple XML.  I’ll use Kevin’s same format for showing the XML

Section 1: <Manifest>

Simply modify the <ID>, <Version>, and <Name> sections based on your custom MP naming standard. Note also the first line is a definition line and not part of the manifest section but included here and required for the MP.

<ManagementPack ContentReadable="true" xmlns:xsd="https://www.w3.org/2001/XMLSchema" xmlns:xsl="https://www.w3.org/1999/XSL/Transform">
  <Manifest>
    <Identity>
      <ID>RegionBasedGroupDemo</ID>
<Version>1.0.0.5</Version>
    </Identity>
    <Name>RegionBasedGroupDemo</Name>
    <References>
      <Reference Alias="SC">
        <ID>Microsoft.SystemCenter.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>
      <Reference Alias="Health">
        <ID>System.Health.Library</ID>
        <Version>6.0.6278.0</Version>
        <PublicKeyToken>31bf3856ad364e35</PublicKeyToken>
      </Reference>
      <Reference Alias="System">
        <ID>System.Library</ID>
        <Version>6.0.6278.0</Version>
        <PublicKeyToken>31bf3856ad364e35</PublicKeyToken>
      </Reference>
    </References>
  </Manifest>

Section 2: <TypeDefinitions>

The example below defines 3 groups, and the relationships for those groups (contains windows computer objects).  Simply replace the bolded red “WestGroup” example with a short name for your custom groups.  (We will define the UI friendly name later, in the <Presentation> section.

<TypeDefinitions>
  <EntityTypes>
    <ClassTypes>
      <ClassType ID="GroupPopulation.WestGroup" Accessibility="Internal" Abstract="false" Base="System!System.Group" Hosted="false" Singleton="true" />
      <ClassType ID="GroupPopulation.CentralGroup" Accessibility="Internal" Abstract="false" Base="System!System.Group" Hosted="false" Singleton="true" />
      <ClassType ID="GroupPopulation.SoutheastGroup" Accessibility="Internal" Abstract="false" Base="System!System.Group" Hosted="false" Singleton="true" />
    </ClassTypes>
    <RelationshipTypes>
      <RelationshipType ID="GroupPopulation.WestGroupContainsWindowsComputers" Accessibility="Internal" Abstract="false" Base="System!System.Containment">
        <Source>GroupPopulation.WestGroup</Source>
        <Target>Windows!Microsoft.Windows.Computer</Target>
      </RelationshipType>
      <RelationshipType ID="GroupPopulation.CentralGroupContainsWindowsComputers" Accessibility="Internal" Abstract="false" Base="System!System.Containment">
        <Source>GroupPopulation.CentralGroup</Source>
        <Target>Windows!Microsoft.Windows.Computer</Target>
      </RelationshipType>
      <RelationshipType ID="GroupPopulation.SoutheastGroupContainsWindowsComputers" Accessibility="Internal" Abstract="false" Base="System!System.Containment">
        <Source>GroupPopulation.SoutheastGroup</Source>
        <Target>Windows!Microsoft.Windows.Computer</Target>
      </RelationshipType>
    </RelationshipTypes>
  </EntityTypes>
</TypeDefinitions>

Section 3: <Monitoring>

In this section – we define the discovery, and add the script to run.  In this example – I am running a VBscript to loop through our text file looking for matches on region.  You will need to modify the group name – just like you did above.  This is where we create a discovery to populate each group – so we will need one of these sections for each group in the MP.  Each of these sections will run a distinct script, with a different criteria, depending on which region you want populated.

I bolded in RED the group name sections you will need to modify, just like we did above… and you will need to modify the criteria information, and the script name.

I set the discovery time to every 3600 seconds in this example…. you should probably set this to once or twice a day max…. no need to keep re-running it for groups that wont change that often.

  <Monitoring>
    <Discoveries> 
      <Discovery ID="WestGroup.Discovery" Enabled="true" Target="SC!Microsoft.SystemCenter.RootManagementServer" ConfirmDelivery="true" Remotable="true" Priority="Normal">
        <Category>Discovery</Category>
        <DiscoveryTypes>
          <DiscoveryClass TypeID="GroupPopulation.WestGroup" />
        </DiscoveryTypes>
        <DataSource ID="DS" TypeID="Windows!Microsoft.Windows.TimedScript.DiscoveryProvider">
          <IntervalSeconds>3600</IntervalSeconds>
          <SyncTime />
          <ScriptName>WestGroupDiscovery.vbs</ScriptName>
          <Arguments>$MPElement$ $Target/Id$</Arguments>
          <ScriptBody><![CDATA[Dim SourceId
Dim objConnection
Dim oRS
Dim sConnectString
Dim ManagedEntityID
Dim oAPI
Dim oDiscoveryData
Dim oAttributes
Dim oLocation
Dim oHostName
Dim oMOdel
Dim oState
Dim oRegion
Dim oFSO
Dim oINst
dim fsoFile
SourceId                     = WScript.Arguments(0)
ManagedEntityId        = WScript.Arguments(1)

Set oAPI                      = CreateObject("MOM.ScriptAPI")
Set oDiscoveryData     = oAPI.CreateDiscoveryData(0,SourceId,ManagedEntityId)
Set oFSO                     = CreateObject("Scripting.FileSystemObject")
Set groupInstance      = oDiscoveryData.CreateClassInstance("$MPElement[Name='GroupPopulation.WestGroup']$")
set fsoFile = ofso.opentextfile("c:\extract.txt", 1)

oAttributes=fsofile.readline

Do Until fsofile.AtEndOFStream
oAttributes=fsofile.readline
oLocation=Left(oAttributes,(InStr(oAttributes,",")-1))
oAttributes=Right(oAttributes,Len(oAttributes)-Len(oLocation)-1)
oHostName=Left(oAttributes,(InStr(oattributes,",")-1))
oAttributes=Right(oAttributes,Len(oAttributes)-Len(oHostName)-1)
oModel=Left(oAttributes,(InStr(oattributes,",")-1))
oAttributes=Right(oAttributes,Len(oAttributes)-Len(oModel)-1)
oState=Left(oAttributes,(InStr(oattributes,",")-1))
oAttributes=Right(oAttributes,Len(oAttributes)-Len(oState)-1)
oRegion=oAttributes
oAttributes=""

    If oRegion="West" Then
        Set ServeriNstance = oDiscoveryData.CreateClassInstance("$MPElement[Name='Windows!Microsoft.Windows.Computer']$")
        serverInstance.AddProperty "$MPElement[Name='Windows!Microsoft.Windows.Computer']/PrincipalName$",oHostName
        Set relationshipInstance = oDiscoveryData.CreateRelationshipInstance("$MPElement[Name='GroupPopulation.WestGroupContainsWindowsComputers']$")
        relationshipInstance.Source = groupInstance
        relationshipInstance.Target = serverInstance
        oDiscoveryData.AddInstance relationshipInstance
    End If
Loop

'Once the discovery data is obtained it needs to be submitted
Call oAPI.Return(oDiscoveryData)
                     ]]></ScriptBody>

          <TimeoutSeconds>120</TimeoutSeconds>
        </DataSource>
      </Discovery>
  </Discoveries>
</Monitoring>

Section 4: <LanguagePacks>

<LanguagePacks>
  <LanguagePack ID="ENU" IsDefault="true">
    <DisplayStrings>
      <DisplayString ElementID="RegionBasedGroupDemo">
        <Name>Region Based Group Population Demo MP</Name>
      </DisplayString>

      <DisplayString ElementID="GroupPopulation.WestGroup">
        <Name>West Servers Group</Name>
      </DisplayString>
      <DisplayString ElementID="GroupPopulation.WestGroupContainsWindowsComputers">
        <Name>West Based Group Contains Windows Computers</Name>
      </DisplayString>
      <DisplayString ElementID="WestGroup.Discovery">
        <Name>West Based Group Discovery</Name>
        <Description />
      </DisplayString>

      <DisplayString ElementID="GroupPopulation.CentralGroup">
        <Name>Central Servers Group</Name>
      </DisplayString>
      <DisplayString ElementID="GroupPopulation.CentralGroupContainsWindowsComputers">
        <Name>Central Based Group Contains Windows Computers</Name>
      </DisplayString>
      <DisplayString ElementID="CentralGroup.Discovery">
        <Name>Central Based Group Discovery</Name>
        <Description />
      </DisplayString>

      <DisplayString ElementID="GroupPopulation.SoutheastGroup">
        <Name>Southeast Servers Group</Name>
      </DisplayString>
      <DisplayString ElementID="GroupPopulation.SoutheastGroupContainsWindowsComputers">
        <Name>Southeast Based Group Contains Windows Computers</Name>
      </DisplayString>
      <DisplayString ElementID="SoutheastGroup.Discovery">
        <Name>Southeast Based Group Discovery</Name>
        <Description />
      </DisplayString>

    </DisplayStrings>
  </LanguagePack>
</LanguagePacks>

That’s it!  If you get errors trying to import – you most likely modified a definition incompletely…. the import error should help you figure out what's wrong.

Now I can go to my groups – find “West Group” and see if it is populated:  SUCCESS!

image

TextFileBasedGroups-Sample MP and Data File.zip