Diagnose BizTalk WMI SQL permissions

This commit is contained in:
2026-07-29 13:21:22 +02:00
parent 8d2934e343
commit 0001d7b861
11 changed files with 396 additions and 38 deletions
@@ -96,6 +96,7 @@ namespace BizTalkCheckmkPulse
detail.Append(available ? "BizTalk WMI and platform data reachable" : "BizTalk WMI or required platform data not readable");
detail.Append(", server=").Append(EmptyAsUnknown(result.Platform.ServerName));
AppendOptional(detail, "group", result.Platform.GroupName);
AppendOptional(detail, "operator_group", result.Platform.OperatorGroup);
AppendOptional(detail, "mgmt_db", JoinDb(result.Platform.ManagementDbServer, result.Platform.ManagementDbName));
AppendOptional(detail, "msgbox_db", JoinDb(result.Platform.MessageBoxDbServer, result.Platform.MessageBoxDbName));
+1
View File
@@ -124,6 +124,7 @@ namespace BizTalkCheckmkPulse
public bool RuntimeArtifactsDataAvailable { get; set; }
public string ServerName { get; set; }
public string GroupName { get; set; }
public string OperatorGroup { get; set; }
public string ManagementDbServer { get; set; }
public string ManagementDbName { get; set; }
public string MessageBoxDbServer { get; set; }
@@ -39,14 +39,25 @@ namespace BizTalkCheckmkPulse
if (!result.Platform.PlatformDataAvailable)
{
var wmiPermissionFailure = result.Diagnostics.Any(x =>
x.Area == DiagnosticArea.Wmi
&& x.Category == DiagnosticCategory.Permission
&& x.Required
&& string.Equals(x.Component, "MSBTS_GroupSetting", StringComparison.OrdinalIgnoreCase));
result.Diagnostics.Add(new ProbeDiagnostic
{
Area = DiagnosticArea.Sql,
Category = DiagnosticCategory.Configuration,
Category = wmiPermissionFailure
? DiagnosticCategory.Permission
: DiagnosticCategory.Configuration,
Component = "BizTalk database discovery",
Required = true,
Summary = "SQL-Zielermittlung ist unvollstaendig, weil nicht alle erforderlichen BizTalk-Plattformklassen gelesen wurden.",
Action = "Zuerst den Service 'BizTalk Platform' und MSBTS_GroupSetting pruefen. Danach den Agent-Dump erneut ausfuehren."
Summary = wmiPermissionFailure
? "SQL-Zielermittlung ist unvollstaendig, weil der BizTalk-WMI-Provider beim SQL-Zugriff abgewiesen wurde."
: "SQL-Zielermittlung ist unvollstaendig, weil nicht alle erforderlichen BizTalk-Plattformklassen gelesen wurden.",
Action = wmiPermissionFailure
? "Zuerst die Wmi/Permission-Diagnose und die Mitgliedschaft des Computerkontos in der konfigurierten BizTalk-Operator-Gruppe beheben."
: "Zuerst den Service 'BizTalk Platform' und MSBTS_GroupSetting pruefen. Danach den Agent-Dump erneut ausfuehren."
});
}
@@ -75,9 +86,19 @@ namespace BizTalkCheckmkPulse
var isLocalSystem = identity != null
&& identity.User != null
&& identity.User.IsWellKnown(WellKnownSidType.LocalSystemSid);
result.NetworkIdentityHint = isLocalSystem
? "<DOMAIN>\\" + Environment.MachineName + "$"
: result.ExecutionIdentity;
if (isLocalSystem)
{
if (string.IsNullOrWhiteSpace(result.NetworkIdentityHint)
|| string.Equals(result.NetworkIdentityHint, "unknown", StringComparison.OrdinalIgnoreCase)
|| result.NetworkIdentityHint.StartsWith("<DOMAIN>\\", StringComparison.OrdinalIgnoreCase))
{
result.NetworkIdentityHint = "<DOMAIN>\\" + Environment.MachineName + "$";
}
}
else
{
result.NetworkIdentityHint = result.ExecutionIdentity;
}
}
}
catch (Exception ex)
+79 -6
View File
@@ -25,7 +25,7 @@ namespace BizTalkCheckmkPulse
private const int OrchestrationStarted = 4;
private const int EAccessDenied = unchecked((int)0x80070005);
internal const string GroupSettingQuery =
"SELECT Name, MgmtDbServerName, MgmtDbName, SubscriptionDBServerName, SubscriptionDBName FROM MSBTS_GroupSetting";
"SELECT Name, BizTalkOperatorGroup, MgmtDbServerName, MgmtDbName, SubscriptionDBServerName, SubscriptionDBName FROM MSBTS_GroupSetting";
private readonly MonitoringOptions _options;
/// <summary>
@@ -105,6 +105,7 @@ namespace BizTalkCheckmkPulse
groupFound = true;
result.Platform.GroupName = FirstNonEmpty(WmiHelpers.GetString(item, "Name"), WmiHelpers.GetString(item, "MgmtDbName"));
result.Platform.OperatorGroup = WmiHelpers.GetString(item, "BizTalkOperatorGroup");
result.Platform.ManagementDbServer = WmiHelpers.GetString(item, "MgmtDbServerName");
result.Platform.ManagementDbName = WmiHelpers.GetString(item, "MgmtDbName");
result.Platform.MessageBoxDbServer = WmiHelpers.GetString(item, "SubscriptionDBServerName");
@@ -502,6 +503,12 @@ namespace BizTalkCheckmkPulse
TimeSpan? duration)
{
var category = ClassifyWmiException(exception);
var sqlLoginPrincipal = ExtractSqlLoginPrincipal(exception.Message);
if (!string.IsNullOrWhiteSpace(sqlLoginPrincipal))
{
result.NetworkIdentityHint = sqlLoginPrincipal;
}
var technicalDetails = exception.GetType().Name
+ " HRESULT=0x" + exception.HResult.ToString("X8")
+ ": " + exception.Message;
@@ -521,8 +528,8 @@ namespace BizTalkCheckmkPulse
Category = category,
Component = component,
Required = required,
Summary = BuildWmiSummary(category, component, connectionFailure),
Action = BuildWmiAction(category, connectionFailure),
Summary = BuildWmiSummary(category, component, connectionFailure, sqlLoginPrincipal),
Action = BuildWmiAction(category, connectionFailure, sqlLoginPrincipal),
TechnicalDetails = technicalDetails
});
}
@@ -551,7 +558,7 @@ namespace BizTalkCheckmkPulse
/// </summary>
/// <param name="exception">Zu klassifizierende Exception.</param>
/// <returns>Handlungsorientierte Diagnosekategorie.</returns>
private static DiagnosticCategory ClassifyWmiException(Exception exception)
internal static DiagnosticCategory ClassifyWmiException(Exception exception)
{
var managementException = exception as ManagementException;
var comException = exception as COMException;
@@ -583,6 +590,15 @@ namespace BizTalkCheckmkPulse
}
var message = exception.Message ?? string.Empty;
if (ContainsAny(
message,
"Login failed for user",
"Cannot open database",
"not associated with a trusted SQL Server connection"))
{
return DiagnosticCategory.Permission;
}
if (ContainsAny(message, "timed out", "timeout", "Zeitlimit"))
{
return DiagnosticCategory.Timeout;
@@ -596,6 +612,45 @@ namespace BizTalkCheckmkPulse
return DiagnosticCategory.Provider;
}
/// <summary>
/// Extrahiert das von SQL Server abgewiesene Konto aus der Provider-Fehlermeldung.
/// </summary>
/// <param name="message">WMI-/OLEDB-Fehlermeldung.</param>
/// <returns>Abgewiesenes Konto oder eine leere Zeichenfolge.</returns>
internal static string ExtractSqlLoginPrincipal(string message)
{
const string marker = "Login failed for user";
if (string.IsNullOrWhiteSpace(message))
{
return string.Empty;
}
var markerIndex = message.IndexOf(marker, StringComparison.OrdinalIgnoreCase);
if (markerIndex < 0)
{
return string.Empty;
}
var valueStart = markerIndex + marker.Length;
while (valueStart < message.Length
&& (char.IsWhiteSpace(message[valueStart]) || message[valueStart] == ':'))
{
valueStart++;
}
if (valueStart >= message.Length
|| (message[valueStart] != '\'' && message[valueStart] != '"'))
{
return string.Empty;
}
var quote = message[valueStart];
var valueEnd = message.IndexOf(quote, valueStart + 1);
return valueEnd > valueStart + 1
? message.Substring(valueStart + 1, valueEnd - valueStart - 1).Trim()
: string.Empty;
}
/// <summary>
/// Erstellt eine kurze Fehlerursache fuer einen WMI-Fehler.
/// </summary>
@@ -603,11 +658,20 @@ namespace BizTalkCheckmkPulse
/// <param name="component">Betroffene Komponente.</param>
/// <param name="connectionFailure">Kennzeichen fuer den Namespace-Verbindungsaufbau.</param>
/// <returns>Menschenlesbare Fehlerzusammenfassung.</returns>
private static string BuildWmiSummary(DiagnosticCategory category, string component, bool connectionFailure)
private static string BuildWmiSummary(
DiagnosticCategory category,
string component,
bool connectionFailure,
string sqlLoginPrincipal)
{
switch (category)
{
case DiagnosticCategory.Permission:
if (!string.IsNullOrWhiteSpace(sqlLoginPrincipal))
{
return "BizTalk-WMI-Provider konnte den SQL-Zugriff fuer " + sqlLoginPrincipal + " nicht anmelden.";
}
return "Zugriff auf " + component + " wurde im LocalSystem-Kontext verweigert.";
case DiagnosticCategory.Configuration:
return connectionFailure
@@ -630,11 +694,20 @@ namespace BizTalkCheckmkPulse
/// <param name="category">Klassifizierte Fehlerart.</param>
/// <param name="connectionFailure">Kennzeichen fuer den Namespace-Verbindungsaufbau.</param>
/// <returns>Empfohlener Diagnoseschritt.</returns>
private static string BuildWmiAction(DiagnosticCategory category, bool connectionFailure)
private static string BuildWmiAction(
DiagnosticCategory category,
bool connectionFailure,
string sqlLoginPrincipal)
{
switch (category)
{
case DiagnosticCategory.Permission:
if (!string.IsNullOrWhiteSpace(sqlLoginPrincipal))
{
return "Computerkonto " + sqlLoginPrincipal
+ " der in der BizTalk-Gruppe konfigurierten BizTalk-Operator-Gruppe zuordnen; keine direkten SQL-Logins oder Datenbankrollen vergeben.";
}
return connectionFailure
? "Namespace-ACL fuer root\\MicrosoftBizTalkServer gezielt pruefen. LocalSystem benoetigt lokalen Lesezugriff; keine pauschalen WMI-Rechte vergeben."
: "Agent-Dump pruefen und Computerkonto <DOMAIN>\\" + Environment.MachineName + "$ zunaechst der konfigurierten BizTalk-Operator-Gruppe zuordnen.";