Walkthrough: Creating a Security Add-in Using XML and C#
This walkthrough demonstrates how to use XML and C# to create an add-in that adds a security component to the list on the Security Tab of the Windows SBS Console. The add-in will provide the status of the security component and detailed information about the security component. For this walkthrough, the Windows Firewall will represent the security component that will be added.
This walkthrough illustrates the following tasks:
Creating a health check class that defines the actions to be performed to check the status of the security component.
Creating a detail information class that defines the information that will be displayed for the security component in the details pane of the Windows SBS Console.
Creating the .xml file that is used by the Windows SBS Console to determine which .dll files to use for the security add-in.
Deploying the security add-in.
Prerequisites
This walkthrough assumes that you have a basic understanding of XML and C#. For more information about using XML, see “XML Overviews” at the Microsoft Web site (https://go.microsoft.com/fwlink/?LinkId=120543).
You will need Visual Studio 2005 or Visual Studio 2008 to complete the procedures in this document.
Creating a Health Check Class
In this section, a user-defined class is created that inherits the HealthCheck class. The HealthCheck class is used to obtain the status of a security component and to provide that status to the Security tab of the Windows SBS Console.
To create a health check class
Open Visual Studio.
On the File menu, point to New, and then click Project.
In the Project types pane, expand Visual C#, and then click Windows.
In the Templates pane, click Class Library.
Name the project SecurityAddinExample.
Click OK.
In Solution Explorer, select the Class1.cs file and rename it to SecurityHealthCheck.cs.
Next, create the constructor for the SecurityHealthCheck class and override the Execute method.
To add the constructor and override the method
In Solution Explorer, right-click the SecurityHealthCheck.cs file and then click View Code.
Add the following using statements to the top of the code file:
using NetFwTypeLib; using Microsoft.WindowsServerSolutions.SystemHealth.Infrastructure; using System.Runtime.InteropServices;
Note
The NetFwTypeLib assembly is only required to enable communication with the Windows Firewall and will not be needed for other security add-ins. You must copy the assembly to the %programfiles%\Windows Small Business Server\Bin directory when deploying your add-in. Any other assemblies that you include in your add-ins must be located in the %programfiles%\Windows Small Business Server\Bin directory.
In Solution Explorer, right-click References under the SecurityAddinExample project and click Add Reference.
On the COM page, select NetFwTypeLib, and then click OK.
In Solution Explorer, right-click References under the SecurityAddinExample project and click Add Reference.
Note
If you are developing your add-in on a computer other than the server, you must copy the SHInfra.dll file to your development computer. The SHInfra.dll file is located in the %programfiles%\Windows Small Business Server\Bin directory.
On the Browse page, select the SHInfra.dll file that you copied to the development computer, and then click OK.
Replace the class declaration with the following code:
public class SecurityHealthCheck : HealthCheck { }
Add the following code to the SecurityHealthCheck class to define the private variables and constructor:
private const string firewallAPIComObjectName = "HNetCfg.FwMgr"; private readonly INetFwMgr firewallManager; private string initializationProblemDetails; public SecurityHealthCheck() : base() { try { // Create an instance of the Windows Firewall COM object Type firewallManagerType = System.Type.GetTypeFromProgID(firewallAPIComObjectName, true); firewallManager = (INetFwMgr)Activator.CreateInstance(firewallManagerType); } catch (InvalidComObjectException invalidComObject) { initializationProblemDetails = invalidComObject.Message; } catch (COMException comException) { initializationProblemDetails = comException.Message; } }
Add code to the SecurityHealthCheck class to override the Execute method. The Execute method returns the status of the security component. The status can be one of the values of the SecurityFeatureHealthStatus enumeration. In this example, the status of OK is returned if the Windows Firewall service is running and the firewall is turned on. The status of Warning is returned if the Windows Firewall service is running and the firewall is turned off. The status of Critical is returned if the Windows Firewall service is not running.
public override HealthCheckResult Execute() { HealthCheckResult result = new HealthCheckResult(); // If the firewallManager object does not exist, the health state is set to Critical if (firewallManager == null) { result.Status = SecurityFeatureHealthStatus.Critical; result.Detail = initializationProblemDetails; return result; } // If the firewallManager object exists, the health state is set to OK result.Status = SecurityFeatureHealthStatus.Ok; try { object allowed; object restricted; firewallManager.IsPortAllowed( null, NET_FW_IP_VERSION_.NET_FW_IP_VERSION_ANY, 0, null, NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_ANY, out allowed, out restricted); bool allowedAsBool = (bool)allowed; bool restrictedAsBool = (bool)restricted; if (allowedAsBool && !restrictedAsBool) { // If the firewall is turned off, the health state is set to Warning result.Status = SecurityFeatureHealthStatus.Warning; // Detail information is associated with the health check to provide information about the status // This information is used in the detail pane of Windows SBS Console and in Reports result.Detail = "Windows Firewall is disabled"; } } catch (COMException comException) { result.Status = SecurityFeatureHealthStatus.Critical; result.Detail = "A problem has occurred : " + comException.Message; } return result; }
On the Build menu, click Build Solution. Keep Visual Studio open to complete the next procedure.
The following example shows the complete SecurityHealthCheck class.
using NetFwTypeLib;
using Microsoft.WindowsServerSolutions.SystemHealth.Infrastructure;
using System.Runtime.InteropServices;
public class SecurityHealthCheck : HealthCheck
{
private const string firewallAPIComObjectName = "HNetCfg.FwMgr";
private readonly INetFwMgr firewallManager;
private string initializationProblemDetails;
public SecurityHealthCheck() : base()
{
try
{
// Create an instance of the Windows Firewall COM object
Type firewallManagerType = System.Type.GetTypeFromProgID(firewallAPIComObjectName, true);
firewallManager = (INetFwMgr)Activator.CreateInstance(firewallManagerType);
}
catch (InvalidComObjectException invalidComObject)
{
initializationProblemDetails = invalidComObject.Message;
}
catch (COMException comException)
{
initializationProblemDetails = comException.Message;
}
public override HealthCheckResult Execute()
{
HealthCheckResult result = new HealthCheckResult();
// If the firewallManager object does not exist, the health state is set to Critical
if (firewallManager == null)
{
result.Status = SecurityFeatureHealthStatus.Critical;
result.Detail = initializationProblemDetails;
return result;
}
// If the firewallManager object exists, the health state is set to OK
result.Status = SecurityFeatureHealthStatus.Ok;
try
{
object allowed;
object restricted;
firewallManager.IsPortAllowed(
null,
NET_FW_IP_VERSION_.NET_FW_IP_VERSION_ANY,
0,
null,
NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_ANY,
out allowed,
out restricted);
bool allowedAsBool = (bool)allowed;
bool restrictedAsBool = (bool)restricted;
if (allowedAsBool && !restrictedAsBool)
{
// If the firewall is turned off, the health state is set to Warning
result.Status = SecurityFeatureHealthStatus.Warning;
// Detail information is associated with the health check to provide
// information about the status. This information is used in the detail
// pane of Windows SBS Console and in Reports
result.Detail = "Windows Firewall is disabled";
}
}
catch (COMException comException)
{
result.Status = SecurityFeatureHealthStatus.Critical;
result.Detail = "A problem has occurred : " + comException.Message;
}
return result;
}
}
}
After you have successfully built the SecurityAddinExample project, you will add a class to the project that provides detail information for the security component.
Creating a Detail Information Class
In this section, a user-defined class is created that inherits the FeatureDetailGroup class. The FeatureDetailGroup class is used to provide additional detail information about the status of a security component. The detail information is displayed in the details pane of the Security tab in the Windows SBS Console.
To create a detail information class
In Solution Explorer, right-click the SecurityAddinExample project, point to Add and then click New Item.
In the Templates pane, click Class.
Name the class SecurityDetailInformation.cs.
Click OK.
Next, create the constructor for the SecurityDetailInformation class and override the Execute method.
To add the constructor and override the method
In Solution Explorer, right-click the SecurityDetailInformation.cs file and then click View Code.
Add the following using statements to the top of the code file:
using NetFwTypeLib; using Microsoft.WindowsServerSolutions.SystemHealth.Infrastructure; using System.Runtime.InteropServices; using System.Collections.ObjectModel;
Note
The NetFwTypeLib assembly is only required to enable communication with the Windows Firewall and will not be needed for other security add-ins. You must copy the assembly to the %programfiles%\Windows Small Business Server\Bin directory when deploying your add-in. Any other assemblies that you include in your add-ins must be located in the %programfiles%\Windows Small Business Server\Bin directory.
Replace the class declaration with the following code:
public class SecurityDetailInformation : FeatureDetailGroup { }
Add the following code to the SecurityDetailInformation class to define the private variables and constructor:
private const string firewallAPIComObjectName = "HNetCfg.FwMgr"; private readonly INetFwMgr firewallManager; private string initializationProblemDetails; public SecurityDetailInformation() : base() { try { // Create an instance of the Windows Firewall COM object Type firewallManagerType = System.Type.GetTypeFromProgID(firewallAPIComObjectName, true); firewallManager = (INetFwMgr)Activator.CreateInstance(firewallManagerType); } catch (InvalidComObjectException invalidComObject) { initializationProblemDetails = invalidComObject.Message; } catch (COMException comException) { initializationProblemDetails = comException.Message; } }
Add code to the SecurityDetailInformation class to override the Execute method. The Execute method collects detail information about the status of the security component and displays the information as a set of property names and values in the details pane.
public override Collection<HealthCheckResult> Execute() { // If there were problems getting detail information, // the healthCheckList collection will be returned with information about the problem Collection<HealthCheckResult> healthCheckList = new Collection<HealthCheckResult>(); // If the firewallManager object does not exist, no detail information is returned if (firewallManager == null) { return healthCheckList; } try { object allowed; object restricted; firewallManager.IsPortAllowed( null, NET_FW_IP_VERSION_.NET_FW_IP_VERSION_ANY, 0, null, NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_ANY, out allowed, out restricted); bool allowedAsBool = (bool)allowed; bool restrictedAsBool = (bool)restricted; bool firewallEnabled = (!allowedAsBool || restrictedAsBool); string firewallStatusValue = "Disabled"; if (firewallEnabled) { firewallStatusValue = "Enabled"; } // Add the detail information that will be displayed. // The first parameter represents the label that will be displayed // The second parameter represents the value that will be displayed AddFeatureProperty("Firewall Status", firewallStatusValue); } catch (COMException comException) { HealthCheckResult healthProblem = new HealthCheckResult(); healthProblem.Status = SecurityFeatureHealthStatus.Critical; healthProblem.Detail = "A problem has occurred : " + comException.Message; healthCheckList.Add(healthProblem); } return healthCheckList; }
On the Build menu, click Build Solution.
The following example shows the complete SecurityDetailInformation class.
using NetFwTypeLib;
using Microsoft.WindowsServerSolutions.SystemHealth.Infrastructure;
using System.Runtime.InteropServices;
using System.Collections.ObjectModel;
public class SecurityDetailInformation : FeatureDetailGroup
{
private const string firewallAPIComObjectName = "HNetCfg.FwMgr";
private readonly INetFwMgr firewallManager;
private string initializationProblemDetails;
public SecurityDetailInformation() : base()
{
try
{
// Create an instance of the Windows Firewall COM object
Type firewallManagerType = System.Type.GetTypeFromProgID(firewallAPIComObjectName, true);
firewallManager = (INetFwMgr)Activator.CreateInstance(firewallManagerType);
}
catch (InvalidComObjectException invalidComObject)
{
initializationProblemDetails = invalidComObject.Message;
}
catch (COMException comException)
{
initializationProblemDetails = comException.Message;
}
}
public override Collection<HealthCheckResult> Execute()
{
// If there were problems getting detail information,
// the healthCheckList collection will be returned with information about the problem
Collection<HealthCheckResult> healthCheckList = new Collection<HealthCheckResult>();
// If the firewallManager object does not exist, no detail information is returned
if (firewallManager == null)
{
return healthCheckList;
}
try
{
object allowed;
object restricted;
firewallManager.IsPortAllowed(
null,
NET_FW_IP_VERSION_.NET_FW_IP_VERSION_ANY,
0,
null,
NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_ANY,
out allowed,
out restricted);
bool allowedAsBool = (bool)allowed;
bool restrictedAsBool = (bool)restricted;
bool firewallEnabled = (!allowedAsBool || restrictedAsBool);
string firewallStatusValue = "Disabled";
if (firewallEnabled)
{
firewallStatusValue = "Enabled";
}
// Add the detail information that will be displayed.
// The first parameter represents the label that will be displayed
// The second parameter represents the value that will be displayed
AddFeatureProperty("Firewall Status", firewallStatusValue);
}
catch (COMException comException)
{
HealthCheckResult healthProblem = new HealthCheckResult();
healthProblem.Status = SecurityFeatureHealthStatus.Critical;
healthProblem.Detail = "A problem has occurred : " + comException.Message;
healthCheckList.Add(healthProblem);
}
return healthCheckList;
}
}
After you have successfully built the SecurityAddinExample project, you will create the .xml file that the Windows SBS Console uses to add the security component to the Security tab.
Creating the .xml file
In this section, an .xml file is created that contains the elements and attributes to represent a security component that will be added to the Security Tab of the Windows SBS Console.
To create an .xml file
Open Notepad.
Add the following XML data to the new file to define the name and application for the security component.
<?xml version="1.0" encoding="utf-8" ?> <FeatureList> <Feature Name="Windows Firewall Add-in" Source="Windows Firewall" Type="Server"> </Feature> </FeatureList>
The Name attribute of the Feature Element defines the name of the security component that will be displayed in the Security Essentials column of the Windows SBS Console. The Source attribute of the Feature element defines the name of the application that is being monitored and will be displayed in the Source column. The Type attribute defines the type of system with which the security component is associated.
Note
You can add multiple Feature elements to a FeatureList Element to define multiple security components.
To add the health check for the security component
The HealthChecks Element is added to the Feature element to define the status information for the security component. The status represents the health state of the security component. Add the following XML data to the file to retrieve status information for the security component.
<HealthChecks> <HealthCheck Type="Binary"> <Assembly>SecurityAddinExample.dll</Assembly> <Class>SecurityAddinExample.SecurityHealthCheck</Class> </HealthCheck> </HealthChecks>
The Type attribute of the HealthCheck Element defines the method by which status information will be obtained. The Assembly Element defines the assembly that contains the class that provides status information. The Class Element defines the namespace and class that is used in the assembly.
To add the detail information for the security component
The DetailGroups Element is added to the Feature element to define the detail information for the security component. The detail information provides additional information about the health state of the security component. Add the following XML data to the file to retrieve detail information for the security component.
<DetailGroups> <DetailGroup Type="Binary"> <Assembly>SecurityAddinExample.dll</Assembly> <Class>SecurityAddinExample.SecurityDetailInformation</Class> </DetailGroup> </DetailGroups>
The Type attribute of the DetailGroup Element defines the method by which detail information will be obtained. The Assembly Element defines the assembly that contains the class that provides detail information. The Class Element defines the namespace and class that is used in the assembly.
Status information and detail information can be obtained using a class or a Windows PowerShell script. For more information about creating an add-in using Windows PowerShell, see Walkthrough: Creating a Security Add-in Using XML and PowerShell.
To add help information for the security component
The Help Element is added to the Feature element to define a short description for the security component that is being added. This information will be displayed in the details pane of Windows SBS Console when the component is selected. Add the following XML data to the file to provide information for the security component.
<Help> Information for the security component goes here. </Help>
Save the .xml file. You can provide any meaningful name to the file, but it must have the .xml extension.
The following example shows the complete XML data for the security component.
<?xml version="1.0" encoding="utf-8" ?>
<FeatureList>
<Feature Name="Windows Firewall Add-in" Source="Windows Firewall" Type="Server">
<HealthChecks>
<HealthCheck Type="Binary">
<Assembly>SecurityAddinExample.dll</Assembly>
<Class>SecurityAddinExample.SecurityHealthCheck</Class>
</HealthCheck>
</HealthChecks>
<DetailGroups>
<DetailGroup Type="Binary">
<Assembly>SecurityAddinExample.dll</Assembly>
<Class>SecurityAddinExample.SecurityDetailInformation</Class>
</DetailGroup>
</DetailGroups>
<Help>
Information for the security component goes here.
</Help>
</Feature>
</FeatureList>
After you have created the .xml file and the .dll file, you can deploy the security add-in files to the server.
Deploying a Security Add-in
In this section, you will place the .xml file that you previously created in the directory where the Windows SBS Console and Windows SBS Manager service can locate it, place the .dll file where it can be located, and verify that the security add-in is functioning correctly.
To deploy the security add-in
Copy the .xml file that was created earlier in this document to the %programfiles%\Windows Small Business Server\Data\SHExtensions directory on the computer that is running the Windows SBS 2008 operating system.
Copy the .dll file that was created earlier in this document to the %programfiles%\Windows Small Business Server\Bin directory on the computer that is running the Windows SBS 2008 operating system.
(Optional) Copy all dependent .dll files to the %programfiles%\Windows Small Business Server\Bin directory on the computer that is running the Windows SBS 2008 operating system.
Open the Windows SBS Console, and then click the Security tab.
Verify that your new security add-in is functioning correctly by clicking the Security tab and reviewing the entry for your security component.