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:
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:
The discovery will discover any customer class we have defined:
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 0const 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_objDiscoveryDatajamaInitialize()
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 functionfunction jamaCreateDiscovery()
dim strCustomerIdstrCustomerId = 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 functionfunction jamaGetAttribute(byval strAttribute)
dim strResult
dim objRegstrResult = ""
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 0jamaGetAttribute = strResult
end functionfunction jamaWriteLog(iEventId, strMsg)
dim bEnabled : bEnabled = true ‘ Set to false to disable logging.if(bEnabled) then
dim objAPI
dim strTempset 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:
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.