Configuring Terminal Services Gateway Using WMI





4.00/5 (1 vote)
Describes methods to add and to remove RAPs from Terminal Services Gateway using WMI.
Introduction
This article is about WMI and the Terminal Services Gateway. You can find a way to manage your TS Gateway server remotely by using WMI. Here, I describe two methods, to add and to remove a Resource Authorization Policy (RAP). All code blocks are picked from a proof-of-concept demo, and they do not represent "in production" state of the code.
Background
Employees that are away from office premises are in constant need of accessing internal resources. Letting people to access their own office computer gives the most familiar feeling while being away. When opening such connections from the Internet, some measures to secure, manage, and handle these connections are needed. To do this without burdening the administrators, an automatic service that does the Terminal Services Gateway configuration for the end-user was developed. This article shows a way to manage one part of the TS Gateway server configuration.
Using the Code
The System.Management
namespace needs to be added before the code works. Also, the methods expect you to have a user group on your server. The name of the group in this case is 'RAP_TEST'. If you are part of a domain or if you have an Active Directory service running, you can use 'DOMAIN\UserGroup' like groups as well.
When executing the code, be aware that the authentication level of PacketPrivacy can not be achieved locally. This means that you are not able to run the code on the server you are trying to configure.
First, I start with the code that uses the class described later on. This is a simple loop that adds 100 RAPs to the server.
public class ProofOfConcepts
{
static void Main(string[] args)
{
TerminalServicesGatewayConnector oTSC =
new TerminalServicesGatewayConnector("TSGWTEST");
string log = "";
for (int i = 0; i < 100; i++)
{
oTSC.AddRAP("COUNT_TEST_" + i.ToString(), out log);
Console.WriteLine("#\t" + i.ToString() + "\t" + log);
}
Console.ReadLine();
}
}
The main point of the article starts by defining the TerminalServerGatewayConnector
class. This class is used to connect and make configuration changes. Methods found later from the article are methods of this class.
using System.Management;
public class TerminalServicesGatewayConnector
{
public enum SUCCESS : byte
{
SUCCESS = 0x00,
ERROR = 0x01,
REMOTE_EXCEPTION = 0x02
}
private ConnectionOptions _oConn;
private ManagementScope _oMScope;
private ManagementPath _oMPath;
private string _sHost;
private string _sPath = @"\root\CIMV2\TerminalServices";
private string _sUsername = @"TSGWTEST\Administrator";
private string _sPassword = @"Password!Admin";
public TerminalServicesGatewayConnector(string sHost)
{
_sHost = @"\\" + sHost;
}
}
The variables _sUsername
and _sPassword
need to be set corresponding to your own setup. The namespace variable _sPath
is the place where all the WMI calls can be found for the Terminal Services Gateway server. I have heard that Terminal Services will be renamed to Remote Desktop Services. This will probably affect the namespace in future.
Here is the method to add a RAP to the gateway configuration:
public SUCCESS AddRAP(string sComputerName, out string sLogString)
{
try
{
_oConn = new ConnectionOptions();
_oConn.Impersonation = ImpersonationLevel.Impersonate;
_oConn.Authentication = AuthenticationLevel.PacketPrivacy;
_oConn.Username = _sUsername;
_oConn.Password = _sPassword;
_oConn.EnablePrivileges = true;
_oMScope = new ManagementScope(_sHost + _sPath, _oConn);
_oMScope.Options.Authentication = AuthenticationLevel.PacketPrivacy;
_oMScope.Options.Impersonation = ImpersonationLevel.Impersonate;
_oMScope.Options.EnablePrivileges = true;
_oMPath = new ManagementPath();
_oMPath.ClassName = "Win32_TSGatewayResourceAuthorizationPolicy";
_oMPath.NamespacePath = _sPath;
_oMScope.Connect();
ManagementClass processClass = new ManagementClass(_oMScope, _oMPath, null);
ManagementBaseObject inParameters =
processClass.GetMethodParameters("Create");
ManagementObject processInstance = processClass.CreateInstance();
ManagementNamedValueCollection mnvc = new ManagementNamedValueCollection();
InvokeMethodOptions imo = new InvokeMethodOptions();
inParameters["Name"] = sComputerName + "_GW";
inParameters["Description"] = "";
inParameters["Enabled"] = true;
inParameters["ResourceGroupName"] = @"RAP_TEST";
inParameters["ResourceGroupType"] = "CG";
inParameters["UserGroupNames"] = @"RAP_TEST";
inParameters["ProtocolNames"] = "RDP";
inParameters["PortNumbers"] = "3389";
mnvc.Add("Authentication", AuthenticationLevel.PacketPrivacy);
imo.Context = mnvc;
sLogString = "";
processClass.Get();
ManagementBaseObject outParameters =
processClass.InvokeMethod("Create", inParameters, imo);
if ((UInt32)outParameters["ReturnValue"] == 0)
{
sLogString = "Item created.";
return SUCCESS.SUCCESS;
}
else
{
sLogString = "ERROR: " +
((UInt32)outParameters["ReturnValue"]).ToString();
return SUCCESS.ERROR;
}
}
catch (System.Exception ex)
{
sLogString = ex.Message;
return SUCCESS.REMOTE_EXCEPTION;
}
}
Here is the method to delete a RAP from a gateway configuration:
public SUCCESS DeleteRAP(string sComputerName, out string sLogString)
{
try
{
_oConn = new ConnectionOptions();
_oConn.Impersonation = ImpersonationLevel.Impersonate;
_oConn.Authentication = AuthenticationLevel.PacketPrivacy;
_oConn.Username = _sUsername;
_oConn.Password = _sPassword;
_oConn.EnablePrivileges = true;
_oMScope = new ManagementScope(_sHost + _sPath, _oConn);
_oMScope.Options.Authentication = AuthenticationLevel.PacketPrivacy;
_oMScope.Options.Impersonation = ImpersonationLevel.Impersonate;
_oMScope.Options.EnablePrivileges = true;
_oMScope.Connect();
ObjectQuery oQuery = new ObjectQuery("SELECT * FROM " +
"Win32_TSGatewayResourceAuthorizationPolicy WHERE Name=\"" +
sComputerName + "_GW\" ");
ManagementObjectCollection oResults =
new ManagementObjectSearcher(_oMScope, oQuery).Get();
if (oResults != null && oResults.Count == 1)
{
foreach (ManagementObject oResult in oResults)
{
uint iResultCode =
(UInt32)oResult.InvokeMethod("Delete", null);
if (iResultCode == 0)
{
sLogString = "Item deleted.";
return SUCCESS.SUCCESS;
}
else
{
sLogString = "ERROR: " + iResultCode.ToString();
return SUCCESS.ERROR;
}
}
}
sLogString = "ERROR: Not Found.";
return SUCCESS.ERROR;
}
catch (System.Exception ex)
{
sLogString = "ERROR: " + ex.Message;
return SUCCESS.REMOTE_EXCEPTION;
}
}
Points of Interest
The data source for RAPs on the Terminal Services Gateway server is not designed for concurrent access. Be aware of that fact when writing a program to access these settings. Accessing RAP configuration concurrently can corrupt the RAP storage. To avoid this problem, I created a Mutex to shield concurrent access programmatically.
History
- 2009-06-29 - First version.
- 2009-07-08 - Spelling corrections.