В общем-то дело достаточно простое. Что нам потребуется:
Windows SDK for Windows Server 2008 and .NET Framework 3.5
Visual Web Developer 2008 Express Edition
PowerShell на хостовой машине (где и как скачивать найдете и сами =) )
Механизм работы достаточно подробно описан на Dev-Infra. Что же касается конкретики... Например у меня ни на одной из машин (ни на домашней ни на рабочей) SDK не захотел интегрироваться в VWD Express Edition, так что пришлось добавлять Referenc'ы вручную (Add Reference на проекте и дальше добавляем все dll из C:\Program Files\Reference Assemblies\Microsoft\WindowsPowerShell\v1.0 ).
Вторые грабли с которыми я столкнулся - правильный вывод команд PowerShell. Т.к. мы получаем не стандартный вывод, а набор объектов, но после добавления следующей строчки:
pipe.Commands.Add("Out-String");
проблема решилась.
Последние грабли случились с IIS 7. Если запускать рабочий процесс в Integrated, а не в Classical mode AJAX работать у меня отказался. С IIS 5 (стоит на домашней машине), не было вообще никаких проблем.
И еще одно - PowerShell выполняется от имени того пользователя, под которым запущен процесс. Так что надо внимательно относиться к выбору пользователя от имени которого должны запускаться скрипты. И назначать ему соответствующие права.
Естественно код предоставляется без всяких гарантий и т.п. Разумеется доступ к этой странице (если вы решитесь ее использовать) следует строго ограничить.
Полный код aspx страницы выложен тут и я не вижу смысла на нем останавливаться.
А вот codebehind файл представляет больший интерес, поэтому его я выкладываю почти полностью. Во-первых потому что на Дев-Инфре он раскидан по всему посту, а во-вторых потому что он чуть-чуть отличается от оригинала.
Добавляем к стандартным пространствам имен следующие:
=====================================================
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.IO;
=====================================================
=====================================================
protected void BtnExecuteScript_Click(object sender, EventArgs e)
{
string strCurrentId = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
// Включаем таймер, блокируем кнопку, очищаем текстовое поле с прошлым результатом и сообщаем от кого выполняется скрипт
this.Timer1.Enabled = true;
this.BtnExecuteScript.Enabled = false;
this.TxtResult.Text = "";
Session["PowerTrace"] = "Выполняется от имени : " + strCurrentId + "\r\n";
// Получаем текст скрипта
string strContent = TxtPowerShellScript.Text;
// Преобразуем HTML в string и говорим выполниться скрипту с указанным текстом
StringWriter writer = new StringWriter();
Server.HtmlDecode(strContent, writer);
this.executePowerShellCode(strContent);
}
=====================================================
=====================================================
private void executePowerShellCode(string code)
{
// Открываем рабочее пространство, задаем строчку с кодом который необходимо выполнить и добавляем командлет который отдает не объекты, а строки, в общем приводим к привычному виду
runspace.Open();
pipe = runspace.CreatePipeline(code);
pipe.Commands.Add("Out-String");
// Закрываем ввод, добавляем обработчики событий и запускаем выполнение тела скрипта
pipe.Input.Close();
pipe.Output.DataReady += new EventHandler(Output_DataReady);
pipe.StateChanged += new EventHandler(pipe_StateChanged);
pipe.InvokeAsync();
}
void Output_DataReady(object sender, EventArgs e)
{
// Забираем объекты из вывода
PipelineReader reader = (PipelineReader)sender;
String strPowershellTrace = reader.Read().ToString();
Session["PowerTrace"] += strPowershellTrace + "\r\n";
}
void pipe_StateChanged(object sender, PipelineStateEventArgs e)
{
// Заканчиваем выполнение скрипта
if (pipe.PipelineStateInfo.State == PipelineState.Completed)
{
runspace.Close();
while ((Session["PowerTrace"] != null) && (Session["PowerTrace"].ToString().Length > 0))
{
}
Session.Remove("PowerTrace");
}
}
protected void Timer1_Tick(object sender, EventArgs e)
{
// Ну и наконец включаем КНОПКУ и сообщаем о конце выполнения скрипта
if (Session["PowerTrace"] == null)
{
this.BtnExecuteScript.Enabled = true;
Timer1.Enabled = false;
this.TxtResult.Text += "Конец скрипта";
}
else
{
String strPoshTrace = Session["PowerTrace"].ToString();
this.TxtResult.Text += strPoshTrace;
Session["PowerTrace"] = "";
}
}
=====================================================
В общем в результате получаем работающий PS в браузере на нужной машине.
В общем осталось прикрутить Exchange и сделать обработчик ошибок, если сделаю (а я думаю что сделаю) и доберусь до блога (а вот с этим уже сложнее), то вывешу обновленный вариант и даже кину куда-нить полные исходники.
Комментариев нет:
Отправить комментарий