Files
biztalk-sap-environment-inv…/src/BizTalkSapEnvironmentInventory/Collectors/SystemCollector.cs
T
admin b9d01339b0
Build und Test / build (push) Has been cancelled
Handle BizTalk artifact collection via ExplorerOM
2026-07-28 11:35:10 +02:00

480 lines
20 KiB
C#

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
{
/// <summary>
/// Erfasst lokale Windows-, BizTalk-Installations- und Management-DB-Metadaten.
/// </summary>
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));
}
/// <summary>
/// Liest ausschließlich lokale WMI- und Registry-Daten.
/// </summary>
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;
}
}
}