Gestion de IIS 7 avec C#

David Tavan

Cet article vous présente une mise en bouche de la gestion de IIS 7 grâce à C# sous Vista et Longhorn server.

N'hésitez pas à commenter cet article ! Commentez ♪

- Titre : Gestion de IIS 7 avec C#

- Auteur : David Tavan

- Parution : 13 mars 2007

- Mise à jour : 28 mars 2007

- Licence : Copyright ® 2007 David Tavan. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Introduction

- II. Création d'un site

- III. Les pools d'application

- III-A. Création d'un nouveau pool

- III-B. Lister les pools existants

- III-C. Assignation d'un pool existant à un site

- III-D. Assignation et création d'un pool à un site

- IV. Création d'une application web

- V. Création d'un répertoire virtuel

- VI. Lister les sites présents dans IIS 7

- VII. Conclusion

- VIII. Remerciements

I - Introduction

À l'heure où j'écris ces lignes, force est de reconnaitre que les articles traitants de la gestion de IIS 7 via du code managé ne sont pas légion. (ou alors j'ai mal cherché ) -
Tout cet article est donc basé sur IIS 7 présent uniquement dans Vista et Longhorn Server, la seule documentation valable que j'ai pu trouver est ici : http://msdn2.microsoft.com/en-us/library/microsoft.web.administration.aspx.

Pourquoi cet article, la raison en est simple, l'objectif est d'automatiser les tâches, pour ce faire j'utilise, Windows Vista Intégrale RTM, Visual studio 2005 et le SDK de Windows, tout le code est écrit en C#. Je pense que cet article est lisible par toutes personnes connaissant C#. Il n'y a à ma connaissance pas de choses vraiment compliquées. Pour résumer, dans cet article les sujets abordés seront : la création d'un site, la création et l'assignation des pools d'applications, la création d'une application web, la création d'un répertoire virtuel et comment lister les processus en cours dans IIS 7.

Avant de commencer,
pensez à ajouter une référence à Microsoft.Web.Administration à votre projet vous le trouverez dans %WinDir%\System32\InetSrv.

Bon cette fois c'est parti.

II - Création d'un site

Pour ce faire nous allons créer une instance des classes ServerManager et Site puis utiliser la méthode Add de la collection Sites
, ceci va nous permettre de créer un nouveau site sur le port choisi qui pointera vers un fichier, nous passerons ces données en paramètres.

Dans toute la suite de cet article je vais développer une classe que nous utiliserons dans une application console.

Pour en revenir à nos moutons, nous allons créer cette fonction puis dans une application console créer un site web
nommé dvp, écoutant le port 8000 et pointant sur le dossier d:\dvp.

Notre fonction aura donc comme paramètres :

Liste des paramètres

1. nom du site : siteName de type string

2. port du site : sitePort de type int

3. chemin physique du site : sitePhysicalPath de type string

Nous nommerons cette fonction CreateASite, voici le code :

CreateASite

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Web.Administration;

namespace DVP.InetMgrLite
{
public class InetMgrLite
{
// Pour commencer créons une référence vers ServerManager et vers Site
private ServerManager _srvMgr = null;
private Site _site = null;

public void CreateASite(string siteName, int sitePort, string sitePhysicalPath)
{
_srvMgr = new ServerManager();
_site = _srvMgr.Sites.Add(siteName, sitePhysicalPath, sitePort);

// Maintenant indiquons que le site démarre automatiquement.
_site.ServerAutoStart = true;

// Ensuite nous validons les changements
_srvMgr.CommitChanges();
}
}
}

Maintenant utilisons cette méthode depuis une application console :

Utilisation de la méthode

using System;
using System.Collections.Generic;
using System.Text;
using DVP.InetMgrLite;

namespace ConsumeInetLite
{
class Program
{
static void Main(string[] args)
{
InetMgrLite iis = new InetMgrLite();
iis.CreateASite("dvp", 8000, "d:\\dvp");
}
}
}

Maintenant regardons dans IIS 7 si tout c'est bien passé :

Vérification de la création du site

III - Les pools d'application

Afin d'en savoir plus sur la notion de pool, plutôt que de faire un copier-coller je vous invite à lire ceci : http://www.microsoft.com/france/msdn/netframework/1X/20031028-dotnet-dick-chapitre35.mspx

III-A - Création d'un nouveau pool

Afin de créer un nouveau pool, nous allons créer une énumération qui nous permettra d'indiquer le ManagedPipelineMode voulu,
puis une méthode CreateNewPool qui aura deux paramètres :

Paramètres de CreateNew Pool

1. poolName le nom du pool du type String

2. pipeLineMode le type de ManagedPipelineMode voulu du type pipelineMode

Il existe deux types de ManagedPipelineMode :

1. « Classic » : Il permet d'indiquer que IIS 7 va utiliser aspnet_isapi.dll pour router les requêtes comme dans IIS 6 ;

2. « Integrated » : Il permet d'indiquer que le serveur va utiliser le pipeline intégré à IIS 7 pour router les requêtes appelant du code managé.

La plupart des applications peuvent tourner sous IIS 7 dans un pool de type « Integrated », mais pour des raisons de compatibilité le type « Classic » peut s'avérer salvateur.

Ceci dit, commençons par créer notre énumération pour des raisons de lisibilité, elle va se nommer pipelineMode et aura l'allure suivante :

Énumération pipelineMode

using System;

namespace DVP.InetMgrLite
{
public enum pipelineMode
{
classic,
integrated
}
}

Maintenant nous allons créer une nouvelle instance de ServerManager, puis ajouter à la collection « ApplicationPools » via la méthode « Add » notre nouveau pool :

CreateNewPool documenté

public void CreateNewPool(string poolName, pipelineMode pipeLineMode)
{
_srvMgr = new ServerManager();

// Via la méthode Add nous ajoutons notre nouveau pool
_srvMgr.ApplicationPools.Add(poolName);

// Nous déclarons une nouvelle instance de ApplicationPools,
_appPool = _srvMgr.ApplicationPools[poolName];

// puis grâce à un switch nous indiquons le ManagedPipelineMode voulu
switch (pipeLineMode)
{
case pipelineMode.classic:
_appPool.ManagedPipelineMode = ManagedPipelineMode.Classic;
break;
case pipelineMode.integrated:
_appPool.ManagedPipelineMode = ManagedPipelineMode.Integrated;
break;
default:
break;
}

// Ensuite on valide les changements
_srvMgr.CommitChanges();
}

Maintenant nous allons utiliser cette méthode dans une application console :

Utilisation de CreateNewPool dans une application console

using System;
using System.Collections.Generic;
using System.Text;
using DVP.InetMgrLite;

namespace ConsumeInetLite
{
class Program
{
static void Main(string[] args)
{
InetMgrLite iis = new InetMgrLite();
iis.CreateNewPool("tes222t", pipelineMode.classic);
}
}
}

Regardons maintenant dans IIS :

Comme prévu notre pool est créé.

III-B - Lister les pools existants

Maintenant nous allons lister les différents pools existants dans IIS. Il y a plusieurs raisons de le faire. Par exemple si nous reprenons ce que nous avons fait précédemment,
il est enfantin de faire « planter » le programme, il suffit de le lancer deux fois,la première fois il va créer le pool, la seconde,
il va lever une exception, car il va essayer de créer un pool qui existe déjà.
Une des autres raisons de le faire sera évidente dans la section « Assignation d'un pool existant… »

Histoire de parvenir à nos fins, nous allons créer deux méthodes, la première GetExistingPools qui nous renverra un IEnumerator, la seconde ExisitingPools, nous renverra une liste de string.

En C# 3, via LINQ, vive le ToList() ;-), mais ceci est une autre histoire.

Donc dans un premier temps créons une collection de pools, pour ce faire quelques lignes suffisent :

GetExistingPools

private IEnumerator GetExistingPools()
{
_srvMgr = new ServerManager();

//C'est ici que la collection est créée
IEnumerator enumPools = _srvMgr.ApplicationPools.GetEnumerator();
return enumPools;
}

Maintenant créons la seconde méthode ExistingPools, le code, me semble-t-il, parle de lui même :

public List<String> ExistingPools()
{
IEnumerator colPools = GetExistingPools();
List<String> lstPools = new List<string>();

int i = 0;

// Remplissage de la liste.
while (colPools.MoveNext())
{
// Nous remplissons la liste avec le nom (.Name) du pool.
lstPools.Add(_srvMgr.ApplicationPools[i].Name);
i++;
}

return lstPools;
}

Voici un petit exemple en mode console toujours qui met en évidence ce que nous venons de voir, nous allons tout simplement afficher le nom des pools existants.

Pools existants

using System;
using System.Collections.Generic;
using System.Text;
using DVP.InetMgrLite;

namespace ConsumeInetLite
{
class Program
{
static void Main(string[] args)
{
InetMgrLite iis = new InetMgrLite();

List<String> lstPools = new List<string>();
lstPools = iis.ExistingPools();

Console.WriteLine("Liste des pools : ");

//Ici on parcourt la liste et on affiche le nom du pool.
foreach (string pool in lstPools)
{
Console.WriteLine("{0}", pool);
}

Console.ReadLine();
}
}
}

Le résultat est le suivant :

III-C - Assignation d'un pool existant à un site

Pour assigner un pool existant à un site, il faut évidemment récupérer la liste des pools existants. Pour ce faire nous utiliserons ce que nous
avons écrit lorsque nous listions les pools en mode console.
Nous allons créer une méthode AssignExistingPool qui prendra deux paramètres de type string : le nom du site (siteName) et le nom du pool (poolName).
Cette méthode est « mécanique » ;) , pour l'utiliser on signifie à ApplicationPoolName le nom du pool souhaité, on valide les changements et on recycle le pool.
Dans un premier temps, voyons le code de cette méthode :

méthode AssignExistingPool

public void AssignExistingPool(string siteName, string poolName)
{
_srvMgr = new ServerManager();
_srvMgr.Sites[siteName].Applications[0].ApplicationPoolName = poolName;
_appPool = _srvMgr.ApplicationPools[poolName];

_srvMgr.CommitChanges();
_appPool.Recycle();
}

Comme je le disais rien de bien sorcier.
Maintenant voyons comment utiliser tout ceci.
Nous allons utiliser un scénario simple en mode console :

1- nous récupérons dans une liste de string les pools existants ;
2- via un Console.ReadLine() nous demandons à l'utilisateur le pool choisi ;
3- encore avec un ReadLine nous demandons le site qui doit changer de pool ;
4- on valide. :)

Maintenant regardons le code suivant de ce scénario :

Utilisation de la méthode AssignExistingPool

List<String> lstPools = new List<string>();
lstPools = iis.ExistingPools();

Console.WriteLine("Liste des pools : ");

//Ici on parcourt la liste et on affiche le nom du pool.
foreach (string pool in lstPools)
{
Console.WriteLine("{0}", pool);
}

// Comme nous sommes en mode console il faut saisir le nom du pool.
Console.WriteLine("Veuillez choisir un pool");
string sPool=Console.ReadLine();
Console.WriteLine("Nom du site qui doit changer de pool");
string sSite = Console.ReadLine();

iis.AssignExistingPool(sSite, sPool);

Je vous accorde que cette méthode n'est pas vraiment « User Friendly », et oui on saisit les paramètres à la main :

Maintenant un petit tour dans IIS, histoire de voir si le pool a bien changé :

Comme prévu le pool a bien changé, heureusement ;)

III-D - Assignation et création d'un pool à un site

Avant d'assigner et de créer un pool à un site, il convient de vérifier que ce pool n'existe pas, sinon cela va lever une exception.
Pour ce faire nous allons créer une méthode TestPool qui renverra un booléen et qui prendra comme paramètre une variable poolToTest de type string.
Le fonctionnement est très simple, on récupère dans une liste de string les pools existants, on parcourt cette dernière, si le nom du pool à tester figure dans la liste on renvoie false, donc impossible de créer ce pool.

la méthode TestPool

public bool TestPool(string poolToTest)
{
List<string> lstPools = new List<string>();
bool result = true;

lstPools = ExistingPools();
foreach (string pool in lstPools)
{
if (poolToTest == pool)
result = false;
}
return result;
}

Le code de cette méthode ressemble beaucoup à ce que nous avons déjà écrit, donc le voici :

public bool CreateAndAssignNewPool(string siteName, string poolName, pipelineMode pipeMode)
{
bool result = TestPool(poolName);
if (result)
{
_srvMgr = new ServerManager();

_srvMgr.ApplicationPools.Add(poolName);
_srvMgr.Sites[siteName].Applications[0].ApplicationPoolName = poolName;
_appPool = _srvMgr.ApplicationPools[poolName];

switch (pipeMode)
{
case pipelineMode.classic:
_appPool.ManagedPipelineMode = ManagedPipelineMode.Classic;
break;
case pipelineMode.integrated:
_appPool.ManagedPipelineMode = ManagedPipelineMode.Integrated;
break;
default:
break;
}

_srvMgr.CommitChanges();
}
return result;
}

Quant à l'utilisation, elle est triviale comme vous allez le voir :

using System;
using System.Collections.Generic;
using System.Text;
using DVP.InetMgrLite;

namespace ConsumeInetLite
{
class Program
{
static void Main(string[] args)
{
InetMgrLite iis = new InetMgrLite();

bool result = iis.CreateAndAssignNewPool("dvp", "toto", pipelineMode.integrated);
if (!result)
Console.WriteLine("Le pool existe déjà");

Console.ReadLine();
}
}
}

Je pense que le code parle de lui-même.
Histoire de se rassurer, voyons si dans IIS la modification a été prise en compte :

Comme prévu tout est OK.

IV - Création d'une application web

Passons maintenant à la création d'une application web. Je ne vous cacherai pas que c'est très simple et qu'en une ligne de code notre application est créée.
Commençons par créer une méthode CreateWebApp qui prendra comme paramètres : le nom du site (siteName de type string), le chemin de l'application (appPath de type string) et le chemin physique de cette application (physicalPath aussi de type string).
Donc pour créer une application nous allons via la méthode « Add » ajouter à la collection « Applications » notre application comme suit :

Méthode CreateWebApp

public void CreateWebApp(string siteName, string appPath, string physicalPath)
{
_srvMgr = new ServerManager();

//Ici on ajoute notre site à la collection.
_srvMgr.Sites[siteName].Applications.Add(appPath, physicalPath);

//On valide les changements
_srvMgr.CommitChanges();
}

L'utilisation de cette méthode est implicite, créons une application web dont le chemin est /myWebApp sur le site dvp dont le chemin physique est D:\dvp\webapp :

utilisation de la méthode CreateWebApp

using System;
using System.Collections.Generic;
using System.Text;
using DVP.InetMgrLite;

namespace ConsumeInetLite
{
class Program
{
static void Main(string[] args)
{
InetMgrLite iis = new InetMgrLite();

iis.CreateWebApp("dvp", "/myWebApp", @"D:\dvp\webapp");

Console.ReadLine();
}
}
}

Allons voir dans IIS :

Comme prévu notre application web a bien été créée.

V - Création d'un répertoire virtuel

Afin de créer un répertoire virtuel, la méthode est sensiblement la même que la précédente. Dans un premier temps nous allons créer une instance de la classe « Application » et lui indiquer quelle application nous allons utiliser comme point de départ. Ensuite toujours avec la méthode « Add » nous allons ajouter à la collection « VirtualDirectories » notre répertoire virtuel.
Notre méthode que nous nommerons CreateVirtualDir, prendra 4 paramètres tous de type string, à savoir : le nom du site (siteName), le chemin de l'application de départ (appPath), le chemin du répertoire virtuel (virDirPath) et le chemin physique du répertoire virtuel (virDirPhysicalPath), la méthode aura donc cette allure :

CreateVirtualDir

public void CreateVirtualDir(string siteName, string appPath, string virDirPath, string virDirPhysicalPath)
{
_srvMgr = new ServerManager();

//On indique l'application de départ.
Application app = _srvMgr.Sites[siteName].Applications[appPath];

//On ajoute le répertoire virtuel à la collection
app.VirtualDirectories.Add(virDirPath, virDirPhysicalPath);

//On valide les changements
_srvMgr.CommitChanges();
}

Nous allons créer un répertoire virtuel nommé myVirDir ayant comme chemin physique d:\dvp\virdir :

Utilisation de CreateVirtualDir

using System;
using System.Collections.Generic;
using System.Text;
using DVP.InetMgrLite;

namespace ConsumeInetLite
{
class Program
{
static void Main(string[] args)
{
InetMgrLite iis = new InetMgrLite();

iis.CreateVirtualDir("dvp", "/", "/myVirDir", @"d:\dvp\virdir");

Console.WriteLine("<-- END -->");
Console.ReadLine();
}
}
}

Regardons maintenant dans IIS :

Voilà notre répertoire virtuel a bien été créé, comme prévu.

VI - Lister les sites présents dans IIS 7

Afin de lister les sites existants dans IIS 7, il suffit de parcourir la collection « Sites » de « ServerManager » et d'afficher le résultat :

List en mode console

_srvMgr = new ServerManager();

foreach (Site site in _srvMgr.Sites)
{
Console.WriteLine("{0} - {1} ", site.Id.ToString(), site.Name);
}

Bon ce n'est pas ce que je veux, je veux créer une méthode réutilisable dans ma classe et ensuite l'utiliser dans une Winform ou Webform. Pour ce faire je vais utiliser le scénario suivant : créer une classe contenant les informations dont j'ai besoin (ColSites.cs), puis créer une méthode ListSites qui renverra une liste de « ColSites », pour remplir cette liste nous allons parcourir la collection « Sites » comme je l'ai mentionné plus haut.
Commençons par créer notre classe ColSites.cs, elle va contenir deux constructeurs et des accesseurs sur les informations que nous voulons récupérer :

ColSites.cs

using System;
using System.Collections.Generic;
using System.Text;

namespace DVP.InetMgrLite
{
public class ColSites
{
public ColSites()
{
}

public ColSites(string siteid, string sitename)
{
this.SiteId = siteid;
this.SiteName = sitename;
}

private string _siteID;
private string _siteName;

public string SiteId
{
get { return _siteID; }
set { _siteID = value; }
}

public string SiteName
{
get { return _siteName; }
set { _siteName = value; }
}

}
}

Ceci fait, créons notre méthode ListSites et nous allons remplir la liste à renvoyer en parcourant la collection comme suit :

Méthode ListSites

public List<ColSites> ListSites()
{
_srvMgr = new ServerManager();
List<ColSites> lstSites = new List<ColSites>();

foreach (Site site in _srvMgr.Sites)
{
lstSites.Add(new ColSites(site.Id.ToString(), site.Name));
}

return lstSites;
}

Pour utiliser cette méthode, nous créons une liste de « ColSites », nous la parcourons et affichons les résultats.

List<ColSites> lst = iis.ListSites();
foreach (ColSites site in lst)
{
Console.WriteLine("{0} - {1}", site.SiteId, site.SiteName);
}

C'est bien ce que nous voulions.

VII - Conclusion

Cet article n'a pas pour but d'être exhaustif, c'est simplement une mise en bouche, les possibilités au niveau de la sécurité, etc. sont énormes et devraient faire un bon sujet pour un livre (tiens c'est pas bête ça ;-) ).
Pour bien maitriser ce domaine il suffit de connaitre un peu (ou plus) le C# et comment fonctionne IIS 7 qui est à mon sens une grande avancée dans le domaine des serveurs web.

Voici d'ailleurs une image montrant la différence entre l'architecture de IIS 6 et 7 :

Ce diagramme est issu du MSDN Mag de mars 2007.
Je vous invite d'ailleurs à lire cet article en entier :

http://msdn.microsoft.com/msdnmag/issues/07/03/iis7/default.aspx?loc=fr

Pour conclure, je mets à votre disposition les sources de la classe créée ainsi que le diagramme de classe :

J'espère que cet article vous aura aidé à mieux appréhender la gestion de IIS 7 via du code managé et vous donnera envie de poursuivre.

Téléchargez les codes source de la classe

VIII - Remerciements

Je tiens à remercier dev01, Ditch et khany pour la relecture tant technique qu'orthographique.
Merci à vous.