Welcome to MSDN Blogs Sign in | Join | Help

A Web service requiring Client certificate authentication is a common scenario.

You may have a client application which needs to send the Client certificate as part of the web request for accessing the web service.

This client application may be a Windows/Console application or another Web application.

Often you will get into issues wherein you are able to send Client certificate as part of the web request from a windows/console app but not from another web app. The primary reason for this could often be around Web app not being able to send the client cert to the target Web service.

This can happen for multiple reasons, in particular account under which Web app is running doesn't have enough permissions to access the Client cert in its local certificate store.

Refer to this excellent kb for this for more details.

In this post I want to highlight ways in which you can grant access to the Web application account to access the Client certificate in its local machine store.

 

When we have to send client cert as part of the web service call from a web app we need to ensure that the client cert is installed in the Local Computer -> Personal Store on the local box (where Web app is running). By default you will see the client cert installed in the Local User Store for the user who requested and installed the cert on the machine. You need to ensure first that the client cert is installed on the Local Computer Store instead of the Local User Store and then follow any of the methods below to grant access to the private key for the account (under which your web app is running).

 

Method 1:

The above article kb gives an example of granting access using the Microsoft Windows HTTP Services Certificate Configuration Tool

 

> WinHttpCertCfg.exe -g -c LOCAL_MACHINE\MY -s " IssuedToName " -a " AccountName "

for e.g.

> WinHttpCertCfg.exe -g -c LOCAL_MACHINE\MY -s " IssuedToName " -a "Network Service"

 

There are other ways in which you can achieve the same result. This feature is in fact built in on Windows Server 2008 within the Certificate mmc console.

 

Method 2:

Using the WSE X509 Certificate tool (This tool has features that can be used to check certificate properties).

You need to download Web Services enhancements (WSE) 2.0+ SP3 for Microsoft.Net and in the install wizard ensure you select Tools as shown below:

 

image

Once installed go ahead and launch the tool. It has a clean UI. You have the option to check certificates in the Local Computer/Current user for the available stores like Personal/Trusted/Intermediate Root CA etc. If you click on View Private Key File Properties (shown below) you can directly modify the permission for private key associated with the certificate. Basically this is just a file under C:\Documents and Settings\All Users\Application Data\Microsoft\Crypto\RSA\MachineKeys on Win2k3 server and  C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys on Win2k8 server.

 

image

You may want to go ahead and give the Service account under which the web app is running Full permission on this file (modify the permissions from the Security tab).

 

Method 3:

If you are running the web app on Windows Server 2008/Vista there is a far simpler way built in the Certificate mmc.

image

Right click on the certificate and go to All Tasks -> Manage Private Keys and then give Full permission for the associated account.

 

Till next time..

Cheers!

I just wanted to add this quick post around Client certificate Mapping on IIS. This is focused on 1-to-1/Many-to-1 mapping in IIS 6.0/7.0.

If you are interested to know more about configuring Client certificate mapping in IIS 6.0 please check this post of mine and for IIS 7.0 this is an excellent article.

Recently a colleague of mine and I was working on this issue for one of our internal teams and after some real slogging we figured out that one *cannot* set this mapping at any Virtual directory/Application level in IIS.

One has to set the Client certificate mapping at the specific Web site level only!

image This is wrong!

image This is right!

I couldn't find a documentation on this so thought of putting this as a short post for general audience in case someone is scratching their head over this.

Cheers!

Here is a quick post that may help you save few hours if you get into this problem.

Symptoms

-- Out of blue you realize that when you try to create and open a new Web  site/Web Application project we do not see any of the Web controls in the Toolbox. It includes most of the sections in the Toolbox pane including Common controls, Data, Login, validation and in fact any of the controls which are associated with an Asp.Net web page.

-- When you click on 'Show All' in the toolbox you see all the Asp.Net related controls grayed out. In fact you will see most if not all, of the sections in the toolbox being grayed out except HTML controls.

image

 

Go to the Source view for the Web form in the VS IDE and add an ASP control yourself like <asp:TextBox...></asp:TextBox>

Browsing to the web page shows the control getting rendered without any issues. So seems like more of a VS IDE issue than anything to do with ASP.Net application configuration.

-- Creating a new Winform project works fine with all the relevant controls being displayed and working in the Toolbox window.

 

Resolution

This issue may happen if by mistake you had associated ASPX pages with the HTML Editor in the VS IDE. It should be ideally associated with the Web form Editor.

To resolve this, In the Solution Explorer, locate the ASPX that we are working on, right click on the page and select 'Open with...'.

You may be seeing that the current selection is HTML Editor (Default). Change it Web Form Editor and click on 'Set as Default'.

image

Close the current VS IDE and re-launch it and now we should *hopefully* be good.

In PSS, we occasionally get requests from our customers wherein they want to automatically add entries for client certificate mapping in IIS or Active Directory (AD). That is either a 1-to-1, Many-to-1 or AD mapping for the client certificate authentication for the web site. I recommend going with AD mapping because that eases the management but it finally depends upon one's need.

I am not sure but I feel there is a security breach plus annoyance when an administrator has to laboriously enter the mappings for all the accounts/certificates (I am being specific to 1-to-1/Many-to-1 here).

The concern I feel when dealing with the administrator doing it for 1-to-1 and Many-to-1 are:

a. If there are hundreds of users you need to do this manually for everyone of those accounts and it's a pain.

b. Yes, the above can be automated using a script but then the second concern that arises is that whoever is running the script has to know the passwords for all these accounts to be mapped. I think this doesn't sound good.

I have written a sample application using which users can enter the mappings themselves in the IIS's Client certificate setting, i.e. entries having the client certificate mapped to a windows account (either a local IIS or AD account) and the corresponding password.

So this is how it works:

  • User accesses this web page from their workstation which already has the client certificate installed.
  • They are authenticated over Basic with SSL.
  • Browser sends across the client certificate as part of the HTTP web request.
  • This application gathers the user account, password plus the client certificate from the incoming HTTP web request and does the mapping in IIS.

 

image

I am adding the code here in case someone may want to extract the section for automated scripting instead of using it as a web app.

This code is also attached to this post as well.

using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Security.Cryptography.X509Certificates;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.DirectoryServices;
 
/* This sample application is to automate One-to-One Client certificate mapping in IIS 6.0.
 * User should be able to access this site from the browser and select the client certificate
 * in their machine which will be mapped to their account on the IIS server for 1-to-1 mapping. 
 * You need to deploy this application on the IIS server which is hosting the website(s) which 
 * needs client certificate mapping, preferably under its own virtual directory.
 *
 * Important:
 * - Have the authentication for this web application configured to use Basic along with SSL.
 * - Have the "Accept client certificates" or "Require client certificates" selected under 
 *   <Website> -> Properties -> Directory Security -> Secure communications -> Edit -> Client certificates
 * - Ensure the website that we want the mapping for is mentioned in the web.config file associated with
 *   this application under <appSettings>
 * - In the Web.config file we are impersonating an Administrator account for this application. 
 *   <identity impersonate="true" userName="Administrator" password="myadminpassword"/>
 *   This is done because non-admin users cannot modify the IIS metabase. If you do not want users to map
 *   entries themselves through web page you can change this to <identity impersonate="true" />.
 *   In such a case only admins can add the mappings for their user accounts. Non-admins won't be able to 
 *   add the client mapping entries.
 *   This is valid for both domain or local Windows NT accounts.
 * - This app is written using .Net 2.0, ASP.Net 2.0 and above in mind. You should be able to make it work
 *   with ASP.Net 1.1 as well.
 * - You may prefer to run this application under its own dedicated application pool to ensure stability and security.
 * 
 * DISCLAIMER: The code is not tested for production scenarios so use it at your own risk. 
 *             In case one wants to use batch scripting etc or some kind of console app instead 
 *             of web app you can extract the code section from this page which should work fine for the job.
 */
 
public partial class _Default : System.Web.UI.Page 
{
    protected void Page_Load(object sender, EventArgs e)
      {
        Response.Write("<B>Client Certificate One-to-One Mapping Application:</B><BR><HR>");
        Response.Write("Serial number: " + Request.ClientCertificate.SerialNumber + "</BR><HR>");
        Response.Write("Issuer: " + Request.ClientCertificate.Issuer + "</BR><HR>");
        Response.Write("Subject Name: " + Request.ClientCertificate.Subject + "</BR><HR>");
        if (Request.ClientCertificate.IsPresent)
        {
            Response.Write("Validity<BR>");
            Response.Write("&nbsp;&nbsp;&nbsp;&nbsp;Not before: " + Request.ClientCertificate.ValidFrom + "</BR>");
            Response.Write("&nbsp;&nbsp;&nbsp;&nbsp;Not after: " + Request.ClientCertificate.ValidUntil + "</BR><HR>");
        }
        else
            Response.Write("<B>There is no client certificate sent along with the request!</B><HR>");
 
        Response.Write("Authenticated User: " + Request.ServerVariables["AUTH_USER"] + "</BR><HR>");
        Response.Write("Authentication Type: " + Request.ServerVariables["AUTH_TYPE"] + "</BR><HR>");
    }
    protected void Button1_Click(object sender, EventArgs e)
    {
        string user = Request.ServerVariables["AUTH_USER"];
        string password = Request.ServerVariables["AUTH_PASSWORD"];
        string clientCertMappingName = "Mapping for " + user;  // <--- Our One-to-One Mapping name for the entry
        HttpClientCertificate cert = Request.ClientCertificate;
        /*
          If you want to map a client certificate located on the disk instead of the one as part of the 
          HTTP Web request try the code below.
          
          X509Certificate certificate = X509Certificate2.CreateFromCertFile(@"c:\cert.cer");
          X509Certificate certificate = cert.Certificate;
          byte[] certHash = certificate.GetRawCertData();
        */
        byte[] certHash = Request.ClientCertificate.Certificate;
        try
        {
        //Get the name of the Web site for which mapping has to be done from the App settings in the web.config file.
        string friendlyWebsiteName = ConfigurationManager.AppSettings["websitename"].ToString();
 
        //Get the Site Identifier based on the friendly name of the Web Site.
        string siteId = getsiteid(friendlyWebsiteName);
 
        if (siteId != null)
            {
            string sitePath = "IIS://localhost/W3SVC/" + siteId + "/IIsCertMapper";
            using (DirectoryEntry de = new DirectoryEntry(sitePath))
            {
                de.Invoke("CreateMapping", new object[] { certHash, user, password, clientCertMappingName, true });
            }
            Response.Write("Account Mapped: <B>" + Request.ServerVariables["AUTH_USER"] + "</B></BR>");
            Response.Write("Mapping Name: <B>" + "Mapping for " + Request.ServerVariables["AUTH_USER"] + "</B></BR>");
            Response.Write("Web Site: <B>" + friendlyWebsiteName + "</B></BR>");
            }
        else
            {
            Response.Write("<B>Web Site does not have a valid Site ID. Ensure we have the correct site name in the config file for this app.</B>");
            }
        }
        
        catch (System.Runtime.InteropServices.COMException)
        {
            Response.Write("A COM exception occurred while setting up the mapping.");
        }
        catch (SystemException)
        {
            Response.Write("An error occurred while setting up the mapping.");
        }
        catch (Exception)
        {
            Response.Write("An error occurred while setting up the mapping.");
        }
       
    }
 
    public string getsiteid(string websitename)
    {
        DirectoryEntry root = new DirectoryEntry("IIS://localhost/W3SVC");
        try
        {
            string siteid = null;
            foreach (DirectoryEntry de in root.Children)
            {
                if (de.SchemaClassName == "IIsWebServer")
                {
                    if (websitename.ToUpper() == de.Properties["ServerComment"].Value.ToString().ToUpper())
                        siteid = de.Name;
                }
            }
            if (siteid == null) return null;
            return siteid;
        }
        catch
        {
            return null;
        }
        finally
        {
            root.Close();
        }
    }
}

 

Ciao

Nice weekend!

I recently had the privilege to get access to a machine from a colleague of mine. It was a Windows server 2003 server and I had to test some ASP.Net application for one of my pet projects. I was focusing completely on the project at hand before I was completely taken off by a surprise, although not a pleasant one.

I found that my application was throwing the following exception, in fact forget my own application even a test Asp.Net 2.0 page having just a one word was failing. Also this happened for web resources hosted directly in IIS. If you run this app from within Cassini (ASP.Net Web server) you may not see this issue at all. This happened for both Website as well as WAP based applications hosted in IIS.

Server Error in '/' Application.
--------------------------------------------------------------------------------

Unable to load DLL 'bcrypt.dll': The specified module could not be found. (Exception from HRESULT: 0x8007007E) 
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 

Exception Details: System.DllNotFoundException: Unable to load DLL 'bcrypt.dll': The specified module could not be found. (Exception from HRESULT: 0x8007007E)

Source Error: 

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.  

Stack Trace: 


[DllNotFoundException: Unable to load DLL 'bcrypt.dll': The specified module could not be found. (Exception from HRESULT: 0x8007007E)]
   Microsoft.Win32.Win32Native.BCryptGetFipsAlgorithmMode(Boolean& pfEnabled) +0
   System.Security.Cryptography.Utils.get_FipsAlgorithmPolicy() +140
   System.Security.Cryptography.RijndaelManaged..ctor() +13
   System.Web.Configuration.MachineKeySection.ConfigureEncryptionObject() +232
   System.Web.Configuration.MachineKeySection.EnsureConfig() +156
   System.Web.Configuration.MachineKeySection.GetEncodedData(Byte[] buf, Byte[] modifier, Int32 start, Int32& length) +37
   System.Web.UI.ObjectStateFormatter.Serialize(Object stateGraph) +166
   System.Web.UI.ObjectStateFormatter.System.Web.UI.IStateFormatter.Serialize(Object state) +4
   System.Web.UI.Util.SerializeWithAssert(IStateFormatter formatter, Object stateGraph) +37
   System.Web.UI.HiddenFieldPageStatePersister.Save() +79
   System.Web.UI.Page.SavePageStateToPersistenceMedium(Object state) +105
   System.Web.UI.Page.SaveAllState() +236
   System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +1099

 


--------------------------------------------------------------------------------
Version Information: Microsoft .NET Framework Version:2.0.50727.3053; ASP.NET Version:2.0.50727.3053 

This was quite perplexing as I couldn't find much information on this across the net. One incident I found talked about un-installation of  MS07-040 security update. I was running on .Net framework 2.0 Sp2, Windows Server 2003 SP2. No luck with it. I had no clues about this dll which was missing as in the exception and why the heck it was looking for it in the first place.

The interesting part here was that the call stack looked like having some encryption/decryption algorithm (RijndaelManaged) being used perhaps related to viewstate. I finally had to disable the attribute EnableViewStateMac="false" for the web page to make it work, but well that may not be an option all the time for everyone.

If you face such a scenario just don't go ahead with reinstallation of .Net framework 2.0, it may not help you but only drain your precious time.

 

Analysis/Resolution

From this KB article this is what RijndaelManaged is all about.

"ASP.NET 2.0 uses the RijndaelManaged implementation of the AES algorithm when it processes view state data. The ReindaelManaged implementation has not been certified by the National Institute of Standards and Technology (NIST) as compliant with the Federal Information Processing Standard (FIPS). Therefore, the AES algorithm is not part of the Windows Platform FIPS validated cryptographic algorithms."

To work around this either set EnableViewStateMac to false or else add the following entry as mentioned in the kb under <system.web> section for the web application.

<machineKey validationKey="AutoGenerate,IsolateApps" decryptionKey="AutoGenerate,IsolateApps" validation="3DES" decryption="3DES"/>

ASP.NET use the Triple Data Encryption Standard (3DES) algorithm to process view state data instead of the AES (Rijndael) algorithm. Remember this is comparatively weaker than Rijndael based encryption and hence your application will be comparatively insecure.

 

*Note that the error message in the above article is not exactly the same as what I saw here for this post but the resolution remains the same :-).

 

Till next time..Beer mug

I recently had the pleasure of working on the implementation of Asp.Net Request Routing feature that comes in bundled with .Net Framework 3.5 SP1. This is a cool stuff wherein care has been taken to ensure it can be used independently of MVC Framework. It can be used with Dynamic Data as well as implemented for any generic Asp.Net 2.0 application for Request routing functionality.

*Remember this is different from ASP.NET 2.0's URL Mapping.

The aim of this post is so obvious for many but I am writing this post because I couldn't get any pointers on this over the Internet, although I know I should have checked the end resolution in the very beginning; silly of me.

 

We were getting the following exception when we we were trying to route a request. Exception was being thrown from within our custom class implementing IRouteHandler.

Server Error in '/' Application. 
________________________________________
Object reference not set to an instance of an object. 
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 

Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.

Source Error: 
An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below. 

Stack Trace: 

[NullReferenceException: Object reference not set to an instance of an object.]
   System.Collections.Generic.Dictionary`2.GetEnumerator() +38
   Mycustomdll.Net.RouteHandler`1.GetHttpHandler(RequestContext requestContext) in C:\abcdedfgh\myprojectforfun\asdfsdf\Net\RouteHandler.cs:37
   System.Web.Routing.UrlRoutingModule.PostResolveRequestCache(HttpContextBase context) +106
   System.Web.Routing.UrlRoutingModule.OnApplicationPostResolveRequestCache(Object sender, EventArgs e) +80
   System.Web.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +68
   System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +75

This application was failing for one of my customers in his environment. I was finally able to reproduce this problem locally on one of my machines as well. Found that ideally this should work fine if you have the latest build.

On debugging found it was failing at the section shown below in the custom Routehandler class while trying to iterate through the RouteValueDictionary collection even though there were items in the collection. It was failing with the null reference exception.

public class RouteHandler:IRouteHandler
     {
        ...... 
        ......
       public IHttpHandler GetHttpHandler(RequestContext requestContext)
        {
             foreach (var value in requestContext.RouteData.Values) <------------
             {
                 .....
             }
             .....
             .....
             return (Page)BuildManager.CreateInstanceFromVirtualPath(VirtualPath, typeof(Page));
        }
     } 

You may see this crash behavior if you are running with the build version of System.Web.routing as shown below. I assume this is from the ASP.Net 3.5 SP1 Beta release, not sure. 

image

In order to rectify this issue ensure you move to the latest build for System.Web.Routing.dll as well as for System.Web.Abstractions.dll.

image

Lesson: Never try to run Beta/CTP version etc. on the production environment. Ensure you have the latest bits deployed.

 Coffee-cup

If you are contemplating to move over (upgrade) to IIS 7.0 from your existing Web hosting on either IIS 5.0 or 6.0 this is a very useful Technical White Paper link. It talks about various aspects of improvements in IIS 7.0 dealing with Management, Performance, Extensibility, Security and Deployment.

Migrating a Large, High-Volume Web Site to Internet Information Services 7.0

Cheers!

If you have a Web folder (WebDAV) hosted on IIS using Basic Authentication there are a few things you need to ensure such that any client accessing the resource does not get into failures.

If your WebDAV site is restricted using Basic authentication and you are trying to access it over HTTP, access may fail. Over HTTP it may keep prompting you for authentication or else will show the following error:

"Internet Explorer could not open http://ComputerName/WebDav as a Web

Folder. Would you like to see its default view instead?"

image

Windows Vista will fail to connect to the server using Basic authentication which is insecure over the network. Vista/XP requires SSL connection to be used with Basic. However you can connect to the Web folder if you set the following registry key on the client machine to 2.

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\WebClient\Parameters\BasicAuthLevel

The BasicAuthLevel can be set to the following values:

  • 0 - Basic authentication disabled
  • 1 - Basic authentication enabled for SSL shares only (Default)
  • 2 or greater - Basic authentication enabled for SSL shares and for non-SSL shares

Check this article for more on this Q841215

 

Also You may not be able to edit an Office 2007 document from within a Web folder from your client. In such a case ensure the following registry key is set:

HKEY_CURRENT_USER\Software\Microsoft\Office\12.0\Common\Internet\OpenDocumentsReadWriteWhileBrowsing = 1

Refer to this Q870853 for more around Office 2003/2007.

* If you are still having issues editing the Office document ensure that there is no Certificate Security alert or Error when browsing to the same Web folder URL. If you do see a security alert because of any of the three reasons associated with it ensure they are fixed. 

Also these should be handy for other WebDAV related issues, KB912152 and KB900900.

 

Until next time...

If you have a FTP site hosted on IIS which receives a burst of client connections at specific intervals and you see the connections getting reset from server-end please read on.

Let's say you have over 1500 concurrent FTP client connections being made at every 'n' minute you may see that the IIS FTP server is resetting many of those client connections. From a FTP client (the typical ftp.exe from the command prompt) you may see the following:

"Connection closed by remote host"

At other times when there is less load on the server things will look good.

Capturing a network trace may show you that a 3-way handshake between the client and the server is taking place and after that server resets the connection. You don't even get to a point wherein you can enter your credentials to access the FTP site. FTP site's maximum concurrent connections property (MaxConnections) is well above this value.

If you are in a similar scenario wherein a sudden bursts of concurrent client connections is leading to the FTP server resetting the requests you may want to play with some combinations of the settings mentioned below. It may be that you are hitting some connection limit for your server. It may not be the total number of connections being made but the total number of simultaneous connections that is causing the problem.

You will have to play with the following settings on the IIS 6.0 server:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\InetInfo\Parameters
    ListenBacklog = xxx (Range: 1 -250, Default: 25)
    MaxPoolThreads = xx (Range: 0 - 4,294,967,295 (unlimited), Default: 4)
    DisableBacklogMonitor=1

And in the metabase, under /LM/MSFTPSVC/
    ServerListenBacklog=”xxx”

* The ServerListenBacklog property specifies the number of outstanding sockets that can be queued. The value is based on the AcceptEx operating system parameter, and the server size specified in the ServerSize property. Valid values for this property range from 5 to 500.

Reset IIS after making the above changes.

See this link for more details on the Global registry entries for IIS 6.0.   

The ListenBacklog setting in the registry controls how many active connections are queued by winsock if IIS is unable to service new requests.  ServerListenBacklog is used by IIS to proactively create sockets to handle incoming requests. This setting adjusts the number of outstanding sockets that your Web server can queue. Setting ServerListenBacklog to a higher value allows IIS to handle a larger number of requests, while ListenBacklog helps the system buffer the connection spikes if the number of new connections briefly exceeds what IIS can service immediately.

Although the range for ListenBacklog  & ServerListenBacklog is given I had seen a scenario wherein we had to adjust the value to a higher number than the given range. You may need to tweak the combination yourself!

*I give credit to Rob Patrick from PSS IIS Escalation team for this post

I was a happy man until a few hours back when I was playing with some C, C++ programs built using Visual Studio 2008 on my work and home machines. I could write some program on my workstation, copy the solution to my personal laptop at home and thereby continue from where I left in office. Things  were good until I installed the latest Windows SDK v6.1 on my Vista laptop while trying my hands on Windows PowerShell.

I realized a day or so later after installing SDK v6.1 that now I could not build my application written in C or C++. I could compile it but not link it during build process from within Visual Studio. Funny though I realized I could not even build a very basic Win32 console application.

I started getting the following error:

fatal error LNK1181: cannot open input file 'kernel32.lib' 

I did something while troubleshooting it and now also started seeing:

Error spawning 'rc.exe'. Project myProject

I did check on the Internet and found all sorts of reasoning around this. One thing looked clear that installing the latest SDK v6.1 had caused me all these headaches. Some links suggested rebooting the server, some talked about  low system  memory, some said remove the latest SDK v6.1, and some also suggested to reinstall Visual Studio. But somehow I felt this can be fixed without reinstallation of VS2008 or un-installation of SDK v6.1 or etc etc and etc.

I opened the cool Process Monitor tool and found the following:

28836    11:51:31.5538847    devenv.exe    5528    QueryOpen    E:\Program Files\Microsoft Visual Studio 9.0\VC\lib\kernel32.lib    NAME NOT FOUND    
28837    11:51:31.5539987    devenv.exe    5528    QueryOpen    E:\Program Files\Microsoft Visual Studio 9.0\VC\lib\kernel32.lib    NAME NOT FOUND    
28839    11:51:31.5542065    devenv.exe    5528    QueryOpen    E:\Program Files\Microsoft Visual Studio 9.0\VC\atlmfc\lib\kernel32.lib    NAME NOT FOUND    
28841    11:51:31.5543630    devenv.exe    5528    QueryOpen    E:\Program Files\Microsoft Visual Studio 9.0\VC\atlmfc\lib\kernel32.lib    NAME NOT FOUND    
28842    11:51:31.5544705    devenv.exe    5528    QueryOpen    E:\Program Files\Microsoft Visual Studio 9.0\VC\atlmfc\lib\i386\kernel32.lib    PATH NOT FOUND    
28844    11:51:31.5545935    devenv.exe    5528    QueryOpen    E:\Program Files\Microsoft Visual Studio 9.0\VC\atlmfc\lib\i386\kernel32.lib    PATH NOT FOUND    
28846    11:51:31.5547253    devenv.exe    5528    QueryOpen    D:\Program Files\Microsoft SDKs\Windows\v6.1\lib\kernel32.lib    PATH NOT FOUND    
28847    11:51:31.5548633    devenv.exe    5528    QueryOpen    D:\Program Files\Microsoft SDKs\Windows\v6.1\lib\kernel32.lib    PATH NOT FOUND    
28848    11:51:31.5550927    devenv.exe    5528    QueryOpen    D:\Program Files\Microsoft SDKs\Windows\v6.1\lib\kernel32.lib    PATH NOT FOUND    
28849    11:51:31.5552863    devenv.exe    5528    QueryOpen    D:\Program Files\Microsoft SDKs\Windows\v6.1\lib\kernel32.lib    PATH NOT FOUND    
28850    11:51:31.5556190    devenv.exe    5528    QueryOpen    E:\Program Files\Microsoft Visual Studio 9.0\kernel32.lib    NAME NOT FOUND    
28851    11:51:31.5559045    devenv.exe    5528    QueryOpen    E:\Program Files\Microsoft Visual Studio 9.0\kernel32.lib    NAME NOT FOUND    
28852    11:51:31.5561361    devenv.exe    5528    QueryOpen    E:\Program Files\Microsoft Visual Studio 9.0\lib\kernel32.lib    PATH NOT FOUND    
28853    11:51:31.5563280    devenv.exe    5528    QueryOpen    E:\Program Files\Microsoft Visual Studio 9.0\lib\kernel32.lib    PATH NOT FOUND    

It was crystal clear that while trying to link Visual Studio was encountering a file not found kind of error and hence could be the resulting failure.

------ Build started: Project: aa, Configuration: Debug Win32 ------
Linking...
LINK : fatal error LNK1104: cannot open file 'kernel32.lib'
Build log was saved at "file://xxxxxxxxxxx\Visual Studio 2008\Projects\myProject\myProject\Debug\BuildLog.htm"
myProject - 1 error(s), 0 warning(s)
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

Similarly rc.exe showed NOT FOUND.

It looked simple yet intriguing as to why the default path was changed for Visual Studio.

I realized that the path it was trying to look at for these files did not even exist, lest I could just have copied these files to the corresponding location.

Looked at the Environment variables from Visual Studio 2008 command prompt and found that WindowsSdkDir environment variable was pointing to D:\Program Files\Microsoft SDKs\Windows\v6.1\ which did not exist.

Tried changing this to the path where I did find the above files on my box which was pointing to C:\program files\microsoft sdks\windows\v6.0A\ (So yes it pretty much looked like after installing SDK v6.1 my environment variable WindowsSdkDir had changed). Tried logging off/rebooting, no luck.

Finally figured out that in order to get things back to normal I had to update the following registry key:

HKEY_CURRENT_USER\SOFTWARE\Microsoft\MicrosoftSDKs\Windows\CurrentInstallFolder

I changed this value to point to the original location which was C:\Program Files\Microsoft SDKs\Windows\v6.0A\ in my case.

I wasted a lot of time on this, so thought of sharing with a wider audience.

bye!

The version of Setspn.exe that came with Microsoft Windows Server 2000/2003 Support Tools did not have features to detect duplicate SPNs. The new version of Setspn.exe that comes bundled with Windows Server 2008 utilities has some really cool features. For someone dealing with the dreaded Kerberos authentication failure issues on a daily basis like me it's a sigh of relief.

If you try the following command on the Windows Server 2008 you will see the various new options (or switches) available.

image

Notice the modifiers/switches:

-F = perform the duplicate checking on forestwide level

-S = add arbitrary SPN after verifying no duplicates exist

-Q = query for existence of SPN

-X = search for duplicate SPNs

 

Searching for duplicate SPNs using Setspn.exe:

D:\>setspn -X http/www.test.com
Processing entry 0
http/www.test.com is registered on these accounts:
    CN=mstest,CN=Users,DC=<some-DC-primary>,DC=<some-DC-secondary>
    CN=<IIS-servername>,OU=Domain Controllers,DC=<some-DC-primary>,DC=<some-DC-secondary> 

found 1 group of duplicate SPNs.

Searching for the existence of an SPN in the domain:

D:\>setspn -Q http/www.test.com
CN=<IIS-servername>,OU=Domain Controllers,DC=<some-DC-primary>,DC=<some-DC-secondary>
    http/www.test.com
    ldap/2334590-45566-113f....
    HOST/<IIS-servername>
    HOST/<IIS-servername.<some-DC-primary>.<some-DC-secondary>
    .......
    .......
CN=mstest,CN=Users,DC=<some-DC-primary>,DC=<some-DC-secondary>
    http/www.test.com 

Existing SPN found!

Adding an arbitrary SPN after verifying no duplicates exist in the domain:

D:\>setspn -S http/www.test.com <IIS-servername>
CN=mstest,CN=Users,.<some-DC-primary>.<some-DC-secondary>
    http/www.test.com 

Duplicate SPN found, aborting operation!

Adding an arbitrary SPN after verifying no duplicates exist in the forest:

D:\>setspn -F -S http/www.test.com <IIS-servername>
Operation will be performed forestwide, it might take a while.
CN=mstest,CN=Users,DC=<some-DC-primary>,DC=<some-DC-secondary>
    http/www.test.com
CN=mstest1,CN=Users,DC=<some-DC-primary>,DC=<some-DC-secondary>
    http/www.test.com 

Duplicate SPN found, aborting operation! 

So what does this mean? It means you no longer have to depend upon boggling commands using LDIFDE or your own custom scripts to find out the duplicate SPNs. This is a good news indeed!

*Prior to this using Windows Server 2000/2003 Support Tools we could use commands using LDIFDE to find duplicate SPNs as below:

Syntax:

ldifde -f <filename> -d "<dc=domain-netbiosname,dc=primary-domain>" -l serviceprincipalname -r "(serviceprincipalname=<serviceprincipalname-to-check-for-duplicates>)" -p subtree

For example, if the domain name is test.abcd.com and the site URL is http//test.abcd.com command should be as shown below:

ldifde –f C:\log.txt -d "dc=test, dc=abcd, dc=com"-l serviceprincipalname –r "(serviceprinicpalname=http/test.abcd.com)" -p subtree

With the newer version of Setspn hopefully the dependency on the above command should reduce drastically.

Till next time,

Cheers!

In continuation to one of my earlier posts which focused on IIS 6.0 this post is more about the confusion that may arise around SPNs for setting up Kerberos authentication in IIS 7.0. IIS 7.0 has a new Kernel-mode authentication feature using which the ticket for the requested service is decrypted using Machine account (Local system) of the IIS server. It no longer depends upon the application pool Identity for this purpose by default and in turn improves the performance.

Here is how it looks like.

image

image

So what does this mean?

You no longer need to worry about the correlation between HTTP SPNs and the Application pool Identity that was required in the earlier version i.e. IIS 6.0. But that's not blindly true. There has been some confusion whether we don't have to care at all about SPNs or may have to depending upon the settings. Here is a checklist to give more clarity for different scenarios that you may fall under:

SCENARIO 1a

IIS 7.0 Web Site/Application  
Authentication Integrated Windows authentication
Application Pool Identity NETWORK SERVICE
Kernel-Mode authentication Enabled (<attribute name="useKernelMode" type="bool" defaultValue="true" /> in the ApplicationHost.config file)
Site URL Accessed with the NetBIOS name, like http://<myIISserver-NetBIOS-name>/Default.aspx

SPNs will be required ONLY for the IIS machine account:

HOST/<myIISserver-NetBIOS-name>
HOST/<myIISserver-NetBIOS-name.fully-qualified-domainname> for e.g. HOST/myIISserver.mydomain.com

***Note: By default HOST/<myIISserver-NetBIOS-name> and HOST/<myIISserver-NetBIOS-name.fully-qualified-name> is already added for the machine account when a machine is added to a domain and HTTP forms a part of HOST. So you may not have to do anything special here for SPNs. Everything should be set by default.

You can check the set of existing SPNs for the machine account by running the following command:

> Setspn.exe -L <myIISserver-NetBIOS-name> or directly using a Snap-in like Adsiedit.msc.

 

SCENARIO 1b

IIS 7.0 Web Site/Application  
Authentication Integrated Windows authentication
Application Pool Identity Custom account for e.g. Domain1\Username1
Kernel-Mode authentication Enabled (<attribute name="useKernelMode" type="bool" defaultValue="true" /> in the ApplicationHost.config file)
Site URL Accessed with the NetBIOS name, like http://<myIISserver-NetBIOS-name>/Default.aspx

The SPN requirements remain the same as above. You don't have to add SPNs like http/<myIISserver-NetBIOS-name> for the Domain1\Username1 unlike in IIS 6.0 (where we had to add an SPN of the form http/<myIISserver-NetBIOS-name> for the Application Pool identity).

SPNs will be required ONLY for the IIS machine account:

HOST/<myIISserver-NetBIOS-name>
HOST/<myIISserver-NetBIOS-name.fully-qualified-domainname> for e.g. HOST/myIISserver.mydomain.com

***Note: By default HOST/<myIISserver-NetBIOS-name> and HOST/<myIISserver-NetBIOS-name.fully-qualified-name> is already added for the machine account when a machine is added to a domain and HTTP forms a part of HOST. So you may not have to do anything special here for SPNs. Everything should be set by default.

You can check the set of existing SPNs for the machine account by running the following command:

> Setspn.exe -L <myIISserver-NetBIOS-name> or directly using Snap-in like Adsiedit.msc.

 

SCENARIO 2a

IIS 7.0 Web Site/Application  
Authentication Integrated Windows authentication
Application Pool Identity NETWORK SERVICE
Kernel-Mode authentication Enabled (<attribute name="useKernelMode" type="bool" defaultValue="true" /> in the ApplicationHost.config file)
Site URL Accessed with a Custom Host name, like http://www.mysite.com


SPNs will be required ONLY for the IIS machine account in the following format:

HTTP/<site-custom-name> for e.g. HTTP/www.mysite.com

You can add an SPN using Setspn.exe like

> Setspn -a http/<site-custom-name> <myIISserver-NetBIOS-name> 

where <myIISserver-NetBIOS-name> is the IIS machine account and <site-custom-name> is the custom host/host header name for the Web Site URL.

e.g. > Setspn -a http/www.mysite.com <myIISserver-NetBIOS-name>
*The command is NOT case sensitive

You can check the existing set of SPNs for the machine account by running the following command:

> Setspn.exe -L <myIISserver-NetBIOS-name>

 

SCENARIO 2b

IIS 7.0 Web Site/Application  
Authentication Integrated Windows authentication
Application Pool Identity Custom account for e.g. Domain1\Username1
Kernel-Mode authentication Enabled (<attribute name="useKernelMode" type="bool" defaultValue="true" /> in the ApplicationHost.config file)
Site URL Accessed with a Custom host/Host header name, like http://www.mysite.com


SPNs will be required ONLY for the IIS machine account and NOT for Domain1\Username1 account unlike in IIS 6.0.

HTTP/<site-custom-name> for e.g. HTTP/www.mysite.com

You can add an SPN using Setspn.exe like

> Setspn -a http/<site-custom-name> <myIISserver-NetBIOS-name> where <myIISserver-NetBIOS-name> is the IIS machine account and <site-custom-name> is the custom host/host header name for the Web Site URL.

e.g. > Setspn -a http/www.mysite.com <myIISserver-NetBIOS-name>
*The command is NOT case sensitive  

You can check the existing set of SPNs for the machine account by running the following command:

> Setspn.exe -L <myIISserver-NetBIOS-name>

 

Special case of running IIS 7.0 in a WEB FARM

If you are running IIS 7.0 server in a Web farm the KDC will not know in advance which individual server the request may go to and hence ticket decryption may fail. Hence in such a scenario instead of registering SPNs under a specific machine account use a domain account. I am not a SharePoint guy but based on what I have read on the Web this scenario is also applicable to a single SharePoint server configuration.

There are two ways to go:

Either

Disable Kernel mode authentication and follow the general steps for Kerberos as in the previous IIS 6.0 version. Refer this

Or,

[Recommended for Performance reasons]

Let Kernel mode authentication be enabled and the Application pool's identity be used for Kerberos ticket decryption. The only thing you need to do here is:

1. Run the Application pool under a common custom domain account.

2. Add this attribute "useAppPoolCredentials" in the ApplicationHost.config file.

<system.webServer>
   <security>
      <authentication>
         <windowsAuthentication enabled="true" useKernelMode="true" useAppPoolCredentials="true" />
      </authentication>
   </security>
</system.webServer>

Remember there is no GUI setting for this. You need to modify the ApplicationHost.config file from

<%SystemDrive%>/Windows/System32/inetsrv/config folder on the IIS 7.0 machine.

3. Add the SPNs in the form:

http/<virtualhost-name> and

http/<virtualhost-name.fully-qualified-name>  for the Application Pool Identity.

Ensure that we don't have such an entry for SPNs for any other account including IIS server machine account.

***If we have the same SPN mapped to multiple accounts (be it a machine or an user account) it leads to Duplicate SPNs and will break Kerberos.

Hope this helps!

I was working on an FTP issue the other day on IIS 7.0 and I missed something very basic which could have resolved my issue way back had I noticed the nuances of this new model. Normally people tend to go ahead with restarting IIS services for some changes done in the configuration to make sure everything is fresh. Like running IISRESET from the cmd prompt or Restarting IIS services from the IIS manager itself. But in IIS 7.0 if you want to restart FTP services ensure you don't go by the above. This will restart IISADMIN, WAS and W3SVC services but not FTPSVC. IIS 7.0 OOB Secure FTP module runs under a separate process Svchost.exe. So doing an IISRESET won't help for FTP changes to take effect.

Ensure you run these two commands instead:

Start -> Run -> cmd

At the cmd prompt:

>net stop ftpsvc
The Microsoft FTP Service service is stopping.
The Microsoft FTP Service service was stopped successfully.

>net start ftpsvc
The Microsoft FTP Service service is starting.
The Microsoft FTP Service service was started successfully.

Hope this helps one who may have unnoticed this change.

I am sharing here some of the general + elusive + ignored + must-have info that you may want to recheck when you are troubleshooting a kerberos cum delegation failure scenario and feel like reaching nowhere near the end of the tunnel (resolution!). These are my personal checklists based on experiences of troubleshooting kerberos related gotchas. I had also posted my first article on troubleshooting kerberos issues way back in January 2007. This article is a kind of continuation to it since I still see a lot of people missing some finer points here and there. Please check this post for the general kerberos checklist.

So here I go...

  • Kerberos was designed and is supported in Intranet scenarios. If you are trying to make it work over an Internet environment you may want to recheck other options (unless you are going ahead with Protocol transition for e.g. from Basic/NTLM to kerberos). Remember that for kerberos to work, the client (e.g. client browser) should be able to connect to the Domain Controller(KDC) to acquire the tickets. If your clients are coming over the Internet they may not be having access to the Domain Controller. Most security conscious organizations keep their DC away from Internet facing network in order to reduce the likelihood of it getting compromised. You may have to check with the firewall/proxy settings etc. and more...to make this work which I personally feel is not a good idea.

 

  • Kerberos will work for resources (client-IIS-Backend DB etc.) in the same domain or in trusted domains within the same forest. Either have mutual trust (preferable) between the domains in the forest or at least have the IIS domain trust the client's domain. If your clients are coming from a domain across the forest with an external trust we need to do extra work. Refer to this article. I am not an AD guy Embarrassed.

       Here is an excerpt from the same article:

The Windows Server 2003 family supports domain trusts and forest trusts. We know what domain trusts are: they allow a user to authenticate to resources in another domain. Like always, all domain trust relationships have only two domains in the relationship: the trusting domain and the trusted domain. There are one-way trusts (unidirectional) and two-way trusts (bi-directional) and a Windows Server 2003 domain can establish a trust among other Windows 2000/2003 domains in the same or different forest, Windows NT 4.0 domains and Kerberos V5 realms. In Windows 2000, if users in one forest needed access to resources in a second forest, an administrator could create an external trust relationship between the two domains, which is one-way and non-transitive. This meant that in order to extend your trust to other domains in the forests you had to explicitly configure each and every one of them.

Windows Server 2003 offers a forest trust: two-way Kerberos-based transitive trust between Windows Server 2003 forests, enabling a transitive trust between all the domains in the two forests. Forest trusts are established between the root domain of both forests and can be either one way or two way. A Few things to remember are to make sure all domain controllers in both forests are running Windows Server 2003, with a correctly configured DNS infrastructure and forest functionality level set to Windows Server 2003 mode in both forests.

 

  • Many a times you will see something as shown below when connecting to a web site over Windows integrated authentication. You may have checked all the basic settings for kerberos and things look okay, yet somehow mysteriously this is failing to work with kerberos. After three attempts it will fail with 401.

      You typed in http://www.test.com in the browser and it seems to be connecting to some other machine   name  as  shown below in the picture.

image

Look at the IE prompt which shows that we are trying to connect to testkrb.saurabh1.com although web site URL in the browser's address box shows we are trying to reach the site www.test.com. Ideally we should have seen "Connecting to www.test.com" and not "connecting to testkrb.saurabh1.com". Equivalently try a ping to www.test.com and see what it resolves to.

image

If you see such a scenario it's time to check whether the web site URL is an Alias(CNAME) or a DNS Host (A) Record. There is a known issue with using Alias for a site which may not allow kerberos to work. There are some details which I don't want to get into at this point, probably some other day. In short, it tries to look into the KDC based on the SPN http/testkrb.saurabh1.com and not an SPN of the form http/www.test.com.

Solution:

Server side: Either go ahead and change the DNS entry to add www.test.com as a DNS Host (A) Record and not CNAME.

or,

Client side: Apply this hotfix to IE browser on the client(s) (I don't see this as a good option).

 

  • I would recommended to use a host name instead of an IP address to access a web site meant for a kerberos based authentication. You may see it working just fine even with IP address in some scenarios but then it may pose problems when we have client and servers in different domains etc. You may get into an issue wherein domain2 will not give any referral back to to the client to look into domain1 for the SPN. This can occur if IP address is being used to look for a service. In such a case even after adding SPN's for IP addresses, Kerberos won't work and will fall back to NTLM.

 

  • If your web site is configured to use a non-default HTTP port like 81 instead of 80, users will access the site as http://www.test.com:81 and not http://www.test.com (browsers append ':80' as the default port if none specified). Here lies the confusion when you add SPNs for the web site. Don't have an SPN with the port number appended even if you are running your site on a non-default port.

If the site is accessed as http://www.test.com:8080 SPN will still be of the form http/www.test.com  and  not http/www.test.com:8080.

      Refer to this article. It confuses me further but I would suggest go ahead with the default as above.

 

  • Consider a scenario wherein two applications http://servername/app1 and http://servername/app2 are running under a NETWORK SERVICE & a domain user Application Pool identities respectively .
    The SPNs requested will be http/servername in both the cases, and since we can’t have duplicate SPNs; kerberos may not work for either of the applications. We need to then either use the same Application Pool identity or separate host headers for the web sites and set SPNs accordingly. NOTE: This issue is taken care of in IIS 7.0 with Kernal mode authentication.

    Again,if you are using two web sites with same name but different ports like http://servername:81 and http://servername:82; by default IE will request a ticket for the same SPN HTTP/servername.

    We would then need an hotfix for the client machines, Refer to this.

     

  • When do we have Duplicate SPNs leading to kerberos not working?

Duplicate SPN arises from the fact that the same SPN is mapped to multiple accounts, it may be a machine or an user account. Doesn't matter. Mapping to multiple accounts will lead to duplicate SPNs!

*Remember: You can have multiple different SPNs registered under the same account but not vice-versa, i.e. you should *not* have the same SPN registered under multiple accounts.

 

  • IIS uses NTLM credentials when accessing a resource for a local request coming to it (i.e. client say IE, and IIS are on the same box). It may use Kerberos or NTLM from a separate client machine depending on the setup.The best way to check if delegation is working is from a client machine which is not same as the IIS server. NTLM doesn't support delegation. Kerberos does!

 

  • At times making sure all the settings on the client, IIS, AD, back-end (if any) to make kerberos work properly doesn't help, and in such cases make sure that we purge all the kerberos tickets using Klist or Kerbtray on the client. In fact if possible logoff and re-login to the client machine from where you are testing the web application for kerberos authentication so that the client is issued a fresh ticket.

 

Till next time...Martini Glass

Virtual Server 2005 (VS2005) is a well-known tool to manage Virtual Machines (VMs) on a host server. It is a great feature to manage virtualization for hardware/software. Most often one would use the VS2005 Web Admin  interface to manage VMs. But at times (like the way it recently happened for a customer of mine) one may want to programmatically control the VMs. This would most often sound a necessity if you would like to provide end users access to manage basic VM activities like turn on/off, save state etc. etc. without giving them access to everything as in through the VS2005 Web Admin site.

Here are some code snippets/sample web application/links in case you would like to try your hands on something like this.

Let me show you a typical screen-shot of how VS2005 Web Admin interface looks like.

image

image

To programmatically manage the Virtual machines on a host server, you need to add a reference to a COM object for your .Net application which exposes various properties/methods.

 image

Visual Studio will add a COM Interop layer for this. You should see the following in your Visual Studio project:

image

Ensure we add a namespace as "using Microsoft.VirtualServer.Interop;" in the web/form page.

Here are code snippets (in C#) to:

a. Turn On/Restore from a saved state for a Virtual machine on a local host machine.

VMVirtualServerClass vs = new VMVirtualServerClass();

VMVirtualMachine vm = vs.FindVirtualMachine(VM_name);

if (vm.State == VMVMState.vmVMState_TurnedOff || vm.State == VMVMState.vmVMState_Saved)
{
VMTask vt = vm.Startup();
if (vt != null)
vt.WaitForCompletion(-1);
}

b. Turn Off a Virtual machine on a local host machine.

VMVirtualServerClass vs = new VMVirtualServerClass();

VMVirtualMachine vm = vs.FindVirtualMachine(VM_name);

if (vm.State == VMVMState.vmVMState_Running || vm.State == VMVMState.vmVMState_Paused)
{
// Turn the power off on the Guest VM
Label1.Text = "Forcefully shutting down VM...";
VMTask vt = vm.TurnOff();
if (vt != null)
vt.WaitForCompletion(-1);
else
Label1.Text = "Not ready for a turn off. Please wait and try again after a few seconds...";
}

c. Save state of a Virtual machine on a local host machine.

VMVirtualServerClass vs = new VMVirtualServerClass();

VMVirtualMachine vm = vs.FindVirtualMachine(VM_name);

if (vm.State == VMVMState.vmVMState_TurnedOff || vm.State == VMVMState.vmVMState_Saved)
{
VMTask vt = vm.Startup();
if (vt != null)
vt.WaitForCompletion(-1);
}

d. Pause a Virtual machine on a local host machine.

VMVirtualServerClass vs = new VMVirtualServerClass();

VMVirtualMachine vm = vs.FindVirtualMachine(VM_name);

if (vm.State == VMVMState.vmVMState_TurnedOff || vm.State == VMVMState.vmVMState_Saved)
{
VMTask vt = vm.Startup();
if (vt != null)
vt.WaitForCompletion(-1);
}

e. Resume a Virtual machine on a local host machine.

VMVirtualServerClass vs = new VMVirtualServerClass();

VMVirtualMachine vm = vs.FindVirtualMachine(VM_name);

if (vm.State == VMVMState.vmVMState_TurnedOff || vm.State == VMVMState.vmVMState_Saved)
{
VMTask vt = vm.Startup();
if (vt != null)
vt.WaitForCompletion(-1);
}

f. Reset a Virtual machine on a local host machine.

VMVirtualServerClass vs = new VMVirtualServerClass();

VMVirtualMachine vm = vs.FindVirtualMachine(VM_name);

if (vm.State == VMVMState.vmVMState_TurnedOff || vm.State == VMVMState.vmVMState_Saved)
{
VMTask vt = vm.Startup();
if (vt != null)
vt.WaitForCompletion(-1);
}

g. Shut down a virtual machine on a local host machine.

VMVirtualServerClass vs = new VMVirtualServerClass();

VMVirtualMachine vm = vs.FindVirtualMachine(VM_name);

if (vm.State == VMVMState.vmVMState_TurnedOff || vm.State == VMVMState.vmVMState_Saved)
{
VMTask vt = vm.Startup();
if (vt != null)
vt.WaitForCompletion(-1);
}

h. Enumerate all the Virtual machines running on the local host machine.

VMVirtualServerClass vs = new VMVirtualServerClass();

VMVirtualMachine vm = vs.FindVirtualMachine(VM_name);

if (vm.State == VMVMState.vmVMState_TurnedOff || vm.State == VMVMState.vmVMState_Saved)
{
VMTask vt = vm.Startup();
if (vt != null)
vt.WaitForCompletion(-1);
}

i. Check various properties exposed through the COM API.

VMVirtualServerClass vs = new VMVirtualServerClass();

VMVirtualMachine vm = vs.FindVirtualMachine(VM_name);

string _VMName = vm.Name;
string _guestOS = String.Empty;
if (vm.State == VMVMState.vmVMState_Running)
_guestOS = vm.GuestOS.OSName;
else
_guestOS = "OS: n/a";
string _memory = vm.Memory.ToString();
.......

j. At times if you want to manage VMs hosted on a remote machine and not on the local machine, here is what you need to do besides all the steps already mentioned above.

private VMVirtualServerClass connectToRemoteHost(string remoteHost)
{
VMVirtualServerClass virtualServerCOM = null;
Type VMVirtualServerClassType = typeof(VMVirtualServerClass);
try
{
// create remote type from class identifier
Type DCOMType = Type.GetTypeFromCLSID(VMVirtualServerClassType.GUID, remoteHost, true);
object DCOMObject = Activator.CreateInstance(DCOMType);
// create local object from remote object
virtualServerCOM = (VMVirtualServerClass)Marshal.CreateWrapperOfType(DCOMObject, VMVirtualServerClassType);
TextBox1.Text = String.Empty;
return virtualServerCOM;
}
catch
{
TextBox1.Text ="There was an error while connecting to the remote host. Make sure that we have the necessary permissions/settings to access the remote host";
return null;
}
}

                                                                                                                       (Courtesy this MSDN magazine)

I have not included error handling in the code snippets above. Please do so when you build the actual application. Also there are many other activities that can be performed besides the ones discussed above like setting up a VM from the beginning with various components like differencing, memory, network adapter etc., changing the disk configuration of an existing machine etc. etc.

The more you explore the more you get to control through the code using the API mentioned above.

Here are some useful links related to the topic.

http://msdn.microsoft.com/en-us/magazine/cc163935.aspx

http://dotnet.sys-con.com/node/47338

http://blogs.msdn.com/david.wang/archive/2006/04/17/HOWTO-Perform-VHD-Maintenance-Automatically.aspx

http://blog.anildesai.net/?p=220, http://blog.anildesai.net/?p=214

Attached is a sample ASP.Net 2.0 web application to try the above commands if interested.

Till next time our paths cross.

Bye!

More Posts Next page »
 
Page view tracker