Controlling Virtual Server through PowerShell

A number of people have been asking me about using PowerShell to control Virtual Server. This is a very cool idea - however, out of the box it does not work. If you try and access the Virtual Server COM object (with a command like: $vs=new-object –com VirtualServer.Application –Strict) it will succeed - but inspecting the object will show no data:

Virtual Server and PowerShell

The reason this happens is that PowerShell is a .Net application - and as a .Net application it does not run with sufficient privilege to be able to talk to our COM interfaces. In order to address this - you will need to make a library that allows you to set the COM security level on an object to 'impersonate'. Below is a chunk of C# code that does exactly this (I have attached this code in a file to this post as well):

using

System;
using System.Runtime.InteropServices;
using System.Reflection;

namespace Microsoft.VirtualServer.Interop
{
using System;
using System.Runtime.InteropServices;
using System.Reflection;

public class
Powershell
{
const uint EOAC_NONE = 0;
const uint RPC_C_AUTHN_WINNT = 10;
const uint RPC_C_AUTHZ_NONE = 0;
const uint RPC_C_AUTHN_LEVEL_DEFAULT = 0;
const uint RPC_C_IMP_LEVEL_IMPERSONATE = 3;

[DllImport(
"Ole32.dll",
CharSet = CharSet.Auto
)
]
public static extern int
CoSetProxyBlanket(
IntPtr pProxy,
uint dwAuthnSvc,
uint dwAuthzSvc,
uint pServerPrincName,
uint dwAuthLevel,
uint dwImpLevel,
IntPtr pAuthInfo,
uint dwCapabilities
);

public static int
SetSecurity(
object objDCOM
)
{
IntPtr dispatchInterface = Marshal.GetIDispatchForObject(objDCOM);
int hr = CoSetProxyBlanket(
dispatchInterface, //pProxy
RPC_C_AUTHN_WINNT, //dwAuthnSvc
RPC_C_AUTHZ_NONE, //dwAuthzSvc
0, //pServerPrincName
RPC_C_AUTHN_LEVEL_DEFAULT, //dwAuthnLevel
RPC_C_IMP_LEVEL_IMPERSONATE, //dwImpLevel
IntPtr.Zero, //pAuthInfo
EOAC_NONE //dwCapabilities
);
return hr;
}
}
}

You can compile this code into a DLL by saving it in a .CS file, opening the Visual Studio 2005 Command Prompt and running 'csc /t:library VSWrapperForPSH.cs'. Once you have done this - you can load this DLL into PowerShell by running '[System.Reflection.Assembly]::LoadFrom(“<<path to DLL>>”)' (note that you need to use the full path to the DLL - if the path is excluded - PowerShell will look in Windows\System32 and complain if the file is not there).

Once you have done all of this - you can now change the COM security level on an object by running '[Microsoft.VirtualServer.Interop.Powershell]::SetSecurity($objectName)'. And as you can see here - you will then be able to access the object properly:

Virtual Server and PowerShell

However - if you make any new objects (which you will):

Virtual Server and PowerShell

You will get bitten again. This is simply handled by setting the COM security on objects as you create them:

Virtual Server and PowerShell

Well. Now that we have all of that working - you can expect to see some posts from me in the near future about how to perform different tasks under PowerShell. But for now - a couple of final notes to make are:

  1. Under Vista hosts - PowerShell needs to be running 'As Administrator' for this to work (otherwise you will fail to create the first COM object)

  2. I could not have done this without the help of Mike Kolitz and Jon White (from the PowerShell team) - thanks guys!

Cheers,
Ben

VSWrapperForPSH.cs