vendredi 23 mai 2008

PowerShell et ASP.NET part 3

Nous voici rendu à la troisième et dernière partie de ce petit tutorial sur PowerShell et ASP.NET

Nous allons voir ici comment piloter un script depuis un formulaire web, et comment déployer ce site sur un serveur IIS convenablement.

Piloter un script

Nous avons vu précédemment comment exécuter du code PowerShell à partir d'une Textbox. Voyons maintenant comment nous pouvons piloter un script en modifiant ces paramètres à la volée.

Ceci est pratique quand vous voulez par exemple faire un portail de provisioning ou d'audit. Plutôt que de proposer à l'utilisateur de taper du code PowerShell, vous pouvez plutôt proposer un formulaire qui va se charger d'exécuter un script avec des paramètres.

Pour illuster ceci, voici un exemple de recherche d'information avec WMI qui peut prendre en argument un nom de machine. j'ai légèrement modifié notre exemple de départ en rajoutant une petite Textbox qui va prendre ce nom de machine :

powerASP4

Voyons maintenant ce qui change côté code. En fait, très peu de chose : Nous allons à présent lire un script incorporé au site web, modifier le contenu et l'exécuter.

Voici le script ajouté au projet :

$strComputer = "*ComputerName*"
get-wmiobject -class "Win32_LogicalDisk" -namespace "root\cimv2" -computername $strComputer where{$_.DriveType –eq 3}out-string


Un script très classique de requête WMI. Comme vous pouvez le voir, j'ai mis le nom de machine en argument. J'ai mis une référence arbitraire de nom "*ComputerName*", qui va me servir pour remplacer cette valeur de variable par le contenu de la TextBox du formulaire.

Voyons le changement opéré dans le code :



        // On the fly script modification before execution


        StreamReader objReader = new StreamReader(Server.MapPath("~/scripts/WmiAsset.ps1"));
        string strContent = objReader.ReadToEnd();
        strContent = strContent.Replace("*ComputerName*", TxtComputerName.Text);
        this.executePowerShellCode(strContent);


Première chose, nous allons lire le fichier contenant le code de notre script. Je l'ai rangé dans un sous-répertoire nommé "scripts", et on utilise simplement à nouveau la librairie system.IO


StreamReader objReader = new StreamReader(Server.MapPath("~/scripts/WmiAsset.ps1"));


Ensuite nous allons mettre l'intégralité du contenu dans une variable, et fermer l'objet StreamReader qui ne nous servira plus :


string strContent = objReader.ReadToEnd();

Ensuite nous utilisons simplement la méthode replace sur cette string, en changeant le code avec le contenu de notre Textbox qui contient le nom de machine.


strContent = strContent.Replace("*ComputerName*", TxtComputerName.Text);

C'est pour ça qu'il est important que le contenu de la variable modifiée soit unique (ici *ComputerName*). Comme vous avez la maitrise du code PowerShell utilisé, ceci est normalement facile à obtenir.

Voilà qui est fait, il ne nous reste plus qu'à exécuter le code comme dans l'exemple précédent.


this.executePowerShellCode(strContent);

Et voilà, rien de bien compliqué si vous avez saisi les précédentes parties. L'intérêt ici est qu'il suffit, en cas de modification de la requête à effectuer, d'aller simplement modifier le fichier .PS1 du répertoire script sur le serveur web, sans avoir à lancer un éditeur C#/ASP.NET.

Vous pouvez bien sûr mettre plusieurs paramètres dans le formulaire. Il reste des choses importantes à implémenter pour une gestion "pro" (prise en compte des erreurs de scripts PowerShell, gestion plus poussé des modes de sorties de script, un contrôle plus adapté que la Textbox pour le suivi des sorties de scripts, etc...) mais vous avez maintenant les bases pour vous lancer, et surtout je l'espère une meilleure compréhension du principe d'interaction envisageable entre le monde web et le scripting PowerShell.

Voyons a présent comment déployer ce portail sur un serveur IIS.

Déploiement du site web

L'intérêt de notre configuration est de pouvoir utiliser un compte de service pour l'exécution des tâches (en l'occurence, l'exécution de nos scripts). Cela simplifie la définition des droits et le modèle de délégation, car il n'est plus nécessaire de donner des droits administratifs spécifiques à chaque utilisateur.

Nous avons néanmoins le besoin de connaitre l'identité de l'utilisateur à l'origine de l'exécution d'une commande afin de se conformer au pré-requis de sécurité sur la production.

La stratégie pour votre déploiement côté IIS doit donc être la suivante :

Tout d'abord filtrer l'accès au site. Pour cela, il suffit simplement de définir des droits appropriés côté NTFS sur l'emplacement de publication. L'idéal étant de définir un groupe d'accès auquel vous donner les droits de lecture sur le site (côte NTFS), et de rajouter les membres adéquats.

Côté IIS, il suffit de faire une publication classique, en désactivant l'accès "anonymous" au site web, et de sélectionner "Integrated Windows Authentification"

powerASP5

Ensuite, il faut définir un application pool pour déléguer l'exécution du code à un compte de service. Pour cela, créer un nouvel application pool, ouvrez ses propriétés et définissez l'Identité dans l'onget approprié.

powerASP6

Il ne vous reste plus qu'à définir cet application pool dans votre site web :

PowerASP7

Modifiez enfin votre web.config pour prendre en charge l'impersonalisation.

Vous devez avoir la ligne "authentication mode", rajoutez en dessous le code suivant :


        <authentication mode="Windows"/>
    <identity impersonate="true"/>
Et voilà, votre site est maintenant prêt pour l'impersonalisation, et l'accès est filtré par les droits NTFS.

En espérant que ce petit tutorial vous aura donner quelques idées, je reviendrai très prochainement sur un concept un peu plus poussé de portail web combinant ASP.NET 3.5, PowerShell, AJAX et Silverlight !

Voici la source de l'exemple de cette partie PowerShellASP2

mercredi 21 mai 2008

PowerShell et ASP.NET Part 2

Rappel : le code source du site présenté est disponible ici.

Vous avez besoins des éléments suivants pour l'utiliser :

WebDevelopper Express Edition

Ajax controls toolkit et library

Voyons maintenant un peu plus dans le détail l’architecture de ce site web et les différents blocs de code.

WEB.CONFIG

Commençons par le plus simple : le fichier web.config de notre projet.

J’ai apporté ici très peu de modifications : notre premier objectif est de rajouter les références des assemblies PowerShell au projet, afin de pouvoir créer un runspace et un pipeline pour exécuter notre code.

<assemblies> 
    <add assembly="System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/> 
    <add assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/> 
    <add assembly="System.Data.DataSetExtensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/> 
    <add assembly="System.Xml.Linq, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/> 
    <add assembly="System.Management, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/> 
    <add assembly="System.Management.Automation, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/> 
</assemblies>

Ceci va nous permettre de référencer ces dll dans le code-behind. Bien, passons maintenant a quelque chose de plus consistant : voyons notre interface utilisateur !

DEFAULT.ASPX

Vous pouvez faire votre design en utilisant simplement du glisser/déposer des différents éléments (textbox, combobox, etc…). Cet aspect est assez simple, pour ceux souhaitant apprendre les bases de la création d’interface ASP.NET, je vous propose la lecture des excellents tutos de Didier Danse sur developpez.com, notamment les 2 premiers tomes.

Hormis ces aspects classiques de création de formulaire, voici les particularités de notre site web pour PowerShell :

UpdatePanel

Dans la première partie de ce tutorial, je vous est indiqué que nous utilisons un peu d’Ajax pour :

· Gérer un Timer
· Gérer le rafraichissement de la Textbox de sortie

Pour pouvoir utiliser les extensions AJAX dans notre page web, il faut comme pré-requis tout d’abord placer un ScriptManager dans le document (qui va permettre la prise en charge des autres contrôles AJAX)

Je l’ai placé en début de form, classiquement :

<body>
    <form id="form1" runat="server">
    <asp:ScriptManager ID="ScriptManager1" runat="server">
    </asp:ScriptManager>

Passons à la suite, en faisant abstraction des petites fioritures en haut de la page, le gros de notre interface est ici :

<asp:TextBox ID="TxtPowerShellScript" runat="server" TextMode="MultiLine" Width="597px"
            Height="194px" BackColor="#012456" ForeColor="#EEEDF0" Wrap="False"></asp:TextBox>
        <br />
        <asp:UpdatePanel ID="UpdatePanel1" runat="server">
            <ContentTemplate>
                <asp:Button ID="BtnExecuteScript" runat="server" Text="Launch Script" OnClick="BtnExecuteScript_Click" />
                <br />
                Output :
                <br />
                <asp:TextBox ID="TxtResult" runat="server" Height="199px" TextMode="MultiLine" Width="600px"
                    Wrap="False" BackColor="#012456" ForeColor="#EEEDF0"></asp:TextBox>
                <br />
                <asp:Timer ID="Timer1" runat="server" Enabled="False" Interval="100" OnTick="Timer1_Tick" />
            </ContentTemplate>
        </asp:UpdatePanel>
Le premier élément est notre TextBox pour taper le script :

<asp:TextBox ID="TxtPowerShellScript" runat="server" TextMode="MultiLine" Width="597px"
          Height="194px" BackColor="#012456" ForeColor="#EEEDF0" Wrap="False"></asp:TextBox>

Rien de particulier ici, je l’ai nommé “TxtPowerShellScript” pour respecter un peu de normalisation, je l’ai définit en multiline et mis des couleurs proches de la console PowerShell pour le fun.

Ensuite, vous pouvez voir que mon bouton et la deuxième TextBox sont encapsulés dans un contrôle UpdatePanel. C’est ceci qui me permet de ne rafraichir que le contenu encapsulé et non la page entière.

On y retrouve aussi notre Timer :

<
asp:Timer ID="Timer1" runat="server" Enabled="False" Interval="100" OnTick="Timer1_Tick" />




L’intervalle (Interval="100") defini le temps de rafraichissement du timer (en milliseconde), et le OnTick (OnTick="Timer1_Tick") la fonction appelée dans le Code-Behind. J’ai désactivé le Timer par défaut (Enabled="False")afin de ne pas faire de charge inutile. Il sera activé qu’au click sur notre bouton.

Voilà pour l’interface. Vous voyez qu’hormis la question de l’UpdatePanel (qui est après tout un contrôle comme les autres), nous ne sortons résolument pas des sentiers battus. Voyons maintenant ce qui se passe en coulisse dans notre Code-Behind, qui est le gros du morceau.

DEFAULT.ASPX.CS

Nous arrivons maintenant dans le code C# qui va gérer notre invocation de PowerShell et le rafraichissement de la TextBox de sortie.

Pour commencer par le commencement, vous pouvez voir en début de code que j’appel en plus des instances par défaut du site asp.net généré par Visual 3 dll :


using System.Management.Automation;

using System.Management.Automation.Runspaces;

using System.IO;


System.Management.Automation et .Runspaces sont nécessaires pour faire créer le Runspace et le Pipeline. System.IO va nous servir pour la conversion du contenu de la TextBox de script comme nous allons le voir plus loin.

Passons maintenant au code proprement dit. Nous allons tout d’abord créer les objets Runspace et Pipeline pour qu’il soit exposé sur l’ensemble de notre classe.

Runspace runspace = RunspaceFactory.CreateRunspace();
Pipeline pipe;

Nous allons maintenant analyser le code en partant de l’action de l’utilisateur : le click sur le bouton. La fonction (que vous trouvez ligne 85) est la suivante :

protected void BtnExecuteScript_Click(object sender, EventArgs e)
{
    string strCurrentId = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
    // Enable timer and disable button, clear TxtResult textbox
    this.Timer1.Enabled = true;
    this.BtnExecuteScript.Enabled = false;
    this.TxtResult.Text = "";

    // put the username at the beginning of the output (optional)
    Session["PowerTrace"] = "Initiateur de la demande : " + strCurrentID + "\r\n";

     // Gather script from the TxtPowerShellScript and convert it from html to clean text
    // then call executePowerShellCode function with the result
      string strContent = TxtPowerShellScript.Text;
    StringWriter writer = new StringWriter();
     Server.HtmlDecode(strContent, writer);
    this.executePowerShellCode(writer.ToString());
}

Voyons un peu ce qu’il se passe. Pour commencer, je crée une string strCurrentID qui va récupérer le nom de l’utilisateur en cours. Ceci n’est pas obligatoire dans notre exemple, mais nous verrons par la suite qu’il est intéressant pour nous de connaitre cette information. En effet, dans le cadre du déploiement de ce site ASP.NET, c’est un compte de service qui sera utilisé pour exécuter le code côté serveur.

Or, comme nous souhaitons (si ce n’est pas le cas, vous devriez !) tracer l’activité du site, il est important de connaitre l’utilisateur à l’origine de l’action, histoire par exemple de savoir à qui tirer les oreilles en cas d’utilisation abusive de notre belle page.

System.Security.Principal.WindowsIdentity.GetCurrent().Name nous donne cette information en récupérant le Login de l’utilisateur.

Ensuite, nous allons activer le Timer :

this.Timer1.Enabled = true;



Désactiver le bouton (pour éviter le lancement par erreur du script alors qu’un script est déjà en exécution)

this.BtnExecuteScript.Enabled = false;


et vider la TextBox de sortie :

this.TxtResult.Text = "";



Ensuite nous initierions notre fameuse variable de Session que j’ai appelé « PowerTrace » avec le nom de la personne exécutant la commande

Session["PowerTrace"] = "Initiateur de la demande : " + strCurrentID + "\r\n";


Un peu d’explication s’impose sur ce choix. Toute la subtilité ici est de pouvoir transmettre à notre page asp.net ce que récupère le Code-Behind quand nous rafraichissons la page. Comme les 2 mondes sont isolés (chacun s’exécute dans son contexte : le page web côté client et le code-behind côté serveur), il nous faut trouver un moyen pour transmettre des informations de l’un à l’autre (ici en l’occurrence, passer la sortie de notre script à la TextBox).


Il existe plusieurs solutions plus ou moins élégantes (créer un fichier texte temporaire, rajouter la sortie dans l’URL…) mais la variable de session est dans notre contexte la meilleure des méthodes. Cela permet de mettre à disposition des informations lisible du côté de l’interface qui se conserve pendant l’ensemble de la session de l’utilisateur. Sinon, par défaut, au rafraîchissement toutes les informations récoltées côté code-behind disparaîtraient. Comme notre sortie de scripts ne prendra jamais une place démesurée en mémoire et qu’il s’agit d’une utilisation en intranet relativement restreinte, c’est ici un bon choix facile à mettre en place.

Le contenu de cette variable est donc exploitable pendant toute la durée de la session de l’utilisateur. Ceci étant fait, nous allons maintenant récupérer le contenu de la TextBox contenant le script :

string strContent = TxtPowerShellScript.Text;



Problème ici : nous sommes dans un contexte HTML, le contenu de cette TextBox est donc au format HTML. Ceci ne nous permet pas d’exécuter le code tels que, car des informations se glissent dans le texte (retour chariot notamment, qui se font avec la syntaxe \r\n). Le code est donc inutilisable par PowerShell tels quel.

Heureusement pour nous, il existe une méthode qui nous permet de décoder l’HTML pour obtenir du texte classique : HtmlDecode

C’est elle que j’utilise ici : je créé une instance d’un StringWriter (qui permet de créer une string), et j’utilise cette méthode pour inscrire le texte « décodé » dans cette string.

StringWriter writer = new StringWriter()
Server.HtmlDecode(strContent, writer);



Je fais appel ensuite à ma fonction d’exécution de code PowerShell avec le résultat :

this.executePowerShellCode(writer.ToString());


Passons donc à l’analyse de cette fonction « executePowerShellCode »

private void executePowerShellCode(string code)
{
    runspace.Open();
    pipe = runspace.CreatePipeline(code);
    pipe.Input.Close();

    // Call output_DataReady when data arrived in the pipe
    pipe.Output.DataReady += new EventHandler(Output_DataReady);
    // Call pipe_StateChanged 
    pipe.StateChanged += new EventHandler<PipelineStateEventArgs>(pipe_StateChanged);
    pipe.InvokeAsync();
}

Je n’est pas réinventé la roue, j’utilise la méthode la plus connue que vous trouverez dans pas mal d’exemple sur le net :

Je commence par ouvrir le runspace

runspace.Open();



Ensuite je créé un pipeline dans ce runspace avec le code retourné précédemment

pipe = runspace.CreatePipeline(code);



Je ferme le pipeline en écriture

pipe.Input.Close();



Ensuite nous créons un gestionnaire d’évènement, qui va tout simplement appeler la fonction « Output_DataReady » quand quelque chose se présente dans la sortie du pipeline

pipe.Output.DataReady += new EventHandler(Output_DataReady);

Nous créons ensuite un autre gestionnaire d’évènement, qui lui va appelé la fonction “pipe_StateChanged” quand le pipeline change d’état. En l’occurrence ici, nous allons tester si l’état du pipeline est « Terminé », qui signifie la fin du script et donc des actions à effectuer tels que la réactivation du bouton Executer comme nous le verrons plus loin.

Ensuite nous appelons la méthode « InvokeAsync() » qui nous permet d’exécuter le pipeline en asynchrone.

pipe.InvokeAsync();


Ok maintenant, notre code est en attente d’activité côté pipeline. Voyons a présent ce qu’il se passe quand des données arrivent dans la sortie du pipeline avec notre fonction Output_DataReady


void Output_DataReady(object sender, EventArgs e)
{
    PipelineReader<PSObject> reader = (PipelineReader<PSObject>)sender;
    String strPowershellTrace = reader.Read().ToString();
    Session["PowerTrace"] += strPowershellTrace + "\r\n";
}

Ici nous allons lire la sortie du pipeline que nous inscrivons dans l’objet reader :

PipelineReader<PSObject> reader = (PipelineReader<PSObject>)sender;



Ensuite, nous définissons une variable de type string qui récupère cette sortie sous forme de… string !

String strPowershellTrace = reader.Read().ToString();


Il ne nous reste plus qu’a rajouter cette variable à notre variable de session :

Session["PowerTrace"] += strPowershellTrace + "\r\n";



Notez que je rajoute “\r\n” derrière cette variable pour gérer le retour chariot. Vous voyez, c’est plutôt simple à comprendre. Pour finir sur le processus d’exécution de code PowerShell, voyons la dernière fonction qui gère le changement d’état du pipeline :

Ici, je pose une condition sur l’état du pipeline : si il a le statut « Completed », nous allons agir, sinon nous ne faisons rien.

if (pipe.PipelineStateInfo.State == PipelineState.Completed)



Alors, que fait-on quand le statut est Completed ? Premièrement nous allons fermer le runspace

Ensuite, tant que la Variable de Sessions n’est pas nulle et qu’elle contient quelque chose (nombre de caractère superieur à 0), nous n’y touchons pas. Ceci afin de laisser le temps au Timer d’inscrire son contenu dans la TextBox de Sortie. Nous verrons cela tout de suite après en analysant la fonction du Timer.

while ((Session["PowerTrace"] != null) && (Session["PowerTrace"].ToString().Length > 0))
{
}
runspace.Close();

Il ne nous reste plus ensuite qu’à supprimer cette variable de session dans l’attente d’un nouveau script :

Session.Remove("PowerTrace");



Voyons maintenant notre fameux Timer :

protected void Timer1_Tick(object sender, EventArgs e)
    {
        if (Session["PowerTrace"] == null)
        {
            this.BtnExecuteScript.Enabled = true;
            Timer1.Enabled = false;
            this.TxtResult.Text += "Fin du script";
        }
        else
        {
            String strPoshTrace = Session["PowerTrace"].ToString();
              this.TxtResult.Text += strPoshTrace;
            Session["PowerTrace"] = "";
        }
    }
Indépendamment des autres fonctions, celle-ci est appelé par notre Timer à intervalles réguliés (dans notre cas toute les 100 ms, on peut bien sûr élargir un peu ce temps)

Ici nous faisons la chose suivante :

Si la Variable de Session est Null (donc directement après sa suppression par la fonction pipe_StateChanged), nous allons :

· Réactiver le bouton
· Désactiver le Timer
· Inscrire « Fin du script » dans la TextBox de sortie

this.BtnExecuteScript.Enabled = true;
Timer1.Enabled = false;
this.TxtResult.Text += "Fin du script";

Sinon, on récupère le contenu de la variable de session dans une string

String strPoshTrace = Session["PowerTrace"].ToString();



On ajoute celle-ci à la TextBox de sortie

this.TxtResult.Text += strPoshTrace;



et on vide la variable de session de son contenu

Session["PowerTrace"] = "";



Notez que nous ne supprimons pas ici la variable, ce qui fait que notre condition dans la fonction pipe_StateChanged est valide :

while ((Session["PowerTrace"] != null) && (Session["PowerTrace"].ToString().Length > 0))

Voilà pour notre tour du propriétaire de ce premier projet. Nous verrons dans la troisième partie de ce tutorial comment piloter un script à partir d’un formulaire, et comment déployer ce site web sur un serveur IIS avec la sécurité adéquate.

vendredi 16 mai 2008

PowerShell et ASP.NET Part 1

English Version Available here

Comme nous l'avons vu, PowerShell permet de créer des interfaces graphiques en passant par les winforms .NET. Ainsi, nous pouvons créer des interfaces pour piloter nos scripts. C'est un vrai plus, d'autant que des outils comme AdminScriptEditor rendent cela particulièrement simple.

Dans mes peregrinations, une question m'est revenue assez souvent :

comment pourrait-on intégrer facilement PowerShell avec une page web ?

A juste titre, utiliser un portail web plutôt que des winforms "clients lourds" apporte plusieurs avantages indéniables :

· Pas besoin d'installer PowerShell ou des Snapins sur l'ensemble des postes d'admins pour utiliser des scripts (je pense par exemple aux interfaces de provisioning Active Directory, un gestionnaire de machines virtuelles, etc... )

· ... Donc pouvoir centraliser et déléguer l'administration facilement

· Filtrer l'accès et l'utilisation de Powershell dans l'entreprise (tout s'exécute du serveur et non du poste)

· Traçabilité : d'un point unique, on peut savoir qui fait quoi, et potentiellement gérer le CTRL+Z salvateur

· Peut s'implémenter sur le portail d'entreprise (Sharepoint ou autre)

Tout ceci serait formidable, mais ne va pas sans quelques inconvenients, du moins au premier abord pour qui n'a jamais touché un site ASP.NET :

· Développer en asp.net et C# s'éloigne résolument du scripting.
· Les exemples de méthodes d'invocation de PowerShell via C# ne donne pas forcément envie à un profil infra de se lancer dans le bain.
· On peut alors plutôt s'orienter sur du developpement C# pour arriver au résultat......
· Or il faut avoir la chance d'avoir un developpeur .NET pur et dur sous la main.

C'est pourquoi je vous soumet ici un premier exemple d'interaction très simple entre une page ASP.NET et du script Powershell, qui prend le problème sous un angle plus "InfraPeople-Compliant". Pour rendre les choses plus digestes, nous allons séparer les choses :

· ASP.NET et C# pour gérer uniquement l'interface utilisateur
· Appel de code powershell pur et non de bloc dans du code C#
· Dialogue entre l'output de PowerShell et le site web pour suivre la progression des commandes.

Ceci permet de bien séparer les 2 aspects du portail, et de garder ainsi PowerShell comme moteur de traitement tels qu'on a l'habitude de l'utiliser en scripting classique.

Comme nous allons le voir au cours de ce tutorial, le code est plutôt simple et la page très légère : et pourtant avec ceci nous allons pouvoir effectuer des commandes complexes et riches grâce à PowerShell.

update : Voici une petite vidéo de l'exemple pour vous donner une idée



Je ne vais pas faire ici un cours sur ASP.NET, car d'une part je ne suis pas le mieux placé pour le faire, d'autre part comme vous allez le voir nous n'allons utiliser que les fondamentaux pour arriver à nos fins.

Reste quelques notions de bases indispensables pour pouvoir saisir la suite :

Quelques bases ASP.NET

Pour parler très schématiquement avec une bonne dose de simplification, on peut concevoir une site ASP.NET comme une page HTML qui pourrait faire exécuter du code .NET côté serveur, et non uniquement via le browser côté poste client.

Voici un exemple qui va être notre premier projet. Notez que nous pouvons (et allons!) utiliser la version gratuite de Visual Studio pour le web : Visual Studio Web developper Express Edition que je vous invite à télécharger. Cette version est tout à fait adaptée à nos besoins et nous permettra de prendre en charge l'ensemble du développement, ne nous privons pas. Vous aurez aussi besoin des Ajax Control Toolkit et library à télécharger ici .

Un site ASP.NET à minima est composé des éléments suivants :

Une page ASP.NET (par défaut ici : default.aspx) : c'est elle qui heberge le code de l'interface e la page (les boutons, cases et autres joyeusetés), au format html et ASP. On y trouve aussi souvent du javascript, mais ceci n'est pas important ni utilisé pour notre exemple.

Un fichier .cs correspondant à la page ASPX (ici : default.aspx.cs) : c'est ce fichier qui contient ce que l'on appelle le "code-behind". C'est le code côté serveur qui sera invoqué par l'interaction entre l'utilisateur et la page. Ce code est dans un langage .NET (C# ou VB.NET). Dans notre cas nous allons utiliser C# qui est proche de PowerShell dans sa structure.

web.config : fichier de configuration du site web. Pour faire simple, on y trouve la configuration du site asp, méthode d'authentification, invocation des DLL (dans notre cas celles qui permettent d'exécuter le code PowerShell), etc..

Bien sûr, l'outil de developpement nous facilite la tâche, et tout est pré-configuré quand vous créez un nouveau projet. Avant de rentrer concrètement sur la création de notre page ASP.NET, voici la technique utilisée pour faire appel à nos scripts d'un formulaire :

La stratégie d'invocation de PowerShell

Le schéma ci-dessous présente la stratégie utilisée pour cet exemple d'invocation de PowerShell par ASP.NET, que nous allons décrire en détail dans la seconde partie de ce tutorial :


Notre site comporte 2 textbox, une pour taper un script, l'autre pour récupérer le retour eventuel généré par ce script, et un bouton pour lancer l'exécution : le tour est joué. Comme vous le voyez, côté interface on est plutôt dans quelque chose de basique.

Le principe est assez simple, nous allons y revenir en détail dans la partie 2 de ce tutorial, mais voici les bases :

Quand on clique sur le bouton "Executer" voici ce qu'il se passe:

On active un Timer (1), je reviendrai sur ce point plus tard, mais disons pour le moment qu'il s'agit d'un mécanisme qui exécute du code en boucle à une fréquence donnée (d'où le nom de Timer d'ailleurs).

Dans le même temps, le code-behind invoque un runspace et un pipeline PowerShell en asynchrone, prend le contenu de la première Textbox et l'exécute dans le pipeline (2).

Toute les secondes, notre timer vérifie la sortie du pipeline (3) et inscrit ce qu'il trouve dans une variable spéciale dites "de session" (4) (on verra pourquoi dans la seconde partie).

On rafraichit la Textbox de sortie avec le contenu de la variable de session (5). Le timer repart jusqu'à la liberation du pipeline (6).

L'utilisation du Timer nous permet d'avoir une vue relativement temps réel de la sortie du script, du moins toute les secondes. Notez que le script n'est pas obligé d'avoir une sortie : comme tout script PowerShell, il peut se passer plein de choses sans rien voir dans la console, donc prenez garde a ce que vous codez ;).

J'ai gardé le code le plus simple possible pour vous permettre d'identifier les différentes fonctions facilement. Nous enrichirons plus tard notre interface une fois que nous aurons vu la composition du code dans la seconde partie de ce tutorial.

Notez toutefois que nous utilisons un peu d'AJAX pour gérer la seconde boite de dialogue : Ajax nous permet ici de rafraichir la boite seule sans rafraichir toute la page. Nous verrons dans la seconde partie qu'intégrer ce type de composant AJAX avec ASP.NET est très facile, des composants prêt à l'emploi sont fournis sans avoir à coder la moindre ligne de javascript ni d'XML.

Pour le moment vous pouvez déjà tester ce site en ouvrant tout simplement le projet en pièce jointe à cet article avec Web developper Express.

Tapez votre script dans la textbox du haut, le résultat s'affichera en bas.

Note : le script s'exécute côté serveur (ici si vous faites un test en local, c'est sur votre machine que le script s'exécute.) Notez que nous récupérons la sortie "objet" de PowerShell : c'est pour cela que j'utilise dans cet exemple le cmdlet "out-string" pour afficher en sortie ce qui apparaitrait dans la console.

vous pouvez bien sûr lancer n'importe quelle commande Powershell (attention donc aux fausses manips :) ). Le site est "light" pour vous permettre de bien saisir le code : point de contrôle d'erreurs ni fioritures côté interface.

Nous verrons dans la seconde partie comment est articulé le code, comment piloter un script avec un formulaire et les questions relatives à la sécurité.

Voici la source : PowerShellASP.zip , n'hésitez pas à poser vos questions en commentaire !