using System; using System.Diagnostics; using System.Globalization; using System.IO; using System.Management; using System.Reflection; using System.Runtime.InteropServices; using System.Security; using Microsoft.Win32; using BizTalkSapEnvironmentInventory.Configuration; using BizTalkSapEnvironmentInventory.Infrastructure; using BizTalkSapEnvironmentInventory.Models; namespace BizTalkSapEnvironmentInventory.Collectors { /// /// Erfasst lokale Windows-, BizTalk-Installations- und Management-DB-Metadaten. /// internal sealed class SystemCollector { private const string BizTalkKey = @"SOFTWARE\Microsoft\BizTalk Server\3.0"; private const string AdministrationKey = BizTalkKey + @"\Administration"; private readonly CommandLineOptions options; private readonly ConsoleFileLogger logger; private readonly TimeSpan timeout; public SystemCollector( CommandLineOptions options, ConsoleFileLogger logger, int timeoutSeconds) { this.options = options; this.logger = logger; timeout = TimeSpan.FromSeconds(Math.Max(5, timeoutSeconds)); } /// /// Liest ausschließlich lokale WMI- und Registry-Daten. /// public void Collect(InventoryDocument document) { document.ComputerName = Environment.MachineName; document.ToolVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString(); document.System.Properties.Add(new NameValueRecord( "Ausführender Benutzer", Environment.UserDomainName + "\\" + Environment.UserName)); document.System.Properties.Add(new NameValueRecord( "64-Bit-Betriebssystem", Environment.Is64BitOperatingSystem ? "Ja" : "Nein")); document.System.Properties.Add(new NameValueRecord( "64-Bit-Prozess", Environment.Is64BitProcess ? "Ja" : "Nein")); document.System.Properties.Add(new NameValueRecord( ".NET Runtime", Environment.Version.ToString())); logger.Info("Lese lokale Windows-Betriebssysteminformationen."); CollectOperatingSystem(document); logger.Info("Lese lokale BizTalk-Installation aus der Registry."); CollectBizTalkRegistry(document); logger.Info("Ermittle die BizTalk-Gruppe über MSBTS_GroupSetting."); CollectBizTalkGroupSetting(document); if (!string.IsNullOrWhiteSpace(options.ManagementServer)) { document.System.ManagementServer = options.ManagementServer; } if (!string.IsNullOrWhiteSpace(options.ManagementDatabase)) { document.System.ManagementDatabase = options.ManagementDatabase; } else if (string.IsNullOrWhiteSpace(document.System.ManagementDatabase)) { document.System.ManagementDatabase = "BizTalkMgmtDb"; } document.System.Properties.Add(new NameValueRecord( "BizTalk Management SQL Server", EmptyAsUnknown(document.System.ManagementServer))); document.System.Properties.Add(new NameValueRecord( "BizTalk Management Database", EmptyAsUnknown(document.System.ManagementDatabase))); logger.Info("Management-Datenbankziel: Server=" + EmptyAsUnknown(document.System.ManagementServer) + "; Datenbank=" + EmptyAsUnknown(document.System.ManagementDatabase) + "."); if (!string.Equals(document.EnvironmentName, "ACC", StringComparison.OrdinalIgnoreCase) && !string.Equals(document.EnvironmentName, "PROD", StringComparison.OrdinalIgnoreCase)) { document.Findings.Add(new Finding { Severity = "Warnung", Area = "Aufruf", Message = "Die Umgebung ist weder ACC noch PROD: " + document.EnvironmentName, RecommendedAction = "Umgebungsparameter und Zielserver vor der Ablage des Berichts bestätigen.", Owner = "BizTalk-Betrieb" }); } } private void CollectOperatingSystem(InventoryDocument document) { const string query = "SELECT Caption,Version,BuildNumber,OSArchitecture,LastBootUpTime " + "FROM Win32_OperatingSystem"; var timer = Stopwatch.StartNew(); logger.Detail("WMI-Namespace: root\\cimv2"); logger.Detail("WQL: " + query); try { using (var searcher = new ManagementObjectSearcher("root\\cimv2", query)) { ConfigureSearcher(searcher); using (var rows = searcher.Get()) { var count = 0; foreach (ManagementObject item in rows) { using (item) { count++; if (count != 1) { continue; } document.System.Properties.Add(new NameValueRecord( "Betriebssystem", Value(item, "Caption"))); document.System.Properties.Add(new NameValueRecord( "Windows-Version", Value(item, "Version"))); document.System.Properties.Add(new NameValueRecord( "Windows-Build", Value(item, "BuildNumber"))); document.System.Properties.Add(new NameValueRecord( "Architektur", Value(item, "OSArchitecture"))); document.System.Properties.Add(new NameValueRecord( "Letzter Systemstart", ConvertWmiDate(Value(item, "LastBootUpTime")))); } } timer.Stop(); logger.Info("Windows-WMI-Abfrage abgeschlossen: Datensätze=" + count + "; Dauer=" + timer.ElapsedMilliseconds + " ms."); } } } catch (Exception exception) when (IsRecoverableWmiException(exception)) { timer.Stop(); document.Findings.Add(new Finding { Severity = "Warnung", Area = "Windows WMI", Message = "Win32_OperatingSystem konnte nicht gelesen werden: " + exception.Message, RecommendedAction = "Lokalen WMI-Dienst und Leseberechtigungen prüfen; " + "die BizTalk-Erfassung wird fortgesetzt.", Owner = "Windows-/BizTalk-Betrieb" }); logger.WarningException( "Windows-WMI-Abfrage fehlgeschlagen", exception); } } private void CollectBizTalkRegistry(InventoryDocument document) { foreach (var view in new[] { RegistryView.Registry64, RegistryView.Registry32 }) { logger.Detail("Prüfe BizTalk-Registryansicht: " + view + "."); try { using (var baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, view)) using (var product = baseKey.OpenSubKey(BizTalkKey, false)) { if (product != null) { AddRegistryValue(document, product, "ProductVersion", "BizTalk Produktversion", view); AddRegistryValue(document, product, "ProductName", "BizTalk Produktname", view); AddRegistryValue(document, product, "Edition", "BizTalk Edition", view); var installPath = FirstRegistryValue( product, "InstallPath", "BizTalkServerInstallPath", "Path"); if (!string.IsNullOrWhiteSpace(installPath) && string.IsNullOrWhiteSpace(document.System.BizTalkInstallPath)) { document.System.BizTalkInstallPath = installPath; document.System.Properties.Add(new NameValueRecord( "BizTalk Installationspfad", installPath, "Registry " + view)); } } } using (var baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, view)) using (var administration = baseKey.OpenSubKey(AdministrationKey, false)) { if (administration == null) { continue; } if (string.IsNullOrWhiteSpace(document.System.ManagementServer)) { document.System.ManagementServer = FirstRegistryValue( administration, "MgmtDBServer", "ManagementDBServer"); } if (string.IsNullOrWhiteSpace(document.System.ManagementDatabase)) { document.System.ManagementDatabase = FirstRegistryValue( administration, "MgmtDBName", "ManagementDBName"); } } } catch (Exception exception) when (IsRecoverableRegistryException(exception)) { document.Findings.Add(new Finding { Severity = "Hinweis", Area = "BizTalk Registry " + view, Message = "BizTalk-Registryansicht konnte nicht gelesen werden: " + exception.Message, RecommendedAction = "Berechtigungen prüfen; WMI oder explizite " + "--management-Parameter können die Ermittlung übernehmen.", Owner = "Windows-/BizTalk-Betrieb" }); logger.WarningException( "BizTalk-Registryansicht " + view + " fehlgeschlagen", exception); } } if (string.IsNullOrWhiteSpace(document.System.BizTalkInstallPath)) { document.Findings.Add(new Finding { Severity = "Warnung", Area = "BizTalk-Installation", Message = "BizTalk-Installationspfad wurde nicht in der Registry gefunden.", RecommendedAction = "Prüfen, ob das Tool lokal auf einem BizTalk Server 2020 ausgeführt wird.", Owner = "BizTalk-Betrieb" }); } } private void CollectBizTalkGroupSetting(InventoryDocument document) { const string query = "SELECT Name,MgmtDbServerName,MgmtDbName FROM MSBTS_GroupSetting"; var timer = Stopwatch.StartNew(); logger.Detail(@"WMI-Namespace: \\.\root\MicrosoftBizTalkServer"); logger.Detail("WQL: " + query); try { var scope = CreateBizTalkScope(); logger.Info("Verbinde lokal mit dem BizTalk-WMI-Namespace."); scope.Connect(); using (var searcher = new ManagementObjectSearcher( scope, new ObjectQuery(query), CreateEnumerationOptions())) { using (var rows = searcher.Get()) { var count = 0; foreach (ManagementObject item in rows) { using (item) { count++; if (count != 1) { continue; } var managementServer = Value(item, "MgmtDbServerName"); var managementDatabase = Value(item, "MgmtDbName"); if (!string.IsNullOrWhiteSpace(managementServer)) { document.System.ManagementServer = managementServer; } if (!string.IsNullOrWhiteSpace(managementDatabase)) { document.System.ManagementDatabase = managementDatabase; } AddWmiValue(document, item, "Name", "BizTalk Gruppenname"); } } timer.Stop(); logger.Info("MSBTS_GroupSetting abgeschlossen: Datensätze=" + count + "; Dauer=" + timer.ElapsedMilliseconds + " ms."); if (count == 0) { AddGroupSettingFinding( document, "MSBTS_GroupSetting lieferte keinen Datensatz."); } else if (count > 1) { AddGroupSettingFinding( document, "MSBTS_GroupSetting lieferte " + count + " Datensätze; der erste Datensatz wurde verwendet."); } } } } catch (Exception exception) when (IsRecoverableWmiException(exception)) { timer.Stop(); AddGroupSettingFinding( document, "MSBTS_GroupSetting konnte für die Management-DB-Erkennung " + "nicht gelesen werden: " + exception.Message); logger.WarningException( "MSBTS_GroupSetting fehlgeschlagen", exception); } } private ManagementScope CreateBizTalkScope() { var scope = new ManagementScope(@"\\.\root\MicrosoftBizTalkServer"); scope.Options.Timeout = timeout; scope.Options.EnablePrivileges = false; scope.Options.Impersonation = ImpersonationLevel.Impersonate; return scope; } private EnumerationOptions CreateEnumerationOptions() { return new EnumerationOptions { ReturnImmediately = false, Rewindable = false, Timeout = timeout }; } private void ConfigureSearcher(ManagementObjectSearcher searcher) { searcher.Options.ReturnImmediately = false; searcher.Options.Rewindable = false; searcher.Options.Timeout = timeout; } private static void AddGroupSettingFinding( InventoryDocument document, string message) { document.Findings.Add(new Finding { Severity = "Hinweis", Area = "BizTalk GroupSetting", Message = message, RecommendedAction = "Registry-Ergebnis prüfen oder --management-server und " + "--management-database explizit angeben.", Owner = "BizTalk-Betrieb" }); } private static bool IsRecoverableWmiException(Exception exception) { return exception is ManagementException || exception is COMException || exception is UnauthorizedAccessException; } private static bool IsRecoverableRegistryException(Exception exception) { return exception is IOException || exception is SecurityException || exception is UnauthorizedAccessException; } private static void AddWmiValue( InventoryDocument document, ManagementBaseObject item, string property, string displayName) { var value = Value(item, property); if (!string.IsNullOrWhiteSpace(value)) { document.System.Properties.Add( new NameValueRecord(displayName, value, "MSBTS_GroupSetting")); } } private static void AddRegistryValue( InventoryDocument document, RegistryKey key, string valueName, string displayName, RegistryView view) { var value = Convert.ToString(key.GetValue(valueName), CultureInfo.InvariantCulture); if (!string.IsNullOrWhiteSpace(value) && !document.System.Properties.Exists(item => string.Equals(item.Name, displayName, StringComparison.OrdinalIgnoreCase))) { document.System.Properties.Add(new NameValueRecord( displayName, value, "Registry " + view)); } } private static string FirstRegistryValue(RegistryKey key, params string[] names) { foreach (var name in names) { var value = Convert.ToString(key.GetValue(name), CultureInfo.InvariantCulture); if (!string.IsNullOrWhiteSpace(value)) { return value.Trim(); } } return string.Empty; } private static string Value(ManagementBaseObject item, string property) { try { return Convert.ToString(item[property], CultureInfo.InvariantCulture) ?? string.Empty; } catch (ManagementException) { return string.Empty; } } private static string ConvertWmiDate(string value) { if (string.IsNullOrWhiteSpace(value)) { return "Unbekannt"; } try { return ManagementDateTimeConverter.ToDateTime(value) .ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture); } catch (ArgumentOutOfRangeException) { return value; } } private static string EmptyAsUnknown(string value) { return string.IsNullOrWhiteSpace(value) ? "Nicht ermittelt" : value; } } }