Monday, 4 October 2010

Stap Me Vitals! Scanning Domain Servers using System.Management

In preparation for an overhaul of our hosting & network infrastructure, I wanted to gather stats on exactly what was running on all of our remote servers – stuff like how much physical memory is installed in the servers, what’s their Dell service tag, what sort of disk interface they’re running, what version of Windows, that kind of thing.

There’s a command-line tool wmic.exe that does some neat stuff by hooking into the Windows Management Instrumentation interface – little snippets like:

C:\>wmic /node:myserver cpu get description
Description

Intel(R) Xeon(TM) CPU 3.00GHz
Intel(R) Xeon(TM) CPU 3.00GHz
Intel(R) Xeon(TM) CPU 3.00GHz
Intel(R) Xeon(TM) CPU 3.00GHz


C:\>


- hey, looks like myserver has a quad-core Xeon CPU. Nice. Anyway, doing this across a whole lot of properties on a whole lot of servers would clearly be a bit time-consuming, so here’s how you do it using C# and the System.Management namespaces. The username/password specified in ConnectionOptions will need admin rights on all the servers you’re connecting to, and I’m sure you can reformat the output to look a little nicer, but in a nutshell, this’ll scan all the servers you specify and dump their vitals into D:\servers.txt

Simple - especially once you work out that the documentation for the cryptic magic strings inside the WMI classes is at http://msdn.microsoft.com/en-us/library/aa394084.aspx

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Management;

namespace ServerScan {
class Program {

static string[] servers = new string[] { "hugh", "pugh", "barney", "mcgrew", "cuthbert", "dibble", "grubb"};

// see notes on Win32 instrumentation classes at http://msdn.microsoft.com/en-us/library/aa394084.aspx

static Dictionary<string, string[]> wmiClasses = new Dictionary<string, string[]> {
{ "Win32_DiskDrive", new string[] { "Caption", "InterfaceType", "MediaType", "Name","Size" }},
{ "Win32_PhysicalMedia", new string[] { "Tag", "SerialNumber" }},
{ "Win32_Processor", new string[] { "Name", "ExtClock", "CurrentClockSpeed", "DeviceID" }},
{ "Win32_OperatingSystem", new string[] { "Name", "CSDVersion", "Description", "TotalVisibleMemorySize" }},
{ "Win32_SCSIController", new string[] { "Caption" }},
{ "Win32_SystemEnclosure", new string[] { "SerialNumber" }}
};

static void Main(string[] args) {
var output = File.CreateText(@"D:\servers.csv");
ConnectionOptions options = new ConnectionOptions();
options.Username = @"MYDOMAIN\big.boss";
options.Password = "password";
foreach (var server in servers) {
ManagementScope scope = new ManagementScope(String.Format(@"\\{0}\root\cimv2", server), options);
try {
scope.Connect();
foreach (var wmiClass in wmiClasses.Keys) {
ObjectQuery query = new ObjectQuery("SELECT * FROM " + wmiClass);
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);
Console.WriteLine("Searching {0} / {1}", server, wmiClass);
var results = searcher.Get();
foreach (var result in results) {
foreach (var thing in result.Properties) {
if (!wmiClasses[wmiClass].Contains(thing.Name)) continue;
output.Write(String.Format("{0}\t{1}\t{2}\t{3}\r\n", server, wmiClass, thing.Name, thing.Value));
}
}
}
} catch (Exception ex) {
output.Write(String.Format("{0}\tFAIL\tFAIL\t{1}\r\n", server, ex.Message));
}
}
output.Close();
Console.Write("All Done!");
Console.ReadKey(false);
}
}
}


No comments: