Wednesday, April 1, 2009

Remote WMI (System.Management) and Wake on LAN in an ASP.NET C#

Below is a solution I came up with for a request of an ASP.NET site that would allow a network admin to perform basic admin actions on AD Windows systems. Using the System.Management and System.Net.Sockets namespaces I was able to create a site that allows a user to power on, reboot, and query info (Drive Size, Processer info, User Logged On, etc...) from those systems. I like to refer to it as my poor man's version of SMS.

Reference Links for C# Remote Command Line and Wake on LAN:

http://www.dalun.com/blogs/05.09.2007.htm
http://www.codeproject.com/KB/IP/cswol.aspx


-------------RWMI.aspx.cs-------------------
using System;
using System.Collections.Generic;
using System.Collections;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Net;
using System.Net.Sockets;
using System.Net.NetworkInformation;
using System.Data;
using System.Text;
using System.IO;
using System.Management;

public partial class RWMI : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
//Load DataTable and GridView with Demo System Info...Configure Application
//to Pull Real Data from Either SQL or Local Text File
//Column 0 = Computer Name
//Column 1 = IP Address
//Column 2 = MAC Address (Format xx:xx:xx:xx:xx:xx)

DataTable dt = new DataTable();

dt.Columns.Add("system", typeof(System.String));
dt.Columns.Add("ipaddress", typeof(System.String));
dt.Columns.Add("macaddress", typeof(System.String));

DataRow dr = dt.NewRow();
dr[0] = "System One";
dr[1] = "192.168.1.100";
dr[2] = "00:1c:23:53:e4:38";
dt.Rows.Add(dr);

DataRow dr1 = dt.NewRow();
dr1[0] = "System Two";
dr1[1] = "192.168.1.101";
dr1[2] = "00:b0:d0:07:6f:e0";
dt.Rows.Add(dr1);

gvComputers.DataSource = dt;
gvComputers.DataBind();

}

protected void CheckStatus(object sender, GridViewRowEventArgs e)
{
//Upon Databound Event, Ping IP to See If It's Up...Disable Power On Button
//If Not Then Disable Restart and Info Buttons

if (e.Row.RowType == DataControlRowType.DataRow)
{
Button btn = new Button();
btn = (Button)e.Row.Cells[3].Controls[0];

Button btn1 = new Button();
btn1 = (Button)e.Row.Cells[4].Controls[0];

Button btn2 = new Button();
btn2 = (Button)e.Row.Cells[5].Controls[0];

Ping pingSender = new Ping();
PingOptions options = new PingOptions();
options.DontFragment = true;
string data = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
byte[] buffer = Encoding.ASCII.GetBytes(data);
int timeout = 120;
PingReply replyPing = pingSender.Send(e.Row.Cells[1].Text, timeout, buffer, options);

if (replyPing.Status != IPStatus.Success)
{
e.Row.CssClass = "NotGood";
btn.Enabled = true;
btn1.Enabled = false;
btn2.Enabled = false;
}
else
{
btn.Enabled = false;
btn1.Enabled = true;
btn2.Enabled = true;
}

}
}

protected void gvComputer_RowCommand(object sender, GridViewCommandEventArgs e)
{
try
{
//Finding Selected Row and Passing that Info to Function
int index = Convert.ToInt32(e.CommandArgument);
GridViewRow row = gvComputers.Rows[index];

//Creating Connection Options
ConnectionOptions coWMI = new ConnectionOptions();
//User Account Settings for Remote WMI Connection...Store Password in Web.Config for Better Security
coWMI.Username = "AdminUserID"; //AD Account That is an Admin on Local Systems
coWMI.Password = "AdminPassword"; //Password for that Account
coWMI.Authority = "NTLMDOMAIN:XXX"; //XXX is Your Domain

//Hide System Info Cell
tc2.Visible = false;

//Switch Statement for Button Command
switch (e.CommandName)
{
case "PowerOn":

TurnOnMAC(row.Cells[2].Text.ToString());
break;

case "Restart":

//Creates Remote Process on Selected System to Restart it in One Minute
ManagementScope msRP = new ManagementScope("\\\\" + row.Cells[1].Text.ToString() + "\\root\\cimv2", coWMI);
msRP.Connect();
ObjectGetOptions ogoRP = new ObjectGetOptions();
ManagementPath mpRP = new ManagementPath("Win32_Process");
ManagementClass mcRP = new ManagementClass(msRP, mpRP, ogoRP);
ManagementBaseObject inParams = mcRP.GetMethodParameters("Create");
inParams["CommandLine"] = @"shutdown /r /t 60";
ManagementBaseObject outParams = mcRP.InvokeMethod("Create", inParams, null);

break;

case "Info":

ArrayList alSoftware = new ArrayList();
ArrayList alDrives = new ArrayList();
ArrayList alProcess = new ArrayList();

//Setup Connection to Remote Systems root\cimv2
ManagementScope msWMI = new ManagementScope("\\\\" + row.Cells[1].Text.ToString() + "\\root\\cimv2", coWMI);
msWMI.Connect();

//WMI Query all Software Installed on System...Not Complete Listing...Only Software that writes to Certain Area in Registry
ObjectQuery oqSoftware = new ObjectQuery("Select Name from Win32_Product");
ManagementObjectSearcher mosSoftware = new ManagementObjectSearcher(msWMI, oqSoftware);
foreach (ManagementObject oReturn in mosSoftware.Get())
{
alSoftware.Add(oReturn["Name"].ToString());
}

alSoftware.Sort();
rptSoftware.DataSource = alSoftware.ToArray();
rptSoftware.DataBind();

//WMI Query for Computer Name, OS, RAM, and Last Bootup Time
ObjectQuery oqComputer = new ObjectQuery("Select * from Win32_OperatingSystem");
ManagementObjectSearcher mosComputer = new ManagementObjectSearcher(msWMI, oqComputer);
foreach (ManagementObject oReturn in mosComputer.Get())
{
lblComputerName.Text = oReturn["CSName"].ToString();
lblOS.Text = oReturn["Caption"].ToString() + " " + oReturn["CSDVersion"].ToString();
Decimal dRam = Convert.ToDecimal(oReturn["TotalVisibleMemorySize"].ToString());
dRam = dRam / 1000000M;
lblRam.Text = dRam.ToString("0.000") + " GBs";
lblBootTime.Text = ManagementDateTimeConverter.ToDateTime(oReturn["LastBootUpTime"].ToString()).ToString();
}

//WMI Query for Local Hard Drives...Calculate Free and Total Space
ObjectQuery oqDrives = new ObjectQuery("Select * from Win32_LogicalDisk WHERE DriveType=3");
ManagementObjectSearcher mosDrives = new ManagementObjectSearcher(msWMI, oqDrives);
foreach (ManagementObject oDrive in mosDrives.Get())
{
Decimal dFree = Convert.ToDecimal(oDrive["FreeSpace"].ToString());
dFree = dFree / 1073741824M;

Decimal dSize = Convert.ToDecimal(oDrive["Size"].ToString());
dSize = dSize / 1073741824M;

alDrives.Add("Drive: " + oDrive["DeviceID"].ToString() + " Free Space: " + dFree.ToString("0.00") + " GBs Total Size: " + dSize.ToString("0.00") + " GBs");

}
alDrives.Sort();
rptDisks.DataSource = alDrives.ToArray();
rptDisks.DataBind();

//WMI Query for Processor Information
ObjectQuery oqProcessor = new ObjectQuery("Select * from Win32_Processor");
ManagementObjectSearcher mosProcessor = new ManagementObjectSearcher(msWMI, oqProcessor);
foreach (ManagementObject oProcess in mosProcessor.Get())
{
alProcess.Add(oProcess["DeviceID"].ToString() + ": " + oProcess["Name"].ToString());

}
alProcess.Sort();
rptProcessor.DataSource = alProcess.ToArray();
rptProcessor.DataBind();

//WMI Query for Locally Logged On User...Will Display Admin Account If No One Logged On
ObjectQuery oqUsers = new ObjectQuery("Select * from Win32_ComputerSystem");
ManagementObjectSearcher mosUsers = new ManagementObjectSearcher(msWMI, oqUsers);
foreach (ManagementObject oUser in mosUsers.Get())
{
lblUser.Text = oUser["UserName"].ToString();
}


tc2.Visible = true;
break;

}
}
catch
{
Response.Write("Error Accessing System");
}
}

protected void TurnOnMAC(string macAddress)
{
//Wake On LAN...Take MAC Address (Format xx:xx:xx:xx:xx:xx)
//Convert to Byte...Send a UDP Packet to Wake Up System

UdpClient client = new UdpClient();
client.Connect(IPAddress.Broadcast, 40000);

Byte[] datagram = new byte[102];

for (int i = 0; i <= 5; i++)
{
datagram[i] = 0xff;
}


string[] macDigits = macAddress.Split(':');

for (int i = 1; i <= 16; i++)
{
for (int x = 0; x < 6; x++)
{
datagram[i * 6 + x] = (byte)Convert.ToInt32(macDigits[x], 16);
}
}

client.Send(datagram, datagram.Length);

}

}

--------------------Portion of RWMI.aspx--------------------------------

< p> < strong> Remote WMI (Windows Systems Only)< /strong> < /p>

< asp:Table ID="tb1" runat="server" CellSpacing="5">
< asp:TableRow>
< asp:TableCell ID="tc1" VerticalAlign="Top" runat="server">
< asp:GridView ID="gvComputers" runat="server" SelectedIndex="0" Font-Size="Small" AutoGenerateColumns="false" OnRowCommand="gvComputer_RowCommand" OnRowDataBound="CheckStatus" CellPadding="5" BorderWidth="2" GridLines="Both" >
< Columns>
< asp:BoundField DataField="system" HeaderText="Computer Name" />
< asp:BoundField DataField="ipaddress" HeaderText="IP Address" />
< asp:BoundField DataField="macaddress" HeaderText="MAC Address" />
< asp:buttonfield buttontype="Button" commandname="PowerOn" text="Power On"/>
< asp:buttonfield buttontype="Button" commandname="Restart" text="Restart"/>
< asp:buttonfield buttontype="Button" commandname="Info" text="Info"/>
< /Columns>
< /asp:GridView>
< /asp:TableCell>
< asp:TableCell ID="tc2" VerticalAlign="Top" Visible="false" runat="server">

< table cellpadding="5" cellspacing="2" border="2">
< tr> < td> < strong> Computer Info for < asp:Label ID="lblComputerName" runat="server" /> < /strong> < /td> < /tr>
< tr> < td> < strong> Logged On User:< /strong> < asp:Label ID="lblUser" runat="server" /> < /td> < /tr>
< tr> < td> < strong> Last Bootup Time:< /strong> < asp:Label ID="lblBootTime" runat="server" /> < /td> < /tr>
< tr> < td> < strong> OS:< /strong> < asp:Label ID="lblOS" runat="server" /> < /td> < /tr>
< tr> < td> < strong> RAM:< /strong> < asp:Label ID="lblRam" runat="server" /> < /td> < /tr>
< tr>
< td> < strong> Processor(s):< /strong> < br />

< asp:Repeater ID="rptProcessor" runat="server">
< ItemTemplate>
< %# Container.DataItem %>
< /ItemTemplate>
< SeparatorTemplate>
< br />
< /SeparatorTemplate>
< /asp:Repeater>
< /td>
< /tr>

< tr>
< td> < strong> Local Disk(s):< /strong> < br />

< asp:Repeater ID="rptDisks" runat="server">
< ItemTemplate>
< %# Container.DataItem %>
< /ItemTemplate>
< SeparatorTemplate>
< br />
< /SeparatorTemplate>
< /asp:Repeater>
< /td>
< /tr>

< tr>
< td> < strong> Installed Applications:< /strong> < br />

< asp:Repeater ID="rptSoftware" runat="server">
< ItemTemplate>
< %# Container.DataItem %>
< /ItemTemplate>
< SeparatorTemplate>
< br />
< /SeparatorTemplate>
< /asp:Repeater>
< /td>
< /tr>
< /table>
< /asp:TableCell>
< /asp:TableRow>
< /asp:Table>


1 comment:

Anonymous said...

This is exactly what I was looking for, Thanks for the great solution, it works like a charm…
Could you point me in the right direction as to set the credentials in the web.config file?
//User Account Settings for Remote WMI Connection...Store Password in Web.Config for Better SecuritycoWMI.Username = "AdminUserID"; //AD Account That is an Admin on Local SystemscoWMI.Password = "AdminPassword"; //Password for that Account
coWMI.Authority = "NTLMDOMAIN:XXX"; //XXX is Your Domain
How do you add them to the web.config and pass them back to the Connection options?
Thanks in advanced!