There has been a lot of interest in a simple way to set the state of an instance. Since we don't allow this to happen directly via the SDK, I've had many requests for a simple way to do this so here it is. One caveat: if the instance's state is Error, for instance, from a different monitor, this will not override that state. The monitor this implementation rolls up to has a "Worst Of" algorithm, so even though this monitor as set with the code below is Healthy, there may be another monitor that contributes state that is not. Hope that makes sense.
First, import the attached management pack, Manual.EntityState.Helper.xml. I've left it as unsealed so you can make changes to it if you want. Next you will need this class:
/// A class to be used in conjunction with the Manual Entity State Helper management pack to quickly set the state of any instance.
public class EntityStateHelper
/// Sets the state of an instance.
/// <param name="monitoringObject">The object to set the state of.</param>
/// <param name="healthState">The state to set to.</param>
public static void SetState(MonitoringObject monitoringObject, HealthState healthState)
// Set the proper event id based on the state
eventNumber = 1;
eventNumber = 2;
eventNumber = 3;
// Insert the event
CustomMonitoringEvent stateEvent = new CustomMonitoringEvent("EntityStateHelper", eventNumber);
Usage is pretty simple:
// Connect to the local management group
ManagementGroup mg = new ManagementGroup("localhost");
// Get the agent class
MonitoringClass hs = mg.GetMonitoringClass(SystemMonitoringClass.HealthService);
// Get an agent
ReadOnlyCollection<MonitoringObject> monitoringObjects = mg.GetMonitoringObjects(hs);
// Set its state
I have tested this a bit, but there may be places where this doesn't work as this was meant to work for instances hosted by the Root Management Server. If you do run into issues with a scenario, please let me know so I can try to get it figured out.
Update: You cannot use the method described above to set the state of any instance not hosted on the Root Management Server.
Would it be possible to compile this into a powershell cmdlet?
I would love to use this in a loop to set state based on the outcome of a script (If...else).
I believe you could do it. Power shell supports .Net so, this should be straightforward to port.
Jakub could you please expand a bit on your update about this only working on the Root Management Server.
Specifically, is it the case generally that the SDK modules (like TargetEntitySdkEventProvider) can only be used against monitoring objects hosted on the root management server? In a previous post you seemed to be suggesting this would work if you got the db permissions right: http://blogs.msdn.com/jakuboleksy/archive/2006/09/05/Inserting-Operational-Data.aspx
Does that essentially mean that connector-inserted objects in general can only really appear to be hosted on the root management server?
Objects inserted by the SDK can be hosted anywhere, as long as you tell the system where you want them to be managed, either by entering them as hosted by something hosted on said machine or by inserting the 'should manage' relationship directly. That being said, if it is hosted somewhere else, the module requires DB permissions to pull data, hence it's only supported on RMS, but also we've never tested it elsewhere and there may be issues trying to use the write action to set the state.
Hmm. Why I asked is that I already tried it, and couldn't get it to work. The discovery is all fine, the monitor just never seems to change state based on the events. But I didn't see any errors such as I would expect if it was failing on a DB permissions issue.
That being said, I will go and give it another go.
Currently my objects are hosted, and hosted remotely to the RMS (inside my own windows.localapplication). I've not really looked at the 'should manage' relationship. Is there any way of forcing a monitor to only execute on the RMS, even whilst it controls the state of a monitoring object 'hosted' elsewhere?
You can't have your monitor run on the RMS and set state for an instance managed elsewhere; there is no way around that. In terms of getting the SDK module to run not on the RMS, I am never actually tried it so it only works "in theory".
I was confused originally as to why you were so clear that the method above would only work against the RMS, whereas you'd said previously (and repeated above) that the 'monitoring SDK events' method could work remotely (accepting you didn't say it would, just that it might).
Looking at the MP above I realise that the native class you use as the implementation for the Manual.EntityState.Helper.SetStateAction module is probably only registered on the RMS server, and not on the agent managed machines (though I can't actually find that GUID anywhere when I look - it's not an R2 ClassID is it?). I'm guessing that’s why you're so clear that this is a technique that can only be used on the RMS.
But that got me thinking about the TargetEntitySdkEventProvider. Ultimately it's implementation relies on SdkEventDataSource from Microsoft.Mom.SdkModules, and that assembly is does not appear to be on my agent managed host anywhere. It looks to me like just because you reference a management pack doesn't mean that any assemblies that MP is implemented against get deployed to the agent along with the monitor definition. I can see the Microsoft.SystemCenter.Library MP in the 'Management Packs' folder on the agent machine, but I can't find Microsoft.Mom.SdkModules anywhere on that machine.
[and that's got a fair few dependencies itself, so bringing it over to get it working might be a bit hit and miss]
Have I hit the wall and should stop wasting my time now, or did I miss something?
You'll have to distribute th SDK dll yourself since we only install that on the RMS. The SetStateAction *should* be available on that machine (it's just copied from a system library).
It's working now. Just to clarify for anyone else reading, this is for the 'monitoring based on SDK events' scenario, not the 'forcing the state of an aggregate monitor' scenario, which is what Jakub was actually discussing in the post above.
Like I said, the Microsoft.Mom.SdkModules has a fair few dependencies, so it's not just a case of copying the SDK dlls (which don't include that assembly anyway) over. I had to copy over the following:
Additionally the following assemblies appear as dependencies from Microsoft.Mom.SdkModules, but don't appear to be required (obviously those call paths are not exercised in this case):
I also had to set the following registry key, which the DataAccessLayer (used by the SdkEventDataSource class) uses to locate the MOM database:
HKLM\SOFTWARE\Microsoft\Microsoft Operations Manager\3.0\Setup\DatabaseServerName
Then I had to configure a Run As Account against the Run As Profile 'Operational Database Account', limiting this to the machine I need to run the SDK data source on.
Finally, since I configured the RunAs account as Network Service, I had to go and grant the Network Service account on the remote machine access to the OperationsManager database. I gave it sdk_users role, and it somehow also got db_datareader, db_datawriter, db_ddladmin.
Then it all worked. Yes, it really was that easy.
Nice work and thanks for following up.