Rappel : le code source du site présenté est disponible ici.
Vous avez besoins des éléments suivants pour l'utiliser :
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>
DEFAULT.ASPX
UpdatePanel
· Gérer un Timer
· Gérer le rafraichissement de la Textbox de sortie
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>
<asp:TextBox ID="TxtPowerShellScript" runat="server" TextMode="MultiLine" Width="597px"
Height="194px" BackColor="#012456" ForeColor="#EEEDF0" Wrap="False"></asp:TextBox>
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" />
DEFAULT.ASPX.CS
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;
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());
}
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";
string strContent = TxtPowerShellScript.Text;
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();
}
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);
pipe.InvokeAsync();
void Output_DataReady(object sender, EventArgs e)
{
PipelineReader<PSObject> reader = (PipelineReader<PSObject>)sender;
String strPowershellTrace = reader.Read().ToString();
Session["PowerTrace"] += strPowershellTrace + "\r\n";
}
PipelineReader<PSObject> reader = (PipelineReader<PSObject>)sender;
String strPowershellTrace = reader.Read().ToString();
Session["PowerTrace"] += strPowershellTrace + "\r\n";
if (pipe.PipelineStateInfo.State == PipelineState.Completed)
…
while ((Session["PowerTrace"] != null) && (Session["PowerTrace"].ToString().Length > 0))
{
}
runspace.Close();
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"] = "";
}
}
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
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();
this.TxtResult.Text += strPoshTrace;
Session["PowerTrace"] = "";
while ((Session["PowerTrace"] != null) && (Session["PowerTrace"].ToString().Length > 0))
2 commentaires:
ma petite caille je vois que t'es tjs aussi fort !!! c pascal ex rte.eds.installshield if u remember me !
Bien sûr mon grand, par quel heureux hasard t'es tu rendus sur ce tutorial ? ça y est tu te mets à PowerShell ? ;)
Enregistrer un commentaire