Fetch Service Status & Storage Info remotely in C# using WMI
Tags: dotnet, devops, webdev, showdev
Published: 2020-09-20 08:24:39

I was recently working on a [pet project](https://github.com/chaitanya-suvarna/RemoteServerStatus/) where I wanted to find the status of some services on **multiple server instances** and display this on a web page so that I have a single page to look at if I want to check the **status** if any of my application services have stopped on any of the sever instances. I also wanted to be able to **Start** or **Stop** these services from the same page. With this in place I no longer needed to log into any of the remote servers to check up on the services. And just as an add-on, I also wanted to be able to look at the storage information on these servers so that I can take action on any of the drives that are filling up quickly.

While looking around trying to figure out the easiest way to create such a Web App I came across [**Windows Management Instrumentation**](https://docs.microsoft.com/en-us/windows/win32/wmisdk/wmi-start-page) which is the infrastructure for management data and operations on **Windows-based** operating systems. Developers can use WMI to **remotely monitor the hardware and software** on remote computers. Remote connections for managed code are accomplished through the [System.Management](https://docs.microsoft.com/en-us/dotnet/api/system.management?view=dotnet-plat-ext-3.1) namespace. 

Let’s have a look at how we can go about fetching Service and Storage info remotely using WMI with the System.Management namespace.

##**Setting up ConntectionOptions**
The **ConnectionOptions** class from the `System.Management` namespace specifies all settings required to make a WMI connection. You can create a `ConnectionOptions` object and use it without setting up any of it’s properties to connect to the remote computer with default connection options. In this example I have setup some of the properties based on my connection requirements to the remote servers.

```
ConnectionOptions connection = new ConnectionOptions();
connection.Username = "User";
connection.Password = "AStrongPassword";
connection.Authority = "ntlmdomain:DOMAINNAME";
connection.EnablePrivileges = true;
connection.Authentication = AuthenticationLevel.Default;
connection.Impersonation = ImpersonationLevel.Impersonate;
```

##**Connecting to remote server using ManagementScope**
The **ManagementScope** class represents a scope for management operations. You can initialize a new `ManagementScope` with a specific path and then connect the scope object to a namespace on a remote computer using the `ConnectionOptions` object.

```
ManagementScope scope = new ManagementScope(
                        $"\\\\{serverName}\\root\\CIMV2", connection);
scope.Connect();
```

##**Fetching management information using ManagementObjectSearcher and ObjectQuery**
The **ObjectQuery** class is used to specify a query in the `ManagementObjectSearcher`.
The **ManagementObjectSearcher** class is used to retrieve a collection of management objects based on a specified query. This class is one of the more commonly used entry points to retrieving management information. In this example I have created the `ObjectQuery` to fetch **LogicalDisk info** and I am using `ManagementObjectSearcher` to get each disk’s info.

```
ObjectQuery query = new ObjectQuery("SELECT * FROM Win32_LogicalDisk");

ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);

foreach (ManagementObject managementObject in searcher.Get())
{
   Console.WriteLine("Drive Name :" +
                      managementObject["Name"].ToString());
   Console.WriteLine("Volume Size :" +
                      managementObject["Size"].ToString());
   Console.WriteLine("Free Space :" +
                      managementObject["FreeSpace"].ToString());
}
```

Similarly we can also fetch **Service information** like below

```
ObjectQuery query = new ObjectQuery("SELECT * FROM Win32_Service");

ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);

foreach (ManagementObject managementObject in searcher.Get())
{
   Console.WriteLine("Service Name :" +
                      managementObject["DisplayName"].ToString());
   Console.WriteLine("Service State :" +
                      managementObject["State"].ToString());
}
```

##**Using InvokeMethod() on a ManagementObject**
We can use the method **InvokeMethod()** on a `ManagementObject` to perform operations on it asynchronously. In the below example, I have created the `ObjectQuery` to fetch the `ManagementObject` to represent a specific service who’s `DisplayName` is stored in service_name. I am then calling `InvokeMethod()` to invoke the `StartService` method with no options which will **start the service on the remote server asynchronously**.

```
ObjectQuery query = new ObjectQuery("SELECT * FROM Win32_Service where
                                     DisplayName= '" + service_name + "'");

using (ManagementObjectSearcher searcher = new
                                   ManagementObjectSearcher(scope, query))
{
  foreach (ManagementObject myservice in searcher.Get())
  {
     myservice.InvokeMethod("StartService", null);
  }
}
```

Thus we have seen how we can use `System.Management` namespace and it’s classes to fetch management info from Windows machines and perform actions remotely. You can see the entire ASP .Net Core Web App that I have created on my Github profile [here](https://github.com/chaitanya-suvarna/RemoteServerStatus).

I hope you found this interesting. Thanks for reading!