Доделываем за халтурщиками
Меня всегда удивляло, почему после установки MS SQL Server нужно ещё дополнительно руками конфигурировать SSRS. При этом "конфигурирование" - это просто тупое протыкивание кнопок, не требующее никакого воображения. Это с одной стороны. С другой - SSRS в составе MSSQL Server 2005 EE with AS должен быть установлен в процессе развёртывания решения. Мы не можем предположить, что все наши 2000 пользователей (на первом этапе. далее - 140000) смогут что-то установить себе. Там где есть нажатия на кнопки - просто должна быть техподдержка. Но это стоит денег и похоже, что написать 100 строк (так казалось) - будет попроще, чем тратить миллионы на телефонные разговоры с несчастными "доустанавливательщиками".
Для работы были использованы:
1. MSDN из состава VS2008
2. Онлайновый MSDN
3. Собственно - VS2008 (C#)
Алгоритм доустановки:
1. Создать виртуальные каталоги для службы и менеджера отчётов
2. Развернуть БД сервера отчётов
3. Назначить на неё права
4. Установить WS-identity
В общем, всё, что надо сделать руками в конфигураторе, должно быть сделано
автоматически
Как это всё делается
Есть некоторые проблемы на этом пути. Прежде всего, MSDN что в составе поставки VS2008 безбожно врёт. Не те сигнатуры методов даже. Затем, сам процесс очень плохо документирован. До такой степени, что непонятно даже с чего начинать. И наконец - уже когда всё вроде бы работает, всплывают мелкие детали, которые надо знать, иначе вся работа -- зря. Итак.
1. Берём в левую руку Reporting Services WMI Provider. Запускаем VS2008 и создаём Console application (это по желанию, но как человек, которому скоро умирать - я ближе к консолям, чем к окнам, хотя казалось бы...).
2. Импортируем System.Management и System.DirectoryServices.
Подготовительная работа завершена
Дальше - создаём виртуальные каталоги сервиса и менеджера:
private static void Main(string[] args)
{
string scopeName = String.Format(@"\\{0}\root\Microsoft\SqlServer\ReportServer\v9\Admin", Environment.MachineName);
// Свойства сервиса
string rsCls = String.Format(@"\\{0}\root\Microsoft\SqlServer\ReportServer\v9\Admin:MSReportServer_ConfigurationSetting", Environment.MachineName);
// Свойства менеджера
string rsmCls = String.Format(@"\\{0}\root\Microsoft\SqlServer\ReportServer\v9:MSReportManager_ConfigurationSetting", Environment.MachineName);
ManagementScope scope = new ManagementScope(scopeName);
scope.Connect();
ManagementObject instance = GetInstance(scope, rsmCls);
CreateVirtualDirectory(instance, "Reports");
ResetUrls(instance);
instance = GetInstance(scope, rsCls);
GenerateDbScript(instance);
CreateVirtualDirectory(instance, "ReportServer");
При этом
CreateVirtualDirectory
выглядит так:
///
/// Создать виртуальный каталог в IIS и записать в .config RS (сервиса или менеджера) изменения
///
/// Объект администрирования
/// Имя создаваемого каталога
private static void CreateVirtualDirectory(ManagementObject instance, string VirtualDirectory)
{
ManagementBaseObject inParams = instance.GetMethodParameters("CreateVirtualDirectory");
inParams["Name"] = VirtualDirectory;
inParams["IISPath"] = String.Format("IIS://{0}/w3svc/1/root", Environment.MachineName);
ManagementBaseObject outParams = instance.InvokeMethod("CreateVirtualDirectory", inParams, null);
CheckErrorCode(outParams);
}
Неудобство способа вызова метода компенсируется его работоспособностью. Дальше так будет всю дорогу.
private static ManagementObject GetInstance(ManagementScope Scope, string ManagementClass)
{
ManagementClass serverClass = new ManagementClass(ManagementClass) {Scope = Scope};
ManagementObjectCollection mcol = serverClass.GetInstances();
ManagementObject instance = null;
foreach(ManagementObject o in mcol)
{
instance = o;
break;
}
return instance;
}
Наш случай - прост. Я точно знаю, что сервер отчётов разворачивался в единственном экземпляре и поэтому умничать - "с каким именно экземпляром мы работаем" я не стал. С одним единственным.
Итак, каталоги установлены и две красных лампочки в конфигураторе погасли. Устанавливаем теперь Web service identity
private static void SetWebServiceIdentity(ManagementObject instance)
{
ManagementBaseObject inParams = instance.GetMethodParameters("SetWebServiceIdentity");
inParams["ApplicationPool"] = String.Empty;
ManagementBaseObject outParams = instance.InvokeMethod("SetWebServiceIdentity", inParams, null);
CheckErrorCode(outParams);
}
создаём БД
///
/// Сгенерировать скрипт для создания БД RS
///
/// Объект администрирования (MSReportServer_ConfigurationSetting)
private static void GenerateDbScript(ManagementObject instance)
{
ManagementBaseObject inParams = instance.GetMethodParameters("GenerateDatabaseCreationScript");
inParams["DatabaseName"] = "ReportServer";
inParams["Lcid"] = "1033";
ManagementBaseObject outParams = instance.InvokeMethod("GenerateDatabaseCreationScript", inParams, null);
object s = outParams["Script"];
using(FileStream fs = new FileStream("database.sql", FileMode.Create))
{
using(TextWriter tw = new StreamWriter(fs))
tw.Write(s);
}
ExecSQL("database.sql");
}
устанавливаем права на эту БД
private static void SetupDatabase(ManagementObject instance)
{
ManagementBaseObject inParams = instance.GetMethodParameters("SetDatabaseConnection");
inParams["Server"] = @"(local)\SQLEXPRESS";
inParams["DatabaseName"] = "ReportServer";
inParams["CredentialsType"] = 2;
inParams["UserName"] = String.Empty;
inParams["Password"] = String.Empty;
ManagementBaseObject outParams = instance.InvokeMethod("SetDatabaseConnection", inParams, null);
CheckErrorCode(outParams);
SetupRights(instance, @"NT AUTHORITY\Network Service");
SetupRights(instance, String.Format(@"{0}\ASPNET", Environment.MachineName));
}
private static void SetupRights(ManagementObject instance, string Account)
{
ManagementBaseObject inParams = instance.GetMethodParameters("GenerateDatabaseRightsScript");
inParams["UserName"] = Account;
inParams["DatabaseName"] = "ReportServer";
inParams["IsRemote"] = false;
inParams["IsWindowsUser"] = true;
ManagementBaseObject outParams = instance.InvokeMethod("GenerateDatabaseRightsScript", inParams, null);
object s = outParams["Script"];
CheckErrorCode(outParams);
using(FileStream fs = new FileStream("permissions.sql", FileMode.Create))
{
using(TextWriter tw = new StreamWriter(fs))
tw.Write(s);
}
ExecSQL("permissions.sql");
}
устанавливаем NTLM-аутентификацию на IIS
private static void SetupIIS()
{
DirectoryEntry de = new DirectoryEntry(String.Format("IIS://{0}/w3svc/1/root", Environment.MachineName));
de.Properties["AuthFlags"].Value = 4;
de.CommitChanges();
}
и наконец правим конфигурационный файл менеджера таким образом, чтобы менеджер мог запускаться без ошибок:
private static void ResetUrls(ManagementObject instance)
{
ManagementBaseObject inParams = instance.GetMethodParameters("SetReportServerURLs");
inParams["ReportServerURL"] = String.Empty;
inParams["ReportServerVirtualDirectory"] = "ReportServer";
inParams["ReportServerExternalURL"] = String.Empty;
ManagementBaseObject outParams = instance.InvokeMethod("SetReportServerURLs", inParams, null);
CheckErrorCode(outParams);
}
Да. Чуть не забыл.
CheckErrorCode
выглядит так:private static void CheckErrorCode(ManagementBaseObject outParams)
{
int errorCode = Convert.ToInt32(outParams["HRESULT"]);
if (0 == errorCode)
Console.Write('+');
else
Console.Write('-');
}
Моменты, связанные с превращением виртуальных каталогов в приложения - пропускаю. На выходе - полностью настроенный SSRS2005. Мы прилично сэкономим на техподдержке