Thursday, December 13, 2007

SCOM Maintenance Mode

One of the main goals for implmenting system management tools such as SMS (SCCM) and MOM (SCOM) is to automate routine tasks, unfortunatly these tools sometimes makes the task more difficult than it needs to be. For instance SCOM has a maintenance mode function which allows monitoring of a system to be suspended while planned work is being done. This aleviates false-alerting and enables scheduled maintenance of a system. The only way to place a system into maintenance mode however is by using the SCOM console.

There are several other scripts available that allow for placing servers into maintenance mode, but they were either clunky or closed source (or both), and ultimately made the task more difficult than it should be (in my opinion). A few examples are available at :

My goal is to have something that fits the following:

  • Runs from the command line for general scripting
  • Can add and remove systems from maintenance mode
  • Easy to use so Tier 1/2 operators can use it
  • Highly portable, should run on any system in the environment - any OS or domain

The first round came up with the below C# applet, it uses .NET 2.0 and the SCOM dlls to manage the maintenance mode settings. My hope for the next version will be a C# web service that runs on the managemetn server and is interfaced via VBScript; this specifically targets the last bullet since it removes the requirement for .NET, the SCOM console, and correct domain membership.

ZIP file containing the source and compiled EXE at http://edgoad.googlepages.com/MaintMode.zip

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using Microsoft.EnterpriseManagement;
using Microsoft.EnterpriseManagement.Administration;
using Microsoft.EnterpriseManagement.Common;
using Microsoft.EnterpriseManagement.Configuration;
using Microsoft.EnterpriseManagement.Monitoring;

// http://msdn2.microsoft.com/en-us/library/bb437532.aspx - Maintenance Mode
// http://msdn2.microsoft.com/en-us/library/bb437559.aspx - repairing agents
namespace MaintMode
{
class Program
{
static void Main(string[] args)
{
Program myProg = new Program();
if ((args.Length != 3) && (args.Length != 6))
{
myProg.DisplayHelp();
}
else
{
string sAction = args[0].ToLower();
string sManagementServer = args[1].ToLower();
string sAgentName = args[2].ToLower();
switch (sAction)
{
case "/a":
MaintenanceModeReason eReason = (MaintenanceModeReason)Enum.ToObject(typeof(MaintenanceModeReason), Convert.ToInt32(args[3]));
string sComment = args[4];
int iMinutes = Convert.ToInt32(args[5]);
myProg.StartMM(sManagementServer, sAgentName, eReason, sComment, iMinutes);
break;
case "/r":
myProg.EndMM(sManagementServer, sAgentName);
break;
case "/q":
myProg.QueryMM(sManagementServer, sAgentName);
//myProg.EndMM(sManagementServer, sAgentName);
break;
}
}
}
public void StartMM(string managementServer, string agentName, MaintenanceModeReason eReason, string sComment, int iMinutes)
{
DateTime startTime = DateTime.UtcNow;
DateTime endTime = DateTime.UtcNow.AddMinutes(iMinutes);
ManagementGroup mg = new ManagementGroup(managementServer);
ReadOnlyCollection monitoringObjects;
MonitoringClass computerMonitoringClass;
MonitoringObjectCriteria criteria;
computerMonitoringClass = mg.GetMonitoringClass(SystemMonitoringClass.WindowsComputer);
criteria = new MonitoringObjectCriteria(string.Format("Name like '" + agentName + "%'"), computerMonitoringClass);
monitoringObjects = mg.GetMonitoringObjects(criteria);
if (monitoringObjects.Count < 1)
{
Console.WriteLine("ComputerName '" + agentName + "' at ManagementServer '" + managementServer + "' is not found");
}
else
{
foreach (MonitoringObject monitoringObject in monitoringObjects)
{
if (!monitoringObject.InMaintenanceMode)
{
monitoringObject.ScheduleMaintenanceMode(startTime, endTime, eReason, sComment);
Console.WriteLine(monitoringObject.DisplayName + " is now in maintenance mode until " + endTime.ToLocalTime().ToShortTimeString());
}
else
{
MaintenanceWindow window = monitoringObject.GetMaintenanceWindow();
endTime = window.ScheduledEndTime;
Console.WriteLine(monitoringObject.DisplayName + " is ALREADY in maintenance mode until " + endTime.ToLocalTime().ToShortTimeString());
}
}
}
}
public void EndMM(string managementServer, string agentName)
{
ManagementGroup mg = new ManagementGroup(managementServer);
ReadOnlyCollection monitoringObjects;
MonitoringClass computerMonitoringClass;
MonitoringObjectCriteria criteria;
computerMonitoringClass = mg.GetMonitoringClass(SystemMonitoringClass.WindowsComputer);
criteria = new MonitoringObjectCriteria(string.Format("Name like '" + agentName + "%'"), computerMonitoringClass);
monitoringObjects = mg.GetMonitoringObjects(criteria);
if (monitoringObjects.Count < 1)
{
Console.WriteLine("ComputerName '" + agentName + "' at ManagementServer '" + managementServer + "' is not found");
}
else
{
foreach (MonitoringObject monitoringObject in monitoringObjects)
{
if (monitoringObject.InMaintenanceMode)
{
monitoringObject.StopMaintenanceMode(DateTime.UtcNow);
Console.WriteLine(monitoringObject.DisplayName + " is now out of maintenance mode");
}
else
{
Console.WriteLine(monitoringObject.DisplayName + " is ALREADY out of maintenance mode");
}
}
}
}
public void QueryMM(string managementServer, string agentName)
{
ManagementGroup mg = new ManagementGroup(managementServer);
ReadOnlyCollection monitoringObjects;
MonitoringClass computerMonitoringClass;
MonitoringObjectCriteria criteria;
computerMonitoringClass = mg.GetMonitoringClass(SystemMonitoringClass.WindowsComputer);
criteria = new MonitoringObjectCriteria(string.Format("Name like '" + agentName + "%'"), computerMonitoringClass);
monitoringObjects = mg.GetMonitoringObjects(criteria);
if (monitoringObjects.Count < 1)
{
Console.WriteLine("ComputerName '" + agentName + "' at ManagementServer '" + managementServer + "' is not found");
}
else
{
foreach (MonitoringObject monitoringObject in monitoringObjects)
{
if (monitoringObject.InMaintenanceMode)
{
MaintenanceWindow window = monitoringObject.GetMaintenanceWindow();
DateTime endTime = window.ScheduledEndTime;
string sComment = window.Comments;
MaintenanceModeReason eReason = window.Reason;
Console.WriteLine(monitoringObject.DisplayName + " is in maintenance mode until " + endTime.ToLocalTime().ToShortTimeString());
Console.WriteLine("Comments: " + sComment);
Console.WriteLine("Reason : " + eReason.ToString());
}
else
{
Console.WriteLine(monitoringObject.DisplayName + " is NOT in maintenance mode");
}
}
}
}
public AgentManagedComputer Connect(string managementServer, string computerName)
{
ManagementGroup mg = new ManagementGroup(managementServer);
// TODO: Add try/catch to ManagementGroup
ManagementGroupAdministration admin = mg.GetAdministration();
// Fully qualified name of the agent-managed computer.
string fullAgentComputerName = computerName + "%";
string query = "Name LIKE '" + fullAgentComputerName + "'";
AgentManagedComputerCriteria agentCriteria = new AgentManagedComputerCriteria(query);
ReadOnlyCollection agents = admin.GetAgentManagedComputers(agentCriteria);
if (agents.Count != 1)
throw new InvalidOperationException("Error! Expected one managed computer with: " + query);
AgentManagedComputer myAgent = agents[0];
return myAgent;
}
public void DisplayHelp()
{
string usage = "MaintMode.exe";
usage += "\n==============================================";
usage += "\nAdd or remove a server from maintenance mode";
usage += "\n==============================================";
usage += "\nUsage: maintmode.exe [/a /r /q] ManagementServer ComputerName [ReasonCode] [Comment] [Time]";
usage += "\n /a: Place server into maintenace mode";
usage += "\n /r: Remove server from maintenace mode";
usage += "\n /r: Query server maintenace mode information";
usage += "\n\n ManagementServer: Name of root management server";
usage += "\n ComputerName: Name of computer to place into maintenance mode";
usage += "\n ReasonCode: List of Reasoncode values (0-14):\n (required to add a server into maintenance)";
string[] myS = Enum.GetNames(typeof(MaintenanceModeReason));
foreach (string s in myS)
usage += "\n " + (int)Enum.Parse(typeof(MaintenanceModeReason), s, true) + ":" + s;
usage += "\n Comment: Comment of reason for maintenance mode\n (required to add a server into maintenance)";
usage += "\n Time: Time in minutes for maintenace mode\n (required to add a server into maintenance)";
usage += "\n\nExample: adding a machine into maintenance mode for 60 minutes";
usage += "\n maintmode.exe /a wtmsscom1 wtrpengeg \"add to maint mode\" 60";
usage += "\nExample: removing a machine from maintenance mode";
usage += "\n maintmode.exe /r wtmsscom1 wtrpengeg";
Console.WriteLine(usage);
}
}
}

6 comments:

Anonymous said...

Thanks this was most helpful!

Anonymous said...

Hey this is great.. I am having to write a c# app that will do the same thing, plus a litte more. So if you have anymore code samples like this pertaining to puting things in maintenance mode I would like to here about them either by your blog or email. Also how did you go about knowing what functions to use from the scom dll's.

Thanks again for this great example.

Anonymous said...

Nice job! I've been tinkering with the same functionality in c# and noticed that this code will place the Windows object into MM; however, it does not place the Health Service (and Watcher) classes into MM. The server will be fine for normal online maintenance but if it goes offline during the MM windows (longer than the heartbeat threshold is set for), then it will still trigger both a Heartbeat and an Offline alert.

Do you have any suggestions for working around this item. It's not too difficult in PowerShell by targeting the Microsoft.SystemCenter.HealthService and HealthServiceWatcher classes, but I haven't been able to get it going in C#. I tried the HealthService enum for the SystemMonitoringClass with no luck.

Any ideas?

Tim McFadden said...

I moved my site. I will have a new tool out soon as well.

http://www.scom2k7.com/offline-maintenance-mode-powershell-scripts-for-operations-manager-2007

Anonymous said...

I am completely new to this application. How and on what system (RMS ?) do I install it ?
Once unzipped it creates and installed in subfolders... execs are in \bin?
Thanks
Marie

Elson Cade said...

I struggled my head with this problem and couldn't get it solved. I started looking for a freelancer and found a team of friendly people called called ServerBuddies.
They provide quality remote server management, including troubleshooting, Server Management, Plesk Support, Server Maintenance, Server Monitoring, Server Troubleshooting and support at a affordable rates. They also provide Server Optimization, Plesk Support, Linux Support, cPanel Support and Plesk Support.
They are great, I highly recommend to use these guys!