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
- nom du site : siteName de type string
- port du site : sitePort de type int
- chemin physique du site : sitePhysicalPath de type string
Nous nommerons cette fonction CreateASite, voici le code :
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 :
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é :
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
- poolName le nom du pool du type String
- pipeLineMode le type de ManagedPipelineMode voulu du type pipelineMode
Il existe deux types de ManagedPipelineMode :
- « Classic » : Il permet d'indiquer que IIS 7 va utiliser aspnet_isapi.dll pour router les requêtes comme dans IIS 6 ;
- « 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 :
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 :
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 :
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 :
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.
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 :
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 :
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.
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 :
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 :
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 :
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 :
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 :
_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 :
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 :
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