How to use .NET managed code in a Management Pack for Operations Manager 2007 R2 - Part 1 - The Overview
How to use .NET managed code in a Management Pack for Operations Manager 2007 R2 - Part 1 - The Overview
I recently attended and participated in the Microsoft Management Summit 2011 (MMS 2011) in Las Vegas, NV. While I was working at the Datacenter Services booth I was approached by a guy that asked me a question "Hey is there any way to use .NET code in a management pack?" I responded and told him that there's nothing native that supports using .NET code, but since the arrival of R2 there is a native PowerShell provider that will allow you to do pretty much everything in .NET in PowerShell. After a lengthy conversation I was then left with the curiosity of can it be done? Is there an easy supportable and efficient way to implement .NET code in a management pack? The answer is YES! This post will provide an overview of how I accomplished the task.
The PowerShell Add-Type Cmdlet (The secret sauce)
When I started to research this possibility, I discussed this with a co-worker Don and he led me to the PowerShell Add-Type cmdlet ( https://technet.microsoft.com/en-us/library/dd315241.aspx) . This cmdlet allows you to define a .NET class in your PowerShell script. As you can see from the TechNet example below, you simply create a variable and encapsulate your .NET code within the @" <.NET code here> ". You can then instantiate your class for use.
C:\PS>$source = @"
public class BasicTest
{
public static int Add(int a, int b)
{
return (a + b);
}
public int Multiply(int a, int b)
{
return (a * b);
}
}
"@
C:\PS> Add-Type -TypeDefinition $source
C:\PS> [BasicTest]::Add(4, 3)
C:\PS> $basicTestObject = New-Object BasicTest
C:\PS> $basicTestObject.Multiply(5, 2)
Creating the PowerShell Script
Now that I have an understanding of how to encapsulate .NET code in a PowerShell script, I now need to come up with an example to use. I've found this to be the most challenging part because everything I could code in .NET I could do in PowerShell and in most cases with much less lines of code. So I decided to go with a common example in the management pack documentation, I will return the number of files in a folder. This is the C# class I will use below.
public static int FileCount(string FolderName)
{
string s = FolderName;
int files;
System.IO.DirectoryInfo f = new System.IO.DirectoryInfo(FolderName);
files = f.GetFiles().Length;
return files;
}
So from looking at the C# class, it is roughly 8 lines of code. Compared to doing the functionality in PowerShell in one line of code like this (Get-ChildItem C:\Temp).Count . Again if it was me I would just write a PowerShell script and use the native script module provider in R2, but since this post is about using .NET code in a management pack we will continue on. Also one thing to note is I had to fully qualify my method calls such as System.IO.DirectoryInfo whereas if you are using a directive such as "using System.IO;" I could just call DirectoryInfo instead of System.IO.DirectoryInfo. I'm sure there's a way to utilize directives, but I just did not investigate all the Add-Type cmdlet options and in my testing the script didn't work when using it.
Now that I know what I want to monitor and have my .NET C# class, I now need to put it in a script for Operations Manager to consume. Below is the PowerShell script containing the .NET C# class that I will use when creating the management pack.
param([string]$MonitoredFolder)
$api = New-Object -comObject 'MOM.ScriptAPI'
$api.LogScriptEvent("CheckFolderFileCount.ps1",101,4,"Script started.")
$api.LogScriptEvent("CheckFolderFileCount.ps1",101,4,"The monitored folder is $MonitoredFolder")
$bag = $api.CreatePropertyBag()
$CSharpCode = @"
public class Folder
{
public static int FileCount(string FolderName)
{
string s = FolderName;
int files;
System.IO.DirectoryInfo f = new System.IO.DirectoryInfo(FolderName);
files = f.GetFiles().Length;
return files;
}
}
"@
Add-Type -TypeDefinition $CSharpCode
$api.LogScriptEvent("CheckFolderFileCount.ps1",101,4,"The amount of files in $MonitoredFolder is {0}" -f [Folder]::FileCount($MonitoredFolder))
$bag.AddValue('FolderFileCount', '[Folder]::FileCount($MonitoredFolder)')
$bag
$api.LogScriptEvent("CheckFolderFileCount.ps1",101,4,"Script complete.")
Part 2 of this post will discuss implementing the script into a custom management pack.