Separate BizTalk collection from Checkmk agent
This commit is contained in:
+480
-335
@@ -1,110 +1,180 @@
|
||||
# Dokumentation: BizTalk Checkmk Pulse
|
||||
# Technische Dokumentation: BizTalk Checkmk Pulse
|
||||
|
||||
## Zielbild
|
||||
## 1. Ziel und Randbedingungen
|
||||
|
||||
Ziel ist ein wartbares Monitoring fuer BizTalk Server 2020 in den Umgebungen `ACC`, `DEV`, `TST` und `PRD`. Jede Umgebung besitzt einen BizTalk Server 2020 und einen SQL Server. SQL Server wird mit dem Checkmk-eigenen MSSQL-Plugin ueberwacht; fuer BizTalk liefert dieses Projekt die fehlende fachliche und technische Laufzeitsicht.
|
||||
Die Anwendung ueberwacht BizTalk Server 2020 auf Windows Server 2019 mit
|
||||
Checkmk 2.4. Pro Umgebung (`ACC`, `DEV`, `TST`, `PRD`) wird lokal auf dem
|
||||
BizTalk-Server gesammelt. Das Checkmk-MSSQL-Plugin bleibt fuer die allgemeine
|
||||
SQL-Ueberwachung zustaendig; BizTalk Checkmk Pulse liefert die
|
||||
BizTalk-spezifische Laufzeit- und Berechtigungssicht.
|
||||
|
||||
Das Monitoring soll:
|
||||
Verbindliche Randbedingungen:
|
||||
|
||||
- auf jeder BizTalk-Maschine lokal laufen
|
||||
- ohne PowerShell-Abhaengigkeit funktionieren
|
||||
- keine BizTalk-DLLs im Build erzwingen
|
||||
- Checkmk-2.4-kompatible Services und Metriken erzeugen
|
||||
- service-discovery-freundlich und dashboard-tauglich sein
|
||||
- bei Fehlern gueltige `UNKNOWN`-Services statt kaputter Agent-Ausgaben liefern
|
||||
- Der Checkmk Windows Agent bleibt `LocalSystem`.
|
||||
- `LocalSystem` erhaelt keine BizTalk-/SQL-Gruppenmitgliedschaft.
|
||||
- Ein separates Dienstkonto oder bevorzugt gMSA sammelt minuetlich.
|
||||
- Der Agentpfad fuehrt niemals WMI- oder SQL-Abfragen aus.
|
||||
- Fehler muessen als gueltige Checkmk-`UNKNOWN`-Services sichtbar werden.
|
||||
- Schreiben und Lesen duerfen nie einen halben Snapshot exponieren.
|
||||
- Laufzeitcode benoetigt keine PowerShell und keine BizTalk-DLL-Referenz.
|
||||
- Installation, ACL und Scheduled Task muessen reproduzierbar sein.
|
||||
|
||||
## Technische Bewertung
|
||||
## 2. Ausloeser der Architekturanderung
|
||||
|
||||
### Option A: Checkmk Local Check mit C#/.NET Framework
|
||||
|
||||
Bewertung: empfohlen und umgesetzt.
|
||||
|
||||
Vorteile:
|
||||
|
||||
- Checkmk 2.4 unterstuetzt Local Checks direkt.
|
||||
- Windows Server mit BizTalk 2020 bringt .NET Framework in der Regel passend mit.
|
||||
- `System.Management` kann BizTalk-WMI lesen.
|
||||
- Keine PowerShell Execution Policy, keine Script-Signing-Frage.
|
||||
- Kein serverseitiges Checkmk-Python-Plugin notwendig.
|
||||
- Rollout ist eine einfache Dateiablage unter `%ProgramData%\checkmk\agent\local`.
|
||||
|
||||
Nachteile:
|
||||
|
||||
- Schwellwerte sind in der `.exe.config`, nicht als Checkmk-Regelsatz in WATO.
|
||||
- Eigene Graphing-Definitionen sind nicht enthalten; Checkmk zeigt Local-Check-Metriken trotzdem als Performance-Daten und Graphen.
|
||||
|
||||
### Option B: Agent Plugin plus serverseitiges Checkmk-Plugin
|
||||
|
||||
Bewertung: technisch elegant, aber fuer den ersten produktiven Schritt schwerer.
|
||||
|
||||
Vorteile:
|
||||
|
||||
- Checkmk-Regeln, Discovery und Metrikdefinitionen koennen sauber zentral modelliert werden.
|
||||
- Bessere langfristige Erweiterbarkeit als MKP.
|
||||
|
||||
Nachteile:
|
||||
|
||||
- Checkmk-Check-API-Versionen muessen enger gepflegt werden.
|
||||
- Server-seitige Installation in jeder Site erforderlich.
|
||||
- Mehr Aufwand fuer Managed-Services-Betrieb und Updates.
|
||||
|
||||
Empfehlung: als Version 2 dieses Projekts denkbar, wenn die Local-Check-Variante stabil in PRD laeuft und zentrale Regelsaetze wirklich benoetigt werden.
|
||||
|
||||
### Option C: PowerShell Local Check
|
||||
|
||||
Bewertung: nicht empfohlen fuer diese Umgebung.
|
||||
|
||||
Vorteile:
|
||||
|
||||
- Schnell zu schreiben.
|
||||
- WMI/CIM-Zugriff ist komfortabel.
|
||||
|
||||
Nachteile:
|
||||
|
||||
- PowerShell ist in vielen Serverumgebungen eingeschraenkt oder signaturpflichtig.
|
||||
- Ausfuehrungsverhalten im Checkmk-Agent-Kontext ist haeufiger fehleranfaellig.
|
||||
|
||||
### Option D: BizTalk ExplorerOM/OperationsOM
|
||||
|
||||
Bewertung: fachlich stark, deployseitig unnoetig schwer.
|
||||
|
||||
Vorteile:
|
||||
|
||||
- Hoehere BizTalk-Abstraktion als rohe WMI-Klassen.
|
||||
|
||||
Nachteile:
|
||||
|
||||
- BizTalk-DLL-Versionen muessen beim Build und teilweise zur Laufzeit passen.
|
||||
- Build-Agenten brauchen BizTalk-Komponenten oder SDK-Dateien.
|
||||
- Fuer die benoetigten Zustandsdaten reicht WMI aus.
|
||||
|
||||
## Architektur
|
||||
Der ACC-Agent-Dump vom 29.07.2026 bewies:
|
||||
|
||||
```text
|
||||
Checkmk Windows Agent
|
||||
|
|
||||
| startet lokale Checks aus %ProgramData%\checkmk\agent\local
|
||||
v
|
||||
biztalk_checkmk_pulse.cmd
|
||||
|
|
||||
| startet
|
||||
v
|
||||
BizTalkCheckmkPulse.exe
|
||||
|
|
||||
| liest lokal und prueft im gleichen Sicherheitskontext
|
||||
+-- WMI root\MicrosoftBizTalkServer
|
||||
+-- Windows Application Event Log
|
||||
+-- SQL-Verbindung zu BizTalkMgmtDb/BizTalkMsgBoxDb
|
||||
|
|
||||
v
|
||||
Checkmk Local Check Zeilen nach STDOUT
|
||||
Lokaler Prozess: NT AUTHORITY\SYSTEM
|
||||
Netzwerkprincipal: BEW\AV23AGPWBIO1$
|
||||
Providerfehler: COMException 0x80131904
|
||||
Innerer Fehler: Login failed for user 'BEW\AV23AGPWBIO1$'
|
||||
```
|
||||
|
||||
Der `.cmd`-Wrapper liefert auch dann fuer alle sechs stabilen Services gueltige `UNKNOWN`-Zeilen mit Massnahme, wenn die EXE fehlt oder bereits der Prozessstart mit einem Exitcode fehlschlaegt. Die EXE selbst faengt Laufzeitfehler ab und schreibt ebenfalls fuer alle stabilen Services `UNKNOWN`, damit unvollstaendige Deployments oder WMI-Probleme in Checkmk sichtbar bleiben.
|
||||
Lokales `root\MicrosoftBizTalkServer` war erreichbar. Die
|
||||
datenbankgestuetzten BizTalk-WMI-Klassen scheiterten erst beim Zugriff des
|
||||
Providers auf den getrennten SQL Server. DCOM-, Firewall- oder pauschale
|
||||
WMI-ACL-Aenderungen beheben diesen Befund nicht.
|
||||
|
||||
## Datenquellen
|
||||
Eine Berechtigung des Maschinenkontos haette die Netzwerkrolle allen
|
||||
`LocalSystem`-Diensten des BizTalk-Servers vermittelt. Die jetzt implementierte
|
||||
Producer/Consumer-Trennung beschraenkt sie stattdessen auf ein dediziertes
|
||||
Konto.
|
||||
|
||||
### BizTalk WMI Namespace
|
||||
## 3. Architektur
|
||||
|
||||
```text
|
||||
Privilegierte Zone
|
||||
┌──────────────────────────────────────────────────────────────┐
|
||||
│ Task Scheduler: "BizTalk Checkmk Pulse Provider" │
|
||||
│ Konto: DOMAIN\svc_biztalk_cmk$ (gMSA empfohlen) │
|
||||
│ Intervall: 1 Minute, IgnoreNew, Laufzeitlimit 5 Minuten │
|
||||
│ │
|
||||
│ BizTalkCheckmkPulse.exe --collect │
|
||||
│ ├─ WMI root\MicrosoftBizTalkServer │
|
||||
│ ├─ integrierter SQL-Test an MgmtDb/MsgBoxDb │
|
||||
│ ├─ lokales Windows Application Event Log │
|
||||
│ ├─ Checkmk-Formatierung │
|
||||
│ └─ atomare Snapshot-Publikation + Provider-Log │
|
||||
└──────────────────────────────┬───────────────────────────────┘
|
||||
│ Datei/ACL
|
||||
v
|
||||
%ProgramData%\BizTalkCheckmkPulse\data\biztalk-checkmk-pulse.snapshot
|
||||
│
|
||||
v
|
||||
┌──────────────────────────────────────────────────────────────┐
|
||||
│ Unprivilegierter Transportpfad │
|
||||
│ Checkmk-Agent (LocalSystem) │
|
||||
│ └─ local\biztalk_checkmk_pulse.cmd │
|
||||
│ └─ BizTalkCheckmkPulse.exe --consume │
|
||||
│ ├─ Format/Alter/Maschine/SHA-256 validieren │
|
||||
│ └─ Payload unveraendert nach STDOUT │
|
||||
└──────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
Eine gemeinsame EXE implementiert beide Modi. Das reduziert
|
||||
Versionsabweichungen. Die zentrale Config unter `%ProgramFiles%` gilt fuer
|
||||
Provider und Consumer.
|
||||
|
||||
### 3.1 Provider-Modus
|
||||
|
||||
Aufruf:
|
||||
|
||||
```cmd
|
||||
"%ProgramFiles%\BizTalkCheckmkPulse\BizTalkCheckmkPulse.exe" --collect
|
||||
```
|
||||
|
||||
Ablauf:
|
||||
|
||||
1. Config laden und absolute Runtimepfade validieren.
|
||||
2. Tageslogs gemaess `LogRetentionDays` bereinigen.
|
||||
3. exklusives Handle auf `<SnapshotPath>.provider.lock` halten.
|
||||
4. WMI-, SQL- und Event-Log-Probes ausfuehren.
|
||||
5. sechs stabile und optionale dynamische Checkmk-Zeilen formatieren.
|
||||
6. Snapshot in einer eindeutigen Temporaerdatei desselben Verzeichnisses
|
||||
schreiben.
|
||||
7. `Flush(true)` ausfuehren und Temporaerdatei atomar publizieren.
|
||||
8. Laufzeit, Zeilen- und Diagnosezahl loggen.
|
||||
|
||||
Exitcodes:
|
||||
|
||||
| Code | Bedeutung |
|
||||
| --- | --- |
|
||||
| `0` | Sammlung und Snapshot-Publikation erfolgreich. |
|
||||
| `1` | unerwarteter Providerfehler; aktueller `UNKNOWN`-Snapshot wird versucht. |
|
||||
| `2` | Parallelstart oder Snapshot-I/O-Fehler. |
|
||||
|
||||
Fachliche WMI-/SQL-Fehler werden strukturiert in den Snapshot geschrieben und
|
||||
muessen nicht zwingend einen Prozessfehler verursachen. So bleibt die genaue
|
||||
Fehlerkategorie in Checkmk sichtbar.
|
||||
|
||||
### 3.2 Consumer-Modus
|
||||
|
||||
Aufruf:
|
||||
|
||||
```cmd
|
||||
"%ProgramFiles%\BizTalkCheckmkPulse\BizTalkCheckmkPulse.exe" --consume
|
||||
```
|
||||
|
||||
Ohne Modusargument ist `--consume` der sichere Default. Der Checkmk-Wrapper
|
||||
setzt es trotzdem explizit.
|
||||
|
||||
Der Consumer:
|
||||
|
||||
- oeffnet nur den Snapshot,
|
||||
- begrenzt die Dateigroesse vor dem Einlesen,
|
||||
- validiert striktes UTF-8,
|
||||
- validiert Formatversion und Header,
|
||||
- fordert die lokale Maschinenkennung,
|
||||
- berechnet SHA-256 ueber den exakten Payload,
|
||||
- prueft Zeilenanzahl und Checkmk-Zeilenform,
|
||||
- verwirft Zeitstempel mehr als fuenf Minuten in der Zukunft,
|
||||
- verwirft Snapshots nach `SnapshotMaxAgeSeconds`,
|
||||
- gibt bei Erfolg ausschliesslich den validierten Payload aus.
|
||||
|
||||
Er gibt immer Exitcode `0` zurueck, damit ein Fehler die komplette
|
||||
Checkmk-Agentsektion nicht zerstoert. Jede Ablehnung erzeugt sechs
|
||||
`UNKNOWN`-Zeilen und einen Eintrag im Consumer-Log.
|
||||
|
||||
## 4. Snapshot-Vertrag
|
||||
|
||||
Version 1:
|
||||
|
||||
```text
|
||||
BIZTALK_CHECKMK_PULSE_SNAPSHOT_V1
|
||||
generatedUtc=2026-07-30T12:34:56.1234567Z
|
||||
machineBase64=QVYyM0FHUFdCSU8x
|
||||
identityBase64=QkVXXHN2Y19iaXp0YWxrX2NtayQ=
|
||||
payloadLines=6
|
||||
payloadSha256=<64 hex characters>
|
||||
|
||||
0 "BizTalk Platform" - ...
|
||||
...
|
||||
```
|
||||
|
||||
Maschine und Identitaet sind Base64-kodiert, um Zeilenumbrueche und
|
||||
Trennzeichen im Header auszuschliessen. Base64 ist hier keine Verschluesselung.
|
||||
Die SHA-256-Pruefung schuetzt vor unbemerkter Teil-/Fehlschreibung, nicht vor
|
||||
einem lokalen Administrator.
|
||||
|
||||
### 4.1 Atomare Publikation
|
||||
|
||||
Die Temporaerdatei liegt zwingend im Snapshotverzeichnis. Dadurch findet der
|
||||
Ersatz auf demselben Volume statt. Existiert bereits ein Snapshot, wird
|
||||
`File.Replace` verwendet; beim ersten Lauf `File.Move`. Bis zum erfolgreichen
|
||||
Ersatz bleibt der letzte vollstaendige Snapshot erhalten. Seine Alterspruefung
|
||||
verhindert unbegrenztes Weiterreichen alter Daten.
|
||||
|
||||
### 4.2 Keine direkte Checkmk-Spooldatei
|
||||
|
||||
Checkmk kann zwar Dateien aus seinem Spoolverzeichnis direkt anhaengen, prueft
|
||||
deren Inhalt aber laut eigener Dokumentation nicht. Ein fehlerhafter Spool kann
|
||||
nachfolgende Agentsektionen beeinflussen. Deshalb liegt der Provider-Snapshot
|
||||
in einem eigenen Verzeichnis und wird erst durch den validierenden Consumer in
|
||||
die Local-Check-Ausgabe uebernommen.
|
||||
|
||||
## 5. Datenquellen
|
||||
|
||||
### 5.1 BizTalk WMI
|
||||
|
||||
Namespace:
|
||||
|
||||
@@ -112,286 +182,361 @@ Namespace:
|
||||
root\MicrosoftBizTalkServer
|
||||
```
|
||||
|
||||
Genutzte Klassen:
|
||||
Pflichtklassen:
|
||||
|
||||
| Klasse | Zweck |
|
||||
| --- | --- |
|
||||
| `MSBTS_GroupSetting` | BizTalk-Gruppe, Management-DB und Master-MessageBox (`SubscriptionDB*`). |
|
||||
| `MSBTS_HostInstance` | Host-Instance-Zustand; Ergebnis wird clientseitig auf den ueberwachten Server begrenzt. |
|
||||
| `MSBTS_ServiceInstance` | Suspended service instances. |
|
||||
| `MSBTS_ReceiveLocation` | Receive-Location-Zustand. |
|
||||
| `MSBTS_SendPort` | Send-Port-Zustand. |
|
||||
| `MSBTS_Orchestration` | Orchestration-Zustand. |
|
||||
| `MSBTS_ReceivePort` | Optionales Best-Effort-Application-Mapping, nur wenn per-Application-Services aktiviert sind. |
|
||||
| `MSBTS_GroupSetting` | Gruppe, Read-Only-/Operator-Gruppe, Management- und Master-MessageBox-Ziel. |
|
||||
| `MSBTS_HostInstance` | lokale Host-Instance-Zustaende. |
|
||||
| `MSBTS_ServiceInstance` | suspendierte Instanzen. |
|
||||
| `MSBTS_ReceiveLocation` | Receive-Location-Zustaende. |
|
||||
| `MSBTS_SendPort` | Send-Port-Zustaende. |
|
||||
| `MSBTS_Orchestration` | Orchestration-Zustaende. |
|
||||
|
||||
Die Plattformabfrage verwendet absichtlich keine vermeintliche Klasse `MSBTS_MessageBoxSetting`: Sie ist nicht Bestandteil des dokumentierten BizTalk-WMI-Schemas. Auch die vorhandene Klasse `MSBTS_MsgBoxSetting` ist fuer die Zielermittlung nicht erforderlich. `MSBTS_GroupSetting` liefert mit `SubscriptionDBServerName` und `SubscriptionDBName` bereits das Ziel der Master-MessageBox. Dadurch entfallen eine Providerabfrage und eine unnoetige Berechtigungs-/Schemaschnittstelle.
|
||||
`MSBTS_GroupSetting` wird mit den dokumentierten Properties
|
||||
`MgmtDbServerName`, `MgmtDbName`, `SubscriptionDBServerName`,
|
||||
`SubscriptionDBName`, `BizTalkReadOnlyUserGroup` und
|
||||
`BizTalkOperatorGroup` gelesen. Eine nicht dokumentierte
|
||||
`MSBTS_MessageBoxSetting`-Klasse wird nicht abgefragt.
|
||||
|
||||
### Windows Application Event Log
|
||||
Hostnamen werden clientseitig verglichen. Sonderzeichen und FQDN-/Kurzname
|
||||
gelangen nicht in dynamisch erzeugte WQL-Filter.
|
||||
|
||||
Der Check liest standardmaessig das lokale Application Log fuer die letzten 60 Minuten und filtert auf Quellen wie:
|
||||
### 5.2 SQL-Zugriffsprobe
|
||||
|
||||
- `BizTalk Server`
|
||||
- `XLANG/s`
|
||||
- `ENTSSO`
|
||||
- `BizTalk Server Application`
|
||||
- `BizTalk Server EDI`
|
||||
Der Provider oeffnet fuer Management- und Master-MessageBox-Datenbank eine
|
||||
kurze `System.Data.SqlClient`-Verbindung mit integrierter
|
||||
Windows-Authentifizierung, fuehrt `SELECT 1` aus und schliesst sie. Keine
|
||||
BizTalk-Tabelle wird direkt gelesen oder veraendert.
|
||||
|
||||
Die Liste ist ueber `EventLogSources` konfigurierbar.
|
||||
Die Ausgabe `execution_identity=` zeigt deshalb das Provider-Konto, nicht mehr
|
||||
`NT AUTHORITY\SYSTEM`. Der Check beweist, dass genau das Scheduled-Task-Konto
|
||||
die Ziele erreichen kann.
|
||||
|
||||
### SQL-Zugriffsprobe
|
||||
### 5.3 Event Log
|
||||
|
||||
Nach erfolgreicher oder teilweise erfolgreicher Plattformabfrage uebernimmt der SQL-Probe die per `MSBTS_GroupSetting` ermittelten Management- und Master-MessageBox-Ziele. Fuer jedes eindeutige Ziel wird mit `System.Data.SqlClient` eine Verbindung mit integrierter Windows-Authentifizierung geoeffnet und `SELECT 1` ausgefuehrt. Die Verbindung wird unmittelbar danach geschlossen; es werden keine BizTalk-Tabellen gelesen oder veraendert.
|
||||
Der Provider liest das lokale Application Log im konfigurierten Zeitfenster
|
||||
und filtert Quellen wie `BizTalk Server`, `XLANG/s`, `ENTSSO`,
|
||||
`BizTalk Server Application` und `BizTalk Server EDI`.
|
||||
|
||||
Der Test laeuft unter derselben Identitaet wie der Checkmk Local Check. Damit wird sichtbar, ob `LocalSystem` beziehungsweise das Maschinenkonto des BizTalk-Servers das SQL-Ziel tatsaechlich erreichen und die Datenbank oeffnen kann. Die Probe ist mit `ProbeSqlConnectivity=false` deaktivierbar und verwendet `SqlConnectionTimeoutSeconds` mit dem Default 5 Sekunden je Ziel als Timeout.
|
||||
## 6. Berechtigungsmodell
|
||||
|
||||
Im produktiven Betrieb sollte der komplette Local Check asynchron mit 300 Sekunden Cache ausgefuehrt werden. So fuehren fehlende SQL-Rechte nicht bei jedem Checkmk-Abruf zu weiteren fehlgeschlagenen Login-Ereignissen. Der Trade-off ist eine Zustandsverzoegerung von maximal fuenf Minuten.
|
||||
### 6.1 Bevorzugte BizTalk-Rolle
|
||||
|
||||
## Berechtigungsmodell
|
||||
BizTalk Server 2020 fuehrte die `BizTalk Server Read Only Users`-Gruppe ein.
|
||||
Sie darf Artefakte, Servicezustand, Message Flow und Trackinginformationen
|
||||
lesen, aber keine administrativen Operationen ausfuehren. SQL-seitig wird sie
|
||||
ueber `BTS_READONLY_USERS` vermittelt.
|
||||
|
||||
### LocalSystem und lokaler WMI-Zugriff
|
||||
|
||||
Der Checkmk Windows Agent und der Agent Controller laufen standardmaessig als `LocalSystem` (`NT AUTHORITY\SYSTEM`). Der Wrapper und die EXE erben diesen Kontext. Das Plugin verbindet sich lokal mit `\\<eigener-server>\root\MicrosoftBizTalkServer`, setzt keine eigenen Anmeldedaten, nutzt kein Remote-WMI und fuehrt keine veraendernden WMI-Methoden aus. Das lokale Windows Application Event Log wird ebenfalls nur gelesen.
|
||||
|
||||
Die lokalen Rechte von `LocalSystem` reichen fuer diese Zugriffe normalerweise aus. Im regulaeren lokalen Betrieb werden deshalb keine zusaetzlichen DCOM-, Firewall- oder pauschalen WMI-Namespace-Freigaben benoetigt.
|
||||
|
||||
### Netzwerkidentitaet zum SQL Server
|
||||
|
||||
BizTalk-WMI-Klassen koennen ihre Daten aus der BizTalk Management- oder MessageBox-Datenbank beziehen. Liegt SQL Server auf einer anderen Maschine, authentifiziert sich `LocalSystem` dort mit dem Active-Directory-Computerkonto des BizTalk-Servers:
|
||||
Soll:
|
||||
|
||||
```text
|
||||
DOMAIN\BIZTALKSERVER$
|
||||
DOMAIN\svc_biztalk_cmk$
|
||||
-> konfigurierte BizTalk Server Read Only Users-Domaingruppe
|
||||
-> vorhandener Windows-Gruppenlogin am SQL Server
|
||||
-> BTS_READONLY_USERS in den von BizTalk konfigurierten Datenbanken
|
||||
```
|
||||
|
||||
Dieses Konto besitzt nicht automatisch BizTalk- oder SQL-Berechtigungen. Daraus kann die Situation entstehen, dass die Verbindung zum lokalen WMI-Namespace erfolgreich ist, einzelne SQL-gestuetzte WMI-Klassen aber `Access denied`, `UnauthorizedAccessException` oder `UNKNOWN` liefern.
|
||||
Die genaue Gruppe ist aus
|
||||
`MSBTS_GroupSetting.BizTalkReadOnlyUserGroup` beziehungsweise der
|
||||
BizTalk-Administration zu lesen. Nicht vom Standardnamen ausgehen.
|
||||
|
||||
Der ACC-Test vom 29.07.2026 bestaetigt genau diesen Pfad: Auf `AV23AGPWBIO1` ist WMI erreichbar, aber der Provider meldet `COMException 0x80131904` mit `Login failed for user 'BEW\AV23AGPWBIO1$'`. Die `UNKNOWN`-Services fuer Plattform, Host Instances, Runtime Artifacts und Suspensions sowie die fehlende SQL-Zielermittlung sind Folgefehler. DCOM-, Firewall- oder WMI-ACL-Erweiterungen beheben diesen konkreten SQL-Loginfehler nicht. Der Event-Log-Check arbeitet bereits und seine Fehler/Warnungen sind separat zu bewerten.
|
||||
Die Operator-Gruppe (`BTS_OPERATORS`) wird erst geprueft, wenn eine konkret
|
||||
benannte erforderliche WMI-Klasse trotz nachgewiesener Read-Only-Konfiguration
|
||||
und neuem Anmeldetoken abgewiesen wird. Die Erweiterung muss mit Security,
|
||||
BizTalk- und SQL-Administration begruendet werden.
|
||||
|
||||
Die Codekorrektur ersetzt die Berechtigungsfreigabe nicht. Sie klassifiziert den Fehler korrekt, zeigt das abgewiesene Konto und fuehrt die SQL-Folgediagnose als Berechtigungsfehler. Sie veraendert weder AD noch SQL Server. Daher bleibt die neue Version ohne Gruppenfreigabe `UNKNOWN`; umgekehrt kann auch die alte Version nach korrekter Freigabe grundsaetzlich auf die Daten zugreifen.
|
||||
Unzulaessig:
|
||||
|
||||
### Pruefung und Freigabe
|
||||
- Checkmk-Dienstkonto aendern,
|
||||
- Maschinenkonto pauschal berechtigen,
|
||||
- individueller SQL-Login fuer das Provider-Konto,
|
||||
- direkte ad-hoc DB-Rollen,
|
||||
- `db_owner` oder `sysadmin`,
|
||||
- WMI-/DCOM-Vollzugriff als Reaktion auf einen SQL-Loginfehler.
|
||||
|
||||
Der direkte Programmstart in einer administrativen Shell laeuft unter dem angemeldeten Benutzer und ist deshalb kein ausreichender Berechtigungstest. Verbindlich ist die Ausfuehrung durch den Checkmk Agent Controller als `LocalSystem`:
|
||||
### 6.2 Dateisystem-ACL
|
||||
|
||||
Der Installer entfernt die Vererbung an den Runtimeverzeichnissen und setzt
|
||||
explizite Eintraege mit sprachneutralen Well-known-SIDs:
|
||||
|
||||
| Ziel | Collector | SYSTEM | lokale Administratoren |
|
||||
| --- | --- | --- | --- |
|
||||
| Runtime-Root | RX | RX | F |
|
||||
| `data` | Modify | RX | F |
|
||||
| `logs` | Modify | Modify | F |
|
||||
|
||||
`LocalSystem` kann deshalb den Snapshot lesen, aber im Datenverzeichnis nicht
|
||||
schreiben. Schreibrecht im Logverzeichnis wird benoetigt, um abgelehnte
|
||||
Snapshots aus dem Consumer-Kontext zu protokollieren.
|
||||
|
||||
## 7. Scheduled Task
|
||||
|
||||
Name:
|
||||
|
||||
```text
|
||||
BizTalk Checkmk Pulse Provider
|
||||
```
|
||||
|
||||
Sollkonfiguration:
|
||||
|
||||
| Einstellung | Wert |
|
||||
| --- | --- |
|
||||
| Trigger | einmalig, Wiederholung jede Minute ohne Enddatum |
|
||||
| Programm | `%ProgramFiles%\BizTalkCheckmkPulse\BizTalkCheckmkPulse.exe` |
|
||||
| Argument | `--collect` |
|
||||
| Arbeitsverzeichnis | `%ProgramFiles%\BizTalkCheckmkPulse` |
|
||||
| Benutzer | dediziertes Dienstkonto oder gMSA |
|
||||
| Mehrfachinstanzen | `IgnoreNew` |
|
||||
| Laufzeitlimit | 5 Minuten |
|
||||
| StartWhenAvailable | aktiv |
|
||||
| Restart | zweimal im Minutenabstand |
|
||||
|
||||
Der Task wird mit gespeichertem Dienstkontokennwort beziehungsweise gMSA
|
||||
ausgefuehrt, also unabhaengig von einer interaktiven Anmeldung.
|
||||
|
||||
## 8. Installation
|
||||
|
||||
### 8.1 Build
|
||||
|
||||
```cmd
|
||||
scripts\test-release.cmd
|
||||
scripts\package-release.cmd
|
||||
```
|
||||
|
||||
### 8.2 gMSA
|
||||
|
||||
Voraussetzungen:
|
||||
|
||||
1. gMSA in AD erstellen und Abrufrecht auf den BizTalk-Server begrenzen.
|
||||
2. Konto lokal installieren und mit `Test-ADServiceAccount` pruefen.
|
||||
3. gMSA in die exakt konfigurierte BizTalk-Read-Only-Gruppe aufnehmen.
|
||||
4. AD-Replikation abwarten.
|
||||
|
||||
Installation:
|
||||
|
||||
```powershell
|
||||
& "C:\Program Files (x86)\checkmk\service\cmk-agent-ctl.exe" dump |
|
||||
Select-String -Pattern "BizTalk|Access denied|Unauthorized|UNKNOWN" -Context 0,1
|
||||
.\Install-BizTalkCheckmkPulse.ps1 `
|
||||
-CollectorAccount 'BEW\svc_biztalk_cmk$' `
|
||||
-Gmsa `
|
||||
-EnvironmentName ACC
|
||||
```
|
||||
|
||||
Bei Zugriffsfehlern gilt folgendes Least-Privilege-Vorgehen:
|
||||
### 8.3 regulaeres Dienstkonto
|
||||
|
||||
1. Die exakt konfigurierte Operator-Gruppe in der BizTalk Administration Console unter `BizTalk Group` > `Properties` > `General` feststellen.
|
||||
2. Das Computerobjekt des BizTalk-Servers durch einen AD-Administrator in genau diese Gruppe aufnehmen. Fuer ACC ist dies `AV23AGPWBIO1` beziehungsweise `BEW\AV23AGPWBIO1$`.
|
||||
3. Keine direkten SQL-Logins, BizTalk-Datenbankrollen oder `sysadmin`-Rechte fuer das Maschinenkonto anlegen.
|
||||
4. AD-Replikation abwarten und den Server im Wartungsfenster neu starten; alternativ Maschinen-Tickets mit `klist purge -li 0x3e7` und den Checkmk-Dienst erneuern.
|
||||
5. Den Agent-Dump wiederholen und `operator_group`, zwei SQL-Ziele sowie das Verschwinden der Berechtigungs-`UNKNOWN`s kontrollieren.
|
||||
6. Nur fuer weiterhin abgelehnte, konkret identifizierte WMI-Klassen mit BizTalk- und SQL-Administration pruefen, ob Administratorrechte erforderlich sind.
|
||||
```powershell
|
||||
.\Install-BizTalkCheckmkPulse.ps1 `
|
||||
-CollectorAccount 'BEW\svc_biztalk_cmk' `
|
||||
-EnvironmentName ACC
|
||||
```
|
||||
|
||||
Die Operator-Rolle ist fuer grundlegendes Monitoring und Zustandsabfragen vorgesehen. Direkte manuelle Aenderungen an den Rollen der BizTalk-SQL-Datenbanken sind zu vermeiden; die durch BizTalk konfigurierte Windows-Gruppe ist die vorgesehene Berechtigungsgrenze.
|
||||
Der Installer muss als lokaler Administrator laufen. Er vergibt keine
|
||||
AD-/BizTalk-/SQL-Rechte; diese bleiben getrennte administrative Freigaben.
|
||||
|
||||
Im Normalfall ist diese Windows-Gruppe bereits als SQL-Gruppenlogin und als Datenbankbenutzer eingerichtet. Ihre Benutzerzuordnung vermittelt `BTS_OPERATORS` unter anderem in `BizTalkMgmtDb` und `BizTalkMsgBoxDb`. `BEW\AV23AGPWBIO1$` erhaelt den Zugriff durch die AD-Gruppenmitgliedschaft und braucht keinen eigenen SQL-Login.
|
||||
### 8.4 Checkmk
|
||||
|
||||
Bleibt der Fehler trotz bestaetigter Gruppenmitgliedschaft, AD-Replikation und erneuertem Maschinen-Token bestehen, pruefen BizTalk- und SQL-Administration:
|
||||
Der installierte Wrapper liegt unter:
|
||||
|
||||
1. Ist die exakt konfigurierte Domain-Gruppe in `sys.server_principals` als Windows-Gruppe vorhanden?
|
||||
2. Existiert ihr Datenbankbenutzer in `BizTalkMgmtDb` und `BizTalkMsgBoxDb`?
|
||||
3. Ist dieser Benutzer in beiden Datenbanken Mitglied von `BTS_OPERATORS`?
|
||||
4. Entspricht diese Abbildung weiterhin der BizTalk-Konfiguration?
|
||||
```text
|
||||
%ProgramData%\checkmk\agent\local\biztalk_checkmk_pulse.cmd
|
||||
```
|
||||
|
||||
Fehlende Zuordnungen werden fuer die konfigurierte Gruppe konsistent repariert. Ein individueller Login, manuelle Sonderrollen oder `sysadmin` fuer das Maschinenkonto waeren keine geeignete Abkuerzung.
|
||||
Der Agent darf den Consumer synchron ausfuehren. Er liest nur maximal 1 MiB
|
||||
lokale Daten; ein Checkmk-Async-Cache ist daher nicht notwendig. Falls alte
|
||||
`check_mk.user.yml`-Regeln fuer den bisherigen 300-Sekunden-Cache existieren,
|
||||
sollten sie entfernt werden, damit Checkmk den minuetlich aktualisierten
|
||||
Snapshot nicht zusaetzlich verzoegert.
|
||||
|
||||
| Agent-Dump-Ergebnis | Massnahme |
|
||||
| --- | --- |
|
||||
| Plausible Werte fuer alle BizTalk-Services | Keine Berechtigungsaenderung. |
|
||||
| WMI-Namespace nicht erreichbar | BizTalk-WMI-Provider, WMI-Dienst, Namespace und ACL gezielt pruefen. |
|
||||
| Nur SQL-gestuetzte Klassen scheitern | Computerkonto in die BizTalk-Operator-Gruppe aufnehmen. |
|
||||
| Nur Event-Log-Service ist `UNKNOWN` | Lokalen Application-Log-Zugriff pruefen. |
|
||||
| Fehler bleibt mit Operator-Rolle bestehen | Betroffene Klasse und konkrete BizTalk-/SQL-Rollenanforderung untersuchen. |
|
||||
Nach dem Agent-Dump:
|
||||
|
||||
### Automatische Diagnose im Programm
|
||||
1. Service Discovery fuer den BizTalk-Host ausfuehren.
|
||||
2. sechs stabile Services aufnehmen.
|
||||
3. Changes aktivieren.
|
||||
4. Views/Benachrichtigungen nach Umgebung konfigurieren.
|
||||
|
||||
WMI-Verbindungsaufbau und jede erforderliche WMI-Klasse werden separat bewertet. Erwartbare Exceptions werden in folgende Kategorien eingeordnet:
|
||||
## 9. Logging
|
||||
|
||||
| Diagnose | Bedeutung | Ausgegebene Massnahme |
|
||||
Pfad:
|
||||
|
||||
```text
|
||||
%ProgramData%\BizTalkCheckmkPulse\logs\
|
||||
biztalk-checkmk-pulse-YYYYMMDD.log
|
||||
```
|
||||
|
||||
Jede Zeile enthaelt UTC-Zeit, Level, Komponente, PID, Windows-Identitaet und
|
||||
einzeilige Nachricht. Provider-Erfolge werden pro Lauf geloggt; der Consumer
|
||||
loggt nur abgelehnte Snapshots. Exceptions werden mit Typ, Nachricht,
|
||||
HRESULT-/Providerdetails und Stacktrace einzeilig gespeichert.
|
||||
|
||||
Logging ist best effort: Ein blockiertes Log darf Checkmk-Ausgabe oder
|
||||
Snapshot-Publikation nicht zerstoeren. Der Provider entfernt beim Start Dateien
|
||||
aelter als `LogRetentionDays`.
|
||||
|
||||
## 10. Diagnose und Runbook
|
||||
|
||||
### 10.1 Grundpruefung
|
||||
|
||||
```powershell
|
||||
Get-ScheduledTaskInfo -TaskName 'BizTalk Checkmk Pulse Provider'
|
||||
Get-Item "$env:ProgramData\BizTalkCheckmkPulse\data\biztalk-checkmk-pulse.snapshot"
|
||||
Get-Content "$env:ProgramData\BizTalkCheckmkPulse\logs\*.log" -Tail 100
|
||||
& "$env:ProgramFiles\BizTalkCheckmkPulse\BizTalkCheckmkPulse.exe" --consume
|
||||
& "C:\Program Files (x86)\checkmk\service\cmk-agent-ctl.exe" dump
|
||||
```
|
||||
|
||||
### 10.2 Kein Snapshot
|
||||
|
||||
Pruefen:
|
||||
|
||||
1. Task existiert und ist aktiviert.
|
||||
2. `LastTaskResult` und Provider-Log.
|
||||
3. Dienstkonto kann sich als Batch anmelden.
|
||||
4. gMSA ist lokal installiert und abrufbar.
|
||||
5. Provider besitzt Modify auf `data` und `logs`.
|
||||
6. EXE/Config sind ausfuehrbar.
|
||||
|
||||
### 10.3 Staler Snapshot
|
||||
|
||||
Der Consumer nennt Alter und Grenzwert. Ursachen:
|
||||
|
||||
- Task deaktiviert oder Konto/Kennwort ungueltig,
|
||||
- vorheriger Lauf haengt,
|
||||
- WMI-/SQL-Timeouts,
|
||||
- Task Scheduler lief waehrend Wartung nicht,
|
||||
- Snapshot-Publikation wird durch AV/EDR oder ACL blockiert.
|
||||
|
||||
Nicht einfach `SnapshotMaxAgeSeconds` stark erhoehen. Zuerst den minuetlichen
|
||||
Lauf wiederherstellen. Bei geplanten Wartungen darf Checkmk bewusst `UNKNOWN`
|
||||
anzeigen.
|
||||
|
||||
### 10.4 SHA-256-/Formatfehler
|
||||
|
||||
Snapshot nicht reparieren oder direkt an Checkmk kopieren. Task neu starten,
|
||||
Datentraeger und AV/EDR pruefen. Wiederholt sich der Fehler, Temporaerdateien,
|
||||
NTFS-Ereignisse und Schreibrechte untersuchen.
|
||||
|
||||
### 10.5 BizTalk-/SQL-Permission
|
||||
|
||||
1. Log zeigt `execution_identity` des Providers.
|
||||
2. `BizTalkReadOnlyUserGroup` gegen BizTalk-Konfiguration abgleichen.
|
||||
3. AD-Gruppenmitgliedschaft des Provider-Kontos pruefen.
|
||||
4. neuen Logontoken durch neuen Tasklauf sicherstellen.
|
||||
5. SQL-seitig Gruppenlogin und `BTS_READONLY_USERS` read-only pruefen.
|
||||
6. keine Einzelrechte erteilen.
|
||||
7. Nur wenn eine spezifische Klasse weiter abgewiesen wird, Read-Only-Umfang
|
||||
mit Microsoft-Dokumentation und BizTalk-Betrieb abgleichen; Operator-Rolle
|
||||
ist eine bewusste Eskalation.
|
||||
|
||||
### 10.6 WMI-Schema
|
||||
|
||||
`InvalidClass` oder `InvalidQuery` wird als `Wmi/Schema` klassifiziert.
|
||||
Berechtigungserhoehung kann eine nicht vorhandene Klasse nicht erzeugen.
|
||||
Installierte BizTalk-Version, WQL und Properties pruefen.
|
||||
|
||||
## 11. Konfiguration
|
||||
|
||||
Alle AppSettings befinden sich in:
|
||||
|
||||
```text
|
||||
%ProgramFiles%\BizTalkCheckmkPulse\BizTalkCheckmkPulse.exe.config
|
||||
```
|
||||
|
||||
Runtime:
|
||||
|
||||
| Key | Default | Bereich |
|
||||
| --- | --- | --- |
|
||||
| `Wmi/Permission` | Namespace, BizTalk-WMI-Klasse oder deren eingebetteter SQL-Zugriff verweigert den Zugriff. | Namespace-ACL nur bei Verbindungsfehlern; bei `Login failed for user` das genannte Maschinenkonto der BizTalk-Operator-Gruppe zuordnen. |
|
||||
| `Wmi/Connectivity` | WMI-/RPC-Ziel nicht erreichbar. | WMI-Dienst, Provider und bei Remote-WMI zusaetzlich DNS/RPC/Firewall pruefen. |
|
||||
| `Wmi/Timeout` | WMI-Abfrage ueberschreitet `QueryTimeoutSeconds`. | WMI-, BizTalk- und SQL-Auslastung untersuchen, bevor der Timeout erhoeht wird. |
|
||||
| `Wmi/Configuration` | BizTalk-WMI-Namespace fehlt oder Plattformdaten sind unvollstaendig. | Provider, Namespace und BizTalk-Konfiguration pruefen. |
|
||||
| `Wmi/Schema` | `InvalidClass` oder `InvalidQuery`; Klasse beziehungsweise WQL passt nicht zum installierten Provider. | Klasse/Properties gegen das BizTalk-WMI-Schema pruefen; keine Rechteerhoehung vornehmen. |
|
||||
| `Sql/Permission` | Login oder Datenbankzugriff wird abgelehnt. | Maschinenkonto der BizTalk-Operator-Gruppe zuordnen, Kerberos erneuern, keine direkten DB-Rollen vergeben. |
|
||||
| `Sql/Connectivity` | SQL-Server oder Instanz nicht erreichbar. | Servername, DNS, SQL-Dienst, TCP-Protokoll, Port und Firewall pruefen. |
|
||||
| `Sql/Timeout` | SQL-Verbindung oder Testabfrage laeuft in den Timeout. | Netzwerk und SQL-Auslastung pruefen; Timeout nur begruendet anheben. |
|
||||
| `Sql/Configuration` | WMI-Ziele unvollstaendig oder TLS-, Zertifikats-, SPN-/SSPI-Konfiguration fehlerhaft. | Plattformabfragen beziehungsweise Zertifikatskette, Verschluesselung, SPN und Kerberos gezielt pruefen. |
|
||||
| `SnapshotPath` | `%ProgramData%\BizTalkCheckmkPulse\data\...` | absolut |
|
||||
| `SnapshotMaxAgeSeconds` | `180` | 60 bis 86400 |
|
||||
| `SnapshotMaxBytes` | `1048576` | 4096 bis 16777216 |
|
||||
| `LogDirectory` | `%ProgramData%\BizTalkCheckmkPulse\logs` | absolut |
|
||||
| `LogRetentionDays` | `30` | 1 bis 365 |
|
||||
|
||||
Jede Diagnose enthaelt Bereich/Kategorie, betroffene Komponente, eine kurze Ursache, `Massnahme:` und `Technik:` mit Exception-Typ, HRESULT oder SQL-Fehlernummer. Bei WMI-Abfragefehlern werden zusaetzlich WQL und Laufzeit bis zum Fehler ausgegeben. Erforderliche Datenquellen besitzen eigene Verfuegbarkeitsflags. Schlaegt beispielsweise `MSBTS_ServiceInstance` fehl, wird `BizTalk Suspended Instances` zwingend `UNKNOWN`; eine leere Ergebnisliste darf nicht als 'keine Suspensions' fehlinterpretiert werden.
|
||||
BizTalk-Probes:
|
||||
|
||||
Die Operator-Mitgliedschaft des Computerkontos steht allen auf diesem Server als `LocalSystem` laufenden Diensten fuer Netzwerkzugriffe zur Verfuegung. Falls diese Sicherheitsauswirkung nicht akzeptabel ist, kann ein separater Collector unter einem dedizierten gMSA- oder Dienstkonto mit Operator-Rechten Checkmk-Spooldaten erzeugen. Diese Variante ist noch nicht Bestandteil der aktuellen Implementierung. Der komplette Checkmk-Agent sollte nicht allein fuer dieses Plugin auf eine andere Identitaet umgestellt werden, weil dies alle Agent-Sektionen und Local Checks betrifft.
|
||||
| Key | Default |
|
||||
| --- | --- |
|
||||
| `Server` | `.` |
|
||||
| `QueryTimeoutSeconds` | `25` |
|
||||
| `ProbeSqlConnectivity` | `true` |
|
||||
| `SqlConnectionTimeoutSeconds` | `5` |
|
||||
| `ProbeEventLog` | `true` |
|
||||
| `EventLogLookbackMinutes` | `60` |
|
||||
|
||||
Quellen:
|
||||
Alarmierung:
|
||||
|
||||
| Key | Default |
|
||||
| --- | --- |
|
||||
| `WarnResumableThreshold` | `1` |
|
||||
| `CritNonResumableThreshold` | `1` |
|
||||
| `AlertOnArtifactRuntimeIssues` | `false` |
|
||||
| `EmitPerApplicationSuspensionServices` | `false` |
|
||||
| `EventLogWarnThreshold` | `1` |
|
||||
| `EventLogCritThreshold` | `10` |
|
||||
|
||||
## 12. Tests und Abnahmekriterien
|
||||
|
||||
Automatisiert:
|
||||
|
||||
- Release-Build .NET Framework 4.7.2,
|
||||
- exakt sechs Self-Test-Services,
|
||||
- unbekannte Quellen werden `UNKNOWN`,
|
||||
- dynamische Anwendungsservices nur bei bekannter Anwendung,
|
||||
- WMI-Queryvertrag nutzt dokumentierte Properties,
|
||||
- eingebetteter SQL-Loginfehler wird `Permission`,
|
||||
- abgewiesener Principal wird extrahiert,
|
||||
- Snapshot-Roundtrip und Ersatz,
|
||||
- SHA-256-Manipulation wird verworfen,
|
||||
- Stale-Snapshot wird verworfen,
|
||||
- Consumer-Fallback enthaelt sechs `UNKNOWN`-Services.
|
||||
|
||||
Windows-/ACC-Abnahme:
|
||||
|
||||
1. `LastTaskResult=0`.
|
||||
2. Snapshot wird mindestens dreimal minuetlich aktualisiert.
|
||||
3. Provider-Log nennt das dedizierte Konto.
|
||||
4. `BizTalk Platform` zeigt `read_only_group=`.
|
||||
5. `BizTalk SQL Access`: `targets=2`, `available=2`.
|
||||
6. keine Permission-`UNKNOWN`s.
|
||||
7. ACL-Test: `LocalSystem` kann Snapshot lesen, nicht schreiben.
|
||||
8. Task deaktivieren: nach 180 Sekunden sechs stale-`UNKNOWN`s.
|
||||
9. Task wieder aktivieren: naechster Snapshot stellt Echtzustand her.
|
||||
10. Agent-Dump und Checkmk Service Discovery erfolgreich.
|
||||
|
||||
Erst nach ACC-Abnahme erfolgt der gestufte Rollout nach DEV/TST/PRD.
|
||||
|
||||
## 13. Deinstallation und Rollback
|
||||
|
||||
```powershell
|
||||
.\Uninstall-BizTalkCheckmkPulse.ps1
|
||||
```
|
||||
|
||||
`-KeepRuntimeData` erhaelt Logs und Snapshot. Das Skript entfernt Task,
|
||||
Wrapper und Programmdateien, aber keine AD-Gruppenmitgliedschaft. Die
|
||||
Berechtigung wird separat nach Change-Verfahren entzogen.
|
||||
|
||||
Rollback auf die alte direkte `LocalSystem`-Architektur ist nicht empfohlen.
|
||||
Falls kurzfristig notwendig, muss Security die Maschinenkonto-Berechtigung
|
||||
explizit freigeben; der Checkmk-Agentdienst selbst darf nicht auf das
|
||||
Collector-Konto umgestellt werden.
|
||||
|
||||
## 14. Transport per certutil
|
||||
|
||||
Die Source-Uebergabe besteht aus ZIP plus Base64-TXT:
|
||||
|
||||
```cmd
|
||||
certutil -decode <datei>.zip.b64.txt <datei>.zip
|
||||
certutil -hashfile <datei>.zip SHA256
|
||||
tar -xf <datei>.zip
|
||||
```
|
||||
|
||||
ZIP und Base64 werden erst nach dem Git-Commit aus genau diesem Commit erzeugt.
|
||||
|
||||
## 15. Referenzen
|
||||
|
||||
- https://learn.microsoft.com/en-us/biztalk/core/managing-biztalk-server-security
|
||||
- https://learn.microsoft.com/en-us/biztalk/core/windows-groups-and-user-accounts-in-biztalk-server
|
||||
- https://learn.microsoft.com/en-us/biztalk/core/technical-reference/msbts-groupsetting-biztalkreadonlyusergroup-property-wmi
|
||||
- https://learn.microsoft.com/en-us/biztalk/core/technical-reference/msbts-groupsetting-wmi
|
||||
- https://docs.checkmk.com/latest/en/agent_windows.html
|
||||
- https://docs.checkmk.com/latest/en/localchecks.html
|
||||
- https://learn.microsoft.com/en-us/biztalk/core/minimum-security-user-rights
|
||||
- https://learn.microsoft.com/en-us/biztalk/core/access-control-and-data-security
|
||||
- https://learn.microsoft.com/en-us/biztalk/core/windows-groups-and-user-accounts-in-biztalk-server
|
||||
- https://learn.microsoft.com/en-us/biztalk/core/access-control-for-administrative-roles
|
||||
- https://learn.microsoft.com/en-us/biztalk/core/how-to-modify-group-properties
|
||||
- https://learn.microsoft.com/en-us/entra/architecture/service-accounts-computer
|
||||
- https://learn.microsoft.com/en-us/powershell/module/activedirectory/add-adgroupmember
|
||||
- https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/klist
|
||||
- https://learn.microsoft.com/en-us/windows/win32/wmisdk/access-to-wmi-namespaces
|
||||
|
||||
## Resilienz
|
||||
|
||||
Das Plugin ist bewusst defensiv gebaut:
|
||||
|
||||
- Der dependency-freie Regressionstest laeuft mit dem gleichen .NET-Framework-/MSBuild-Baseline wie die Anwendung.
|
||||
- WMI-Queries haben ein konfigurierbares Timeout.
|
||||
- WMI-Namespace und Pflichtklassen werden getrennt auf Berechtigung, Konfiguration, Erreichbarkeit und Timeout geprueft.
|
||||
- Management- und Master-MessageBox-Datenbank werden aus einer dokumentierten WMI-Klasse ermittelt und mit der echten Agent-Identitaet getestet.
|
||||
- `InvalidClass`/`InvalidQuery` werden als Schemafehler statt als Berechtigungsproblem ausgewiesen.
|
||||
- Host Instances werden ohne namensabhaengigen WQL-Filter clientseitig auf den ueberwachten Server begrenzt.
|
||||
- Bereits fuer Runtime-Zustaende gelesene Artefakte werden fuer das Best-Effort-Application-Mapping wiederverwendet; doppelte WMI-Abfragen entfallen.
|
||||
- SQL-Fehlernummern werden in Berechtigung, Erreichbarkeit, Timeout oder Providerfehler klassifiziert.
|
||||
- Fehlgeschlagene Pflichtabfragen erzeugen `UNKNOWN` statt irrefuehrender Nullwerte.
|
||||
- Optionale WMI-Klassen erzeugen Diagnosehinweise statt Totalabbruch.
|
||||
- Fehlende Properties werden als leer/unknown behandelt.
|
||||
- Local-Check-Ausgaben verwenden gueltige Checkmk-Zeilen mit genau vier Feldern.
|
||||
- Fatal Errors erzeugen `UNKNOWN` fuer alle stabilen Services.
|
||||
- Event-Log-Auswertung ist isoliert; ein Fehler dort bricht WMI-Monitoring nicht ab.
|
||||
- Service-Namen sind stabil, damit Service Discovery nicht bei jedem Lauf neue Services erzeugt.
|
||||
|
||||
## Statusmodell
|
||||
|
||||
### BizTalk Platform
|
||||
|
||||
- `OK`: BizTalk-WMI erreichbar.
|
||||
- `UNKNOWN`: WMI nicht erreichbar oder kompletter Programmfehler.
|
||||
|
||||
Dieser Service ist der Integrationsindikator. Wenn er `UNKNOWN` ist, sind Berechtigungen, BizTalk-Installation oder WMI-Repository zu pruefen.
|
||||
|
||||
### BizTalk SQL Access
|
||||
|
||||
- `OK`: Datenbankziele vollstaendig ermittelt und integrierte Anmeldung an allen Zielen erfolgreich.
|
||||
- `UNKNOWN`: Zielermittlung unvollstaendig, Anmeldung verweigert, SQL nicht erreichbar, Timeout oder Providerfehler.
|
||||
- Bei `ProbeSqlConnectivity=false`: `OK` mit sichtbarem Hinweis, dass die Probe deaktiviert ist.
|
||||
|
||||
Metriken: `biztalk_sql_targets_total`, `biztalk_sql_targets_available` und `biztalk_sql_targets_failed`. Die Ausgabe nennt ausserdem `execution_identity`, `network_identity` und den Zustand jedes getesteten Datenbankziels.
|
||||
|
||||
### BizTalk Suspended Instances
|
||||
|
||||
- `OK`: keine suspendierten Instanzen.
|
||||
- `WARN`: mindestens `WarnResumableThreshold` resumable suspended instances.
|
||||
- `CRIT`: mindestens `CritNonResumableThreshold` non-resumable suspended instances.
|
||||
- `UNKNOWN`: Datenquelle nicht lesbar.
|
||||
|
||||
### BizTalk Host Instances
|
||||
|
||||
- `OK`: alle Host-Instanzen sind started.
|
||||
- `WARN`: mindestens eine Host-Instanz ist pending.
|
||||
- `CRIT`: mindestens eine Host-Instanz ist stopped oder unknown.
|
||||
- `UNKNOWN`: keine Host-Instanzen gefunden oder Datenquelle nicht lesbar.
|
||||
|
||||
### BizTalk Runtime Artifacts
|
||||
|
||||
- `OK`: Artefakte lesbar, keine unbekannten Statuswerte.
|
||||
- `WARN`: nur wenn `AlertOnArtifactRuntimeIssues=true` und deaktivierte/inaktive Artefakte vorhanden sind.
|
||||
- `CRIT`: unbekannte Send-Port- oder Orchestration-Statuswerte.
|
||||
- `UNKNOWN`: mindestens eine erforderliche WMI-Artefaktklasse nicht lesbar.
|
||||
|
||||
Deaktivierte Receive Locations und gestoppte Ports koennen in BizTalk fachlich korrekt sein. Deshalb ist die Alarmierung hier standardmaessig informativ.
|
||||
|
||||
### BizTalk Event Log
|
||||
|
||||
- `OK`: keine relevanten Fehler/Warnungen ueber Schwellwert.
|
||||
- `WARN`: Fehler oder Warnungen ab `EventLogWarnThreshold`.
|
||||
- `CRIT`: Fehler ab `EventLogCritThreshold`.
|
||||
- `UNKNOWN`: Event Log nicht lesbar.
|
||||
|
||||
## Dashboard-Empfehlung
|
||||
|
||||
Pro Umgebung sollte der BizTalk-Host in einem eigenen Host-Ordner oder Host-Tag fuer `ACC`, `DEV`, `TST`, `PRD` liegen. Im Dashboard eignen sich:
|
||||
|
||||
- Service State Widgets fuer die sechs stabilen BizTalk-Services
|
||||
- Graphen fuer `biztalk_sql_targets_failed`, `biztalk_suspended_total`, `biztalk_host_instances_stopped`, `biztalk_eventlog_errors`
|
||||
- Hostgruppe/Ordner pro Umgebung
|
||||
- Optional eine View gefiltert auf `Service starts with BizTalk`
|
||||
|
||||
Empfohlene Reihenfolge im Dashboard:
|
||||
|
||||
1. `BizTalk Platform`
|
||||
2. `BizTalk SQL Access`
|
||||
3. `BizTalk Suspended Instances`
|
||||
4. `BizTalk Host Instances`
|
||||
5. `BizTalk Event Log`
|
||||
6. `BizTalk Runtime Artifacts`
|
||||
7. SQL-Server-Services aus dem Checkmk-MSSQL-Plugin
|
||||
|
||||
## Rollout-Vorgehen
|
||||
|
||||
1. Build-Paket erzeugen.
|
||||
2. In `DEV` auf dem BizTalk-Server installieren.
|
||||
3. `--self-test` und normalen Lauf ausfuehren.
|
||||
4. Agent-Dump pruefen.
|
||||
5. Checkmk Discovery durchfuehren.
|
||||
6. Eine Woche Messwerte und false positives beobachten.
|
||||
7. Nach `TST` und `ACC` uebernehmen.
|
||||
8. In `PRD` mit `AlertOnArtifactRuntimeIssues=false` starten.
|
||||
9. Nach Betriebsfreigabe Schwellwerte feinjustieren.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Service bleibt UNKNOWN
|
||||
|
||||
Pruefen:
|
||||
|
||||
```cmd
|
||||
"%ProgramData%\checkmk\agent\local\biztalk_checkmk_pulse.cmd"
|
||||
```
|
||||
|
||||
Wenn WMI nicht erreichbar ist:
|
||||
|
||||
```cmd
|
||||
wmic /namespace:\\root\MicrosoftBizTalkServer path MSBTS_HostInstance get HostName,RunningServer,ServiceState
|
||||
```
|
||||
|
||||
Der Checkmk Windows Agent laeuft normalerweise als LocalSystem. Daher immer auch den Agent-Dump verwenden:
|
||||
|
||||
```cmd
|
||||
"C:\Program Files (x86)\checkmk\service\cmk-agent-ctl.exe" dump
|
||||
```
|
||||
|
||||
Die Diagnose im Service-Text nach `Massnahme:` abarbeiten. Wichtige Kategorien:
|
||||
|
||||
- `Wmi/Permission`: lokale Namespace-ACL oder BizTalk-Operator-Zuordnung pruefen.
|
||||
- `Wmi/Schema`: Klasse/WQL gegen das installierte BizTalk-Schema pruefen; `InvalidClass` ist nicht durch zusaetzliche Rechte loesbar.
|
||||
- `Sql/Permission`: Maschinenkonto `DOMAIN\BIZTALKSERVER$` und BizTalk-Operator-Gruppe pruefen.
|
||||
- `Sql/Connectivity`: SQL-Server-/Instanzname, DNS, SQL-Dienst, TCP und Firewall pruefen.
|
||||
- `Sql/Timeout`: SQL- und Netzwerkauslastung pruefen; Timeout nicht als erste Massnahme erhoehen.
|
||||
|
||||
Der direkte Aufruf des Wrappers kann wegen des angemeldeten Administratorkontos ein anderes Ergebnis liefern als der Agent-Dump. Fuer die Freigabe ist immer der Agent-Dump massgeblich.
|
||||
|
||||
### Keine Services in Discovery
|
||||
|
||||
Pruefen:
|
||||
|
||||
- Liegt `biztalk_checkmk_pulse.cmd` direkt unter `%ProgramData%\checkmk\agent\local`?
|
||||
- Gibt der Wrapper direkt eine Zeile im Format `0 "Service" metric=value Details` aus?
|
||||
- Wurde der Checkmk-Agent nach Policy-/Bakery-Aenderungen neu ausgerollt?
|
||||
|
||||
### Runtime Artifacts zeigt deaktivierte Artefakte
|
||||
|
||||
Das ist standardmaessig `OK`, damit gewollt deaktivierte BizTalk-Artefakte nicht alarmieren. Fuer strengere PRD-Standards:
|
||||
|
||||
```xml
|
||||
<add key="AlertOnArtifactRuntimeIssues" value="true" />
|
||||
```
|
||||
|
||||
## Weiterentwicklung
|
||||
|
||||
Sinnvolle naechste Ausbaustufen:
|
||||
|
||||
- MKP mit Agent-Bakery-Regel fuer zentrale Konfiguration.
|
||||
- Optionales serverseitiges Check-Plugin nach Checkmk Check API V2.
|
||||
- Custom Dashboard/View als Checkmk GUI Extension.
|
||||
- Ergaenzung um MessageBox-Spool/Tracking-Daten, falls operativ benoetigt.
|
||||
- https://docs.checkmk.com/latest/en/spool_directory.html
|
||||
|
||||
@@ -1,24 +1,80 @@
|
||||
# BizTalk Checkmk Pulse
|
||||
|
||||
`BizTalk Checkmk Pulse` ist ein lokaler Checkmk-Check fuer Microsoft BizTalk Server 2020 auf Windows. Er liest BizTalk-Betriebsdaten ueber WMI aus `root\MicrosoftBizTalkServer`, erzeugt Checkmk-Local-Check-Ausgaben und kann dadurch ohne serverseitigen Python-Check in Checkmk 2.4 integriert werden.
|
||||
`BizTalk Checkmk Pulse` ist ein robuster Checkmk-2.4-Local-Check fuer Microsoft
|
||||
BizTalk Server 2020 auf Windows Server 2019. Die Anwendung trennt den
|
||||
berechtigten BizTalk-Datenzugriff vollstaendig vom Checkmk-Agenten:
|
||||
|
||||
Der Ansatz ist fuer Umgebungen wie `ACC`, `DEV`, `TST` und `PRD` gedacht, in denen pro Umgebung ein BizTalk Server 2020 und ein SQL Server betrieben werden. SQL Server bleibt beim mitgelieferten Checkmk-MSSQL-Plugin; dieses Projekt ergaenzt die BizTalk-spezifische Sicht.
|
||||
```text
|
||||
Scheduled Task (dediziertes Dienstkonto oder gMSA)
|
||||
|
|
||||
| jede Minute: BizTalkCheckmkPulse.exe --collect
|
||||
v
|
||||
lokales BizTalk-WMI + BizTalk-SQL + Application Event Log
|
||||
|
|
||||
| atomarer, versionierter, SHA-256-geschuetzter Snapshot
|
||||
v
|
||||
%ProgramData%\BizTalkCheckmkPulse\data\biztalk-checkmk-pulse.snapshot
|
||||
^
|
||||
| nur lesen: BizTalkCheckmkPulse.exe --consume
|
||||
|
|
||||
Checkmk Windows Agent (LocalSystem)
|
||||
|
|
||||
v
|
||||
sechs stabile Checkmk Local Checks
|
||||
```
|
||||
|
||||
Die Plattform- und SQL-Zielermittlung verwendet ausschliesslich die dokumentierte WMI-Klasse `MSBTS_GroupSetting`. Management-Datenbank und Master-MessageBox werden ueber `MgmtDbServerName`/`MgmtDbName` sowie `SubscriptionDBServerName`/`SubscriptionDBName` gelesen. Eine Klasse `MSBTS_MessageBoxSetting` gehoert nicht zum BizTalk-WMI-Schema und wird bewusst nicht abgefragt.
|
||||
Damit bekommt `LocalSystem` keine BizTalk- oder SQL-Berechtigung. Nur das
|
||||
dedizierte Provider-Konto wird in die konfigurierte BizTalk Server Read Only
|
||||
Users-Gruppe aufgenommen. Der Checkmk-Consumer liest keine BizTalk-Datenbank,
|
||||
verwendet kein WMI und nimmt keine Identitaetswechsel vor.
|
||||
|
||||
## Warum Local Check statt serverseitigem Check-Plugin?
|
||||
## Warum die Architektur geaendert wurde
|
||||
|
||||
Checkmk 2.4 kann Windows-Agent-Plugins und Local Checks direkt ausfuehren. Fuer diese Umgebung ist ein C#/.NET-Framework-Programm mit `.cmd`-Wrapper die robusteste Variante:
|
||||
Der ACC-Test vom 29.07.2026 zeigte:
|
||||
|
||||
- keine PowerShell-Ausfuehrung erforderlich
|
||||
- keine BizTalk-ExplorerOM/OperationsOM-DLL als Build-Abhaengigkeit
|
||||
- keine Python-Check-API-Abhaengigkeit auf der Checkmk-Site
|
||||
- einfache Verteilung auf die BizTalk-Maschinen per Dateiablage, Softwareverteilung oder Agent Bakery
|
||||
- automatische Service Discovery in Checkmk
|
||||
```text
|
||||
Login failed for user 'BEW\AV23AGPWBIO1$'
|
||||
```
|
||||
|
||||
## Erzeugte Checkmk-Services
|
||||
Der Checkmk-Agent lief korrekt als `NT AUTHORITY\SYSTEM` und erreichte lokales
|
||||
BizTalk-WMI. Datenbankgestuetzte WMI-Abfragen wurden jedoch am SQL Server als
|
||||
Maschinenkonto `BEW\AV23AGPWBIO1$` abgewiesen. Eine Mitgliedschaft des
|
||||
Computerkontos wuerde die BizTalk-Netzwerkberechtigung allen als `LocalSystem`
|
||||
laufenden Diensten des Servers geben.
|
||||
|
||||
Standardmaessig entstehen diese stabilen Services:
|
||||
Die neue Trennung reduziert diesen Berechtigungsumfang. Fuer BizTalk Server
|
||||
2020 ist die konfigurierte `BizTalk Server Read Only Users`-Gruppe mit der
|
||||
SQL-Datenbankrolle `BTS_READONLY_USERS` die bevorzugte Grenze. Die
|
||||
`BizTalk Server Operators`-Gruppe ist nur ein dokumentierter Fallback, wenn
|
||||
eine konkret benoetigte WMI-Klasse trotz bestaetigter Read-Only-Zuordnung
|
||||
abgewiesen wird. Direkte SQL-Logins, manuelle Datenbankrollen und `sysadmin`
|
||||
sind keine Loesung.
|
||||
|
||||
## Robustheit
|
||||
|
||||
Der Datenaustausch ist bewusst defensiv:
|
||||
|
||||
- Der Provider schreibt zuerst eine eindeutige Temporaerdatei im Zielordner,
|
||||
leert Betriebssystempuffer und ersetzt danach den Snapshot atomar.
|
||||
- Der Snapshot enthaelt Formatversion, UTC-Zeit, Quellmaschine,
|
||||
Provider-Identitaet, Zeilenanzahl und SHA-256 des Payloads.
|
||||
- Der Consumer akzeptiert nur denselben Rechner, gueltiges UTF-8, intakte
|
||||
Checkmk-Zeilen, korrekte SHA-256-Pruefsumme und ein maximales Alter von
|
||||
standardmaessig 180 Sekunden.
|
||||
- Fehlende, veraltete, abgeschnittene, manipulierte oder unlesbare Dateien
|
||||
ergeben sechs gueltige `UNKNOWN`-Services statt einer kaputten Agent-Ausgabe.
|
||||
- Ein exklusives Lock und die Task-Einstellung `IgnoreNew` verhindern
|
||||
ueberlappende Providerlaeufe.
|
||||
- Ein unerwarteter Providerfehler erzeugt nach Moeglichkeit einen aktuellen
|
||||
`UNKNOWN`-Snapshot und einen ungleich null lautenden Task-Exitcode.
|
||||
- Provider und Consumer protokollieren in taegliche Dateien; die Aufbewahrung
|
||||
ist standardmaessig 30 Tage.
|
||||
- Snapshotgroesse, WMI-/SQL-Timeouts, Log-Retention und Stale-Grenze sind
|
||||
begrenzt und konfigurierbar.
|
||||
|
||||
## Erzeugte Services
|
||||
|
||||
Standardmaessig entstehen:
|
||||
|
||||
- `BizTalk Platform`
|
||||
- `BizTalk SQL Access`
|
||||
@@ -27,323 +83,301 @@ Standardmaessig entstehen diese stabilen Services:
|
||||
- `BizTalk Runtime Artifacts`
|
||||
- `BizTalk Event Log`
|
||||
|
||||
Wenn `EnvironmentName=ACC`, `DEV`, `TST` oder `PRD` gesetzt wird, wird der Name vor den Suffix gesetzt, z.B. `BizTalk PRD Suspended Instances`. Das ist praktisch, wenn die Umgebung bereits im Hostnamen oder Ordner abgebildet ist, aber nicht zwingend noetig.
|
||||
Mit `EnvironmentName=ACC`, `DEV`, `TST` oder `PRD` wird die Umgebung in den
|
||||
Servicenamen aufgenommen, zum Beispiel `BizTalk ACC Platform`.
|
||||
|
||||
Details zu Statuslogik und Metriken stehen in [docs/CheckmkServices.md](docs/CheckmkServices.md).
|
||||
Beispielausgaben stehen in [docs/ExampleOutput.md](docs/ExampleOutput.md).
|
||||
Die konkrete ACC-Freigabeanleitung steht als Textdatei in [docs/ACC-WMI-SQL-Berechtigung.txt](docs/ACC-WMI-SQL-Berechtigung.txt).
|
||||
Statuslogik und Metriken: [docs/CheckmkServices.md](docs/CheckmkServices.md)
|
||||
|
||||
## Build
|
||||
Beispielausgaben: [docs/ExampleOutput.md](docs/ExampleOutput.md)
|
||||
|
||||
Voraussetzungen auf einem Windows-Build-Host:
|
||||
## Voraussetzungen
|
||||
|
||||
Build-Host:
|
||||
|
||||
- Visual Studio 2019/2022 Build Tools oder Visual Studio
|
||||
- MSBuild im `PATH`
|
||||
- .NET Framework 4.7.2 Developer Pack
|
||||
|
||||
Build:
|
||||
BizTalk-Server:
|
||||
|
||||
- Windows Server 2019
|
||||
- BizTalk Server 2020 und lokaler Namespace
|
||||
`root\MicrosoftBizTalkServer`
|
||||
- .NET Framework 4.7.2
|
||||
- Checkmk Windows Agent
|
||||
- administrativer Zugriff fuer die einmalige Installation
|
||||
- dediziertes AD-Dienstkonto oder bevorzugt gMSA fuer den Provider
|
||||
|
||||
Das Provider-Konto benoetigt:
|
||||
|
||||
- lokales Recht zur Ausfuehrung als Scheduled Task
|
||||
- lokalen Lese-/Ausfuehrungszugriff auf die installierte EXE
|
||||
- Schreibzugriff nur auf Snapshot- und Logverzeichnis
|
||||
- Mitgliedschaft in der exakt konfigurierten BizTalk Server Read Only
|
||||
Users-Gruppe
|
||||
|
||||
Es soll weder lokaler Administrator noch SQL-`sysadmin` sein. Fuer ein gMSA
|
||||
muss der BizTalk-Server das verwaltete Kennwort abrufen duerfen und das Konto
|
||||
lokal installiert sein.
|
||||
|
||||
## Build und Tests
|
||||
|
||||
```cmd
|
||||
scripts\build-release.cmd
|
||||
```
|
||||
|
||||
Build plus dependency-free regression tests:
|
||||
|
||||
```cmd
|
||||
scripts\test-release.cmd
|
||||
```
|
||||
|
||||
Deployment-Paket erstellen:
|
||||
|
||||
```cmd
|
||||
scripts\package-release.cmd
|
||||
```
|
||||
|
||||
Ergebnis:
|
||||
Das Paket wird unter `artifacts\BizTalkCheckmkPulse-deploy` erzeugt:
|
||||
|
||||
```text
|
||||
artifacts\BizTalkCheckmkPulse-deploy\
|
||||
BizTalkCheckmkPulse-deploy\
|
||||
Install-BizTalkCheckmkPulse.ps1
|
||||
Uninstall-BizTalkCheckmkPulse.ps1
|
||||
biztalk_checkmk_pulse.cmd
|
||||
BizTalkCheckmkPulse\
|
||||
application\
|
||||
BizTalkCheckmkPulse.exe
|
||||
BizTalkCheckmkPulse.exe.config
|
||||
```
|
||||
|
||||
Validierung nach dem Build:
|
||||
Format-Self-Test ohne WMI, SQL oder Event Log:
|
||||
|
||||
```cmd
|
||||
artifacts\BizTalkCheckmkPulse-deploy\biztalk_checkmk_pulse.cmd --self-test
|
||||
artifacts\BizTalkCheckmkPulse-deploy\application\BizTalkCheckmkPulse.exe --self-test
|
||||
```
|
||||
|
||||
Der Self-Test fuehrt keine WMI-, SQL- oder Event-Log-Abfrage aus und muss genau sechs `OK`-Zeilen liefern. Der Test-Runner prueft zusaetzlich UNKNOWN-Fallbacks, dynamische Application-Services, FQDN-/Kurznamensvergleich ohne WQL-Namensfilter und den dokumentierten `MSBTS_GroupSetting`-Queryvertrag. Die zusaetzliche lokale Verifikation mit Mono/MSBuild 16 prueft Build und Ausgabeformat, ersetzt aber nicht den Windows-/BizTalk-Test.
|
||||
Erwartet werden exakt sechs `OK`-Zeilen. Die Regressionstests pruefen
|
||||
zusaetzlich Snapshot-Roundtrip, atomaren Ersatz, SHA-256-Manipulation,
|
||||
Stale-Erkennung, stabile Fallbacks und die bestehenden BizTalk-WMI-Diagnosen.
|
||||
Ein Mono-Build ist eine hilfreiche Quellcodepruefung, ersetzt aber nicht die
|
||||
Windows-/BizTalk-Laufzeitvalidierung.
|
||||
|
||||
## Installation auf dem BizTalk-Server
|
||||
## Berechtigung vorbereiten
|
||||
|
||||
Kopiere den Inhalt von `artifacts\BizTalkCheckmkPulse-deploy` nach:
|
||||
Die exakte Read-Only-Gruppe wird in der BizTalk Administration Console unter
|
||||
den Eigenschaften der BizTalk-Gruppe abgelesen. Ein bereits berechtigtes Konto
|
||||
kann sie alternativ ermitteln:
|
||||
|
||||
```powershell
|
||||
Get-CimInstance `
|
||||
-Namespace root/MicrosoftBizTalkServer `
|
||||
-ClassName MSBTS_GroupSetting |
|
||||
Select-Object Name, BizTalkReadOnlyUserGroup, BizTalkOperatorGroup,
|
||||
MgmtDbServerName, MgmtDbName
|
||||
```
|
||||
|
||||
Ein AD-Administrator nimmt das neue Provider-Konto in
|
||||
`BizTalkReadOnlyUserGroup` auf. Bei einem gMSA endet der Kontoname mit `$`.
|
||||
Nach AD-Replikation muss ein regulaeres Dienstkonto einen neuen Anmeldetoken
|
||||
erhalten; bei gMSA wird der Task nach der Gruppenfreigabe neu gestartet.
|
||||
|
||||
Die BizTalk-Konfiguration muss die Domain-Gruppe bereits als Windows-Login und
|
||||
in `BizTalkMgmtDb`, `BizTalkMsgBoxDb`, `BizTalkDTADb`,
|
||||
`BizTalkRuleEngineDb` sowie gegebenenfalls `BAMPrimaryImport` mit
|
||||
`BTS_READONLY_USERS` abbilden. Eine fehlende Abbildung wird durch BizTalk- und
|
||||
SQL-Administration fuer die Gruppe repariert, nicht als Einzelberechtigung
|
||||
fuer das Provider-Konto.
|
||||
|
||||
## Installation mit gMSA
|
||||
|
||||
Deployment-Paket auf den BizTalk-Server kopieren. In administrativer Windows
|
||||
PowerShell:
|
||||
|
||||
```powershell
|
||||
Set-Location C:\Temp\BizTalkCheckmkPulse-deploy
|
||||
|
||||
# Optional, falls das gMSA noch nicht lokal installiert wurde:
|
||||
Install-ADServiceAccount -Identity svc_biztalk_cmk
|
||||
Test-ADServiceAccount -Identity svc_biztalk_cmk
|
||||
|
||||
.\Install-BizTalkCheckmkPulse.ps1 `
|
||||
-CollectorAccount 'BEW\svc_biztalk_cmk$' `
|
||||
-Gmsa `
|
||||
-EnvironmentName ACC
|
||||
```
|
||||
|
||||
## Installation mit regulaerem Dienstkonto
|
||||
|
||||
```powershell
|
||||
Set-Location C:\Temp\BizTalkCheckmkPulse-deploy
|
||||
.\Install-BizTalkCheckmkPulse.ps1 `
|
||||
-CollectorAccount 'BEW\svc_biztalk_cmk' `
|
||||
-EnvironmentName ACC
|
||||
```
|
||||
|
||||
Der Installer fragt das Kennwort ueber `Get-Credential` ab und speichert es
|
||||
durch die Windows-Aufgabenplanung. Das Kennwort steht weder in der
|
||||
Konfigurationsdatei noch in den Logs.
|
||||
|
||||
Der Installer:
|
||||
|
||||
1. kopiert EXE und Config nach
|
||||
`%ProgramFiles%\BizTalkCheckmkPulse`,
|
||||
2. erstellt `%ProgramData%\BizTalkCheckmkPulse\data` und `logs`,
|
||||
3. setzt explizite ACLs fuer Administratoren, Provider und `LocalSystem`,
|
||||
4. installiert nur den kleinen `.cmd`-Consumer unter
|
||||
`%ProgramData%\checkmk\agent\local`,
|
||||
5. registriert `BizTalk Checkmk Pulse Provider` minuetlich mit
|
||||
`IgnoreNew`, fuenf Minuten Laufzeitlimit und zwei Wiederholungen,
|
||||
6. fuehrt den Self-Test aus und startet den Provider einmalig.
|
||||
|
||||
PowerShell wird nur fuer Installation und Betriebsdiagnose verwendet. Der
|
||||
minuetliche Provider und der Checkmk-Consumer sind .NET-/CMD-Laufzeitcode und
|
||||
haengen nicht von der PowerShell Execution Policy ab.
|
||||
|
||||
## Verifikation auf dem Server
|
||||
|
||||
Task und letzter Lauf:
|
||||
|
||||
```powershell
|
||||
Get-ScheduledTask -TaskName 'BizTalk Checkmk Pulse Provider' |
|
||||
Select-Object TaskName, State
|
||||
Get-ScheduledTaskInfo -TaskName 'BizTalk Checkmk Pulse Provider' |
|
||||
Select-Object LastRunTime, LastTaskResult, NextRunTime
|
||||
```
|
||||
|
||||
Provider-Log:
|
||||
|
||||
```powershell
|
||||
Get-ChildItem "$env:ProgramData\BizTalkCheckmkPulse\logs" |
|
||||
Sort-Object LastWriteTime -Descending |
|
||||
Select-Object -First 3 Name, Length, LastWriteTime
|
||||
|
||||
Get-Content `
|
||||
"$env:ProgramData\BizTalkCheckmkPulse\logs\biztalk-checkmk-pulse-*.log" `
|
||||
-Tail 100
|
||||
```
|
||||
|
||||
Snapshot und Consumer:
|
||||
|
||||
```powershell
|
||||
Get-Item `
|
||||
"$env:ProgramData\BizTalkCheckmkPulse\data\biztalk-checkmk-pulse.snapshot" |
|
||||
Select-Object FullName, Length, LastWriteTimeUtc
|
||||
|
||||
& "$env:ProgramFiles\BizTalkCheckmkPulse\BizTalkCheckmkPulse.exe" --consume
|
||||
```
|
||||
|
||||
Verbindlicher Test im echten `LocalSystem`-Kontext:
|
||||
|
||||
```powershell
|
||||
& "C:\Program Files (x86)\checkmk\service\cmk-agent-ctl.exe" dump |
|
||||
Select-String -Pattern "BizTalk|UNKNOWN|Snapshot|Permission" -Context 0,1
|
||||
```
|
||||
|
||||
Danach in Checkmk eine Service Discovery ausfuehren, die sechs Services
|
||||
aufnehmen und Changes aktivieren. Ein zusaetzlicher Checkmk-Async-Cache ist
|
||||
nicht erforderlich: Der Consumer liest nur eine kleine lokale Datei und der
|
||||
Provider besitzt bereits seinen eigenen Minutentakt.
|
||||
|
||||
## Dateisystem und Sicherheitsgrenzen
|
||||
|
||||
```text
|
||||
%ProgramData%\checkmk\agent\local
|
||||
%ProgramFiles%\BizTalkCheckmkPulse\
|
||||
BizTalkCheckmkPulse.exe
|
||||
BizTalkCheckmkPulse.exe.config
|
||||
|
||||
%ProgramData%\BizTalkCheckmkPulse\
|
||||
data\
|
||||
biztalk-checkmk-pulse.snapshot
|
||||
biztalk-checkmk-pulse.snapshot.provider.lock
|
||||
logs\
|
||||
biztalk-checkmk-pulse-YYYYMMDD.log
|
||||
|
||||
%ProgramData%\checkmk\agent\local\
|
||||
biztalk_checkmk_pulse.cmd
|
||||
```
|
||||
|
||||
Zielstruktur:
|
||||
ACL-Soll:
|
||||
|
||||
```text
|
||||
%ProgramData%\checkmk\agent\local\biztalk_checkmk_pulse.cmd
|
||||
%ProgramData%\checkmk\agent\local\BizTalkCheckmkPulse\BizTalkCheckmkPulse.exe
|
||||
%ProgramData%\checkmk\agent\local\BizTalkCheckmkPulse\BizTalkCheckmkPulse.exe.config
|
||||
```
|
||||
| Pfad | Provider | LocalSystem | Administratoren |
|
||||
| --- | --- | --- | --- |
|
||||
| Programm | Lesen/Ausfuehren | Lesen/Ausfuehren | Vollzugriff |
|
||||
| `data` | Aendern | Lesen/Ausfuehren | Vollzugriff |
|
||||
| `logs` | Aendern | Aendern | Vollzugriff |
|
||||
|
||||
Manueller Test auf dem BizTalk-Server:
|
||||
|
||||
```cmd
|
||||
"%ProgramData%\checkmk\agent\local\biztalk_checkmk_pulse.cmd" --self-test
|
||||
"%ProgramData%\checkmk\agent\local\biztalk_checkmk_pulse.cmd"
|
||||
```
|
||||
|
||||
Agent-Ausgabe wie Checkmk sie sieht:
|
||||
|
||||
```cmd
|
||||
"C:\Program Files (x86)\checkmk\service\cmk-agent-ctl.exe" dump
|
||||
```
|
||||
|
||||
Danach in Checkmk fuer den BizTalk-Host eine Service Discovery ausfuehren, die neuen Services aufnehmen und Changes aktivieren.
|
||||
Der Snapshot enthaelt Monitoringzustand und kompakte Fehlerdetails, aber keine
|
||||
Passwoerter oder Nachrichteninhalte. Der Consumer validiert die Datei trotzdem
|
||||
vollstaendig, bevor er sie an Checkmk weitergibt.
|
||||
|
||||
## Konfiguration
|
||||
|
||||
Die Konfiguration liegt neben der EXE:
|
||||
Datei:
|
||||
|
||||
```text
|
||||
%ProgramData%\checkmk\agent\local\BizTalkCheckmkPulse\BizTalkCheckmkPulse.exe.config
|
||||
%ProgramFiles%\BizTalkCheckmkPulse\BizTalkCheckmkPulse.exe.config
|
||||
```
|
||||
|
||||
Wichtige Werte:
|
||||
|
||||
| Key | Default | Bedeutung |
|
||||
| --- | --- | --- |
|
||||
| `Server` | `.` | Lokaler BizTalk-Server. Remote-WMI ist moeglich, aber nicht empfohlen. |
|
||||
| `ServicePrefix` | `BizTalk` | Prefix fuer alle Checkmk-Services. |
|
||||
| `EnvironmentName` | leer | Optional `ACC`, `DEV`, `TST` oder `PRD`. |
|
||||
| `QueryTimeoutSeconds` | `25` | WMI-Timeout pro Query. |
|
||||
| `ProbeSqlConnectivity` | `true` | Prueft die integrierte Windows-Anmeldung an der per WMI ermittelten Management- und Master-MessageBox-Datenbank. |
|
||||
| `SqlConnectionTimeoutSeconds` | `5` | Timeout je SQL-Ziel fuer Verbindungsaufbau und harmlose Testabfrage. |
|
||||
| `WarnResumableThreshold` | `1` | WARN ab n resumable suspended instances. |
|
||||
| `CritNonResumableThreshold` | `1` | CRIT ab n non-resumable suspended instances. |
|
||||
| `AlertOnArtifactRuntimeIssues` | `false` | Wenn `true`, werden deaktivierte Receive Locations und inaktive Ports/Orchestrations als WARN gewertet. |
|
||||
| `EmitPerApplicationSuspensionServices` | `false` | Erzeugt zusaetzliche Services pro Anwendung mit Suspensions. |
|
||||
| `ProbeEventLog` | `true` | Liest das Windows Application Log nach BizTalk-bezogenen Sources. |
|
||||
| `EventLogLookbackMinutes` | `60` | Zeitraum fuer Event-Log-Auswertung. |
|
||||
| `EventLogWarnThreshold` | `1` | WARN ab n Errors oder Warnings. |
|
||||
| `EventLogCritThreshold` | `10` | CRIT ab n Errors. |
|
||||
| `EnvironmentName` | leer | Optional `ACC`, `DEV`, `TST`, `PRD`. |
|
||||
| `SnapshotPath` | `%ProgramData%\BizTalkCheckmkPulse\data\...` | Gemeinsame Provider-/Consumer-Datei. |
|
||||
| `SnapshotMaxAgeSeconds` | `180` | Ab diesem Alter liefert der Consumer `UNKNOWN`. |
|
||||
| `SnapshotMaxBytes` | `1048576` | Harte Eingabegroesse fuer den Consumer. |
|
||||
| `LogDirectory` | `%ProgramData%\BizTalkCheckmkPulse\logs` | Tageslogs. |
|
||||
| `LogRetentionDays` | `30` | Provider bereinigt aeltere Logs. |
|
||||
| `QueryTimeoutSeconds` | `25` | WMI-Timeout je Query. |
|
||||
| `SqlConnectionTimeoutSeconds` | `5` | SQL-Timeout je Ziel. |
|
||||
| `WarnResumableThreshold` | `1` | WARN ab n resumable Suspensions. |
|
||||
| `CritNonResumableThreshold` | `1` | CRIT ab n non-resumable Suspensions. |
|
||||
| `AlertOnArtifactRuntimeIssues` | `false` | WARN fuer bewusst inaktive Artefakte aktivieren. |
|
||||
| `EmitPerApplicationSuspensionServices` | `false` | Zusaetzliche Anwendungsservices. |
|
||||
| `EventLogLookbackMinutes` | `60` | Event-Log-Zeitfenster des Providers. |
|
||||
|
||||
Empfehlung:
|
||||
Nach einer Config-Aenderung den Scheduled Task manuell starten. Der Consumer
|
||||
liest den naechsten atomar publizierten Snapshot.
|
||||
|
||||
- In `DEV`, `TST`, `ACC`: `AlertOnArtifactRuntimeIssues=false`, damit bewusst gestoppte Artefakte nicht rauschen.
|
||||
- In `PRD`: erst zwei Wochen beobachten; danach nur aktivieren, wenn die Runtime-Artefakte wirklich als Betriebsstandard immer aktiv sein muessen.
|
||||
- `EmitPerApplicationSuspensionServices=true` nur verwenden, wenn Anwendungsteams eigene Services benoetigen. Sonst bleibt die Discovery schlanker.
|
||||
## Fehlerbilder
|
||||
|
||||
## Integration in Checkmk Managed Services Edition 2.4
|
||||
| Beobachtung | Ursache / Massnahme |
|
||||
| --- | --- |
|
||||
| Alle sechs Services melden fehlenden Snapshot | Task, Provider-Log, Task-Konto und ACL pruefen. |
|
||||
| Snapshot ist `stale` | `LastTaskResult`, Laufzeit, WMI-/SQL-Timeout und Log pruefen. |
|
||||
| SHA-256 oder Format ungueltig | Datei nicht manuell bearbeiten; Datentraeger/AV und Schreibpfad pruefen, Task neu starten. |
|
||||
| Provider meldet `Login failed` | Provider-Konto und exakt konfigurierte Read-Only-Gruppe sowie `BTS_READONLY_USERS` pruefen. |
|
||||
| `Wmi/Schema` | Klasse/Properties gegen BizTalk-2020-Schema pruefen; keine Rechte ausweiten. |
|
||||
| Nur Event Log `UNKNOWN` | lokalen Application-Log-Zugriff des Provider-Kontos pruefen. |
|
||||
| Task-Result `2` | Parallelstart oder Snapshot-I/O; Log und Lock/ACL pruefen. |
|
||||
|
||||
Aufgaben der Checkmk-Kollegen:
|
||||
Ausfuehrliche Betriebs- und Architekturdetails stehen in
|
||||
[Dokumentation.md](Dokumentation.md).
|
||||
|
||||
1. BizTalk-Hosts in Checkmk anlegen oder bestehende Hosts pruefen.
|
||||
2. Sicherstellen, dass der Checkmk Windows Agent installiert, registriert und erreichbar ist.
|
||||
3. Plugin-Dateien auf die BizTalk-Server verteilen, manuell oder per Agent Bakery.
|
||||
4. Optional `EnvironmentName` je Umgebung setzen, z.B. `ACC`, `DEV`, `TST` oder `PRD`.
|
||||
5. Agent-Ausgabe mit `cmk-agent-ctl.exe dump` pruefen.
|
||||
6. Service Discovery fuer jeden BizTalk-Host ausfuehren.
|
||||
7. Gefundene `BizTalk ...` Services aufnehmen und Changes aktivieren.
|
||||
8. Views, Dashboards, Servicegruppen und Benachrichtigungen fuer die BizTalk-Services konfigurieren.
|
||||
|
||||
Manuelle Integration:
|
||||
|
||||
1. Dateien auf dem BizTalk-Server nach `%ProgramData%\checkmk\agent\local` kopieren.
|
||||
2. Optional `BizTalkCheckmkPulse.exe.config` je Umgebung anpassen.
|
||||
3. Agent-Dump pruefen.
|
||||
4. Service Discovery auf dem BizTalk-Host ausfuehren.
|
||||
5. Services in ein BizTalk-Dashboard aufnehmen.
|
||||
|
||||
Integration ueber Agent Bakery:
|
||||
|
||||
1. Deployment-Dateien in der Checkmk-Site als Custom-Agent-Dateien bereitstellen.
|
||||
2. Windows-Agent-Regel fuer die BizTalk-Hosts erstellen.
|
||||
3. Agent backen und auf ACC/DEV/TST/PRD-BizTalk-Hosts ausrollen.
|
||||
4. Discovery und Dashboard-Aufnahme durchfuehren.
|
||||
|
||||
Die Checkmk-Dokumentation nennt fuer Windows Local Checks `%ProgramData%\checkmk\agent\local` und fuer Windows Agent Plugins `%ProgramData%\checkmk\agent\plugins`. Dieses Projekt nutzt bewusst `local`, weil der Zustand direkt vom Host berechnet und sofort als Checkmk-Service geliefert wird.
|
||||
|
||||
## Berechtigungen fuer WMI und BizTalk-Datenbanken
|
||||
|
||||
### Ausfuehrungskontext
|
||||
|
||||
Der Checkmk Windows Agent und der Agent Controller laufen standardmaessig als `LocalSystem` (`NT AUTHORITY\SYSTEM`). Der `.cmd`-Wrapper und `BizTalkCheckmkPulse.exe` erben diesen Sicherheitskontext.
|
||||
|
||||
Das Programm:
|
||||
|
||||
- verbindet sich lokal mit `\\<eigener-server>\root\MicrosoftBizTalkServer`
|
||||
- setzt keine separaten WMI-Anmeldedaten
|
||||
- verwendet kein Remote-WMI
|
||||
- ruft keine veraendernden BizTalk-WMI-Methoden auf
|
||||
- liest zusaetzlich nur das lokale Windows Application Event Log
|
||||
|
||||
Fuer den lokalen WMI-Verbindungsaufbau und das lokale Application Event Log reichen die Rechte von `LocalSystem` normalerweise aus. Es muessen deshalb im Regelfall keine DCOM-, Firewall- oder WMI-Namespace-Freigaben eingerichtet werden.
|
||||
|
||||
### Zugriff auf einen separaten SQL Server
|
||||
|
||||
Einige BizTalk-WMI-Klassen beziehen ihre Daten aus der BizTalk Management- oder MessageBox-Datenbank. Wenn der SQL Server auf einer anderen Maschine laeuft, greift ein unter `LocalSystem` ausgefuehrter Prozess im Netzwerk mit dem Computerkonto des BizTalk-Servers zu:
|
||||
|
||||
```text
|
||||
DOMAIN\BIZTALKSERVER$
|
||||
```
|
||||
|
||||
Die weitreichenden lokalen Rechte von `LocalSystem` ergeben nicht automatisch Berechtigungen auf dem entfernten SQL Server. Deshalb kann die Verbindung zum lokalen WMI-Namespace funktionieren, waehrend einzelne SQL-gestuetzte BizTalk-WMI-Abfragen mit `Access denied`, `UnauthorizedAccessException` oder `UNKNOWN` fehlschlagen.
|
||||
|
||||
### Einordnung des ACC-Befunds vom 29.07.2026
|
||||
|
||||
Der Agent-Dump von `AV23AGPWBIO1` zeigt den entscheidenden inneren Providerfehler:
|
||||
|
||||
```text
|
||||
COMException HRESULT=0x80131904
|
||||
Internal error from OLEDB provider: 'Login failed for user 'BEW\AV23AGPWBIO1$'.'
|
||||
```
|
||||
|
||||
Damit ist der lokale Namespace `root\MicrosoftBizTalkServer` bereits erreichbar. Der BizTalk-WMI-Provider kann aber seine SQL-gestuetzten Abfragen nicht ausfuehren, weil SQL Server das Computerkonto `BEW\AV23AGPWBIO1$` ablehnt. Zusaetzliche DCOM-, Firewall- oder WMI-Namespace-Rechte sind fuer diesen konkreten Fehler nicht die richtige Massnahme. Die `UNKNOWN`-Zustaende bei Platform, Host Instances, Runtime Artifacts und Suspended Instances sowie `targets=0` bei SQL Access sind Folgewirkungen derselben fehlenden Berechtigung.
|
||||
|
||||
`BizTalk Event Log` funktioniert unabhaengig von diesem SQL-Zugriff. Die dort sichtbaren zwei Fehler und sechs Warnungen sind echte Ereignisse im betrachteten Zeitfenster und nach Behebung der Berechtigung separat zu untersuchen.
|
||||
|
||||
### Codekorrektur und Berechtigungsfreigabe sind zwei getrennte Schritte
|
||||
|
||||
Die ueberarbeitete Version vergibt keine Windows-, Active-Directory- oder SQL-Rechte. Sie erkennt den eingebetteten SQL-Loginfehler nur korrekt, nennt das abgewiesene Konto und verhindert eine irrefuehrende Provider-/Konfigurationsdiagnose. Ohne Berechtigungsfreigabe bleibt der Check deshalb auch mit der neuen EXE `UNKNOWN`.
|
||||
|
||||
Der Zugriff funktioniert erst, wenn `BEW\AV23AGPWBIO1$` Mitglied der exakt konfigurierten BizTalk-Operator-Gruppe ist und das erneuerte Maschinen-Token diese Mitgliedschaft enthaelt. Normalerweise ist diese Windows-Gruppe bereits durch die BizTalk-Konfiguration als SQL-Login beziehungsweise Datenbankbenutzer mit `BTS_OPERATORS` in den BizTalk-Datenbanken eingerichtet. Das Maschinenkonto erbt diese SQL-Rechte ueber seine Gruppenmitgliedschaft; ein eigener SQL-Login fuer `BEW\AV23AGPWBIO1$` ist dann weder erforderlich noch gewuenscht.
|
||||
|
||||
### Test im echten Checkmk-Kontext
|
||||
|
||||
Ein manueller Aufruf von `BizTalkCheckmkPulse.exe` oder des Wrappers verwendet das Konto der angemeldeten Person. Ein erfolgreicher manueller Test beweist daher nicht, dass die Ausfuehrung durch Checkmk als `LocalSystem` ebenfalls funktioniert.
|
||||
|
||||
Der verbindliche Test erfolgt ueber den Agent Controller:
|
||||
## Deinstallation
|
||||
|
||||
```powershell
|
||||
& "C:\Program Files (x86)\checkmk\service\cmk-agent-ctl.exe" dump |
|
||||
Select-String -Pattern "BizTalk|Login failed|Access denied|Unauthorized|UNKNOWN" -Context 0,1
|
||||
.\Uninstall-BizTalkCheckmkPulse.ps1
|
||||
```
|
||||
|
||||
Erwartet werden die sechs stabilen `BizTalk ...` Services mit plausiblen Daten. Insbesondere `BizTalk Platform` und `BizTalk SQL Access` duerfen nicht wegen eines WMI-, SQL- oder Berechtigungsfehlers `UNKNOWN` sein.
|
||||
Das Skript fragt vor dem Entfernen. Mit `-KeepRuntimeData` bleiben Snapshot und
|
||||
Logs erhalten:
|
||||
|
||||
Das Programm prueft Berechtigungen aktiv:
|
||||
|
||||
- Der WMI-Namespace-Verbindungsaufbau und jede erforderliche WMI-Klasse werden getrennt bewertet.
|
||||
- Fehler werden als `Permission`, `Connectivity`, `Timeout`, `Configuration`, `Schema` oder `Provider` klassifiziert.
|
||||
- `BizTalk SQL Access` oeffnet mit integrierter Windows-Authentifizierung eine Verbindung zur ermittelten Management- und Master-MessageBox-Datenbank und fuehrt `SELECT 1` aus.
|
||||
- Die Service-Ausgabe nennt Ausfuehrungsidentitaet, erwartete Netzwerkidentitaet, betroffene Komponente, technische Ursache und konkrete Massnahme.
|
||||
- Ein im BizTalk-WMI-Provider eingebettetes `Login failed for user` wird als `Wmi/Permission` klassifiziert; das tatsaechlich abgewiesene Konto wird in die Diagnose und in `BizTalk SQL Access` uebernommen.
|
||||
- Nach erfolgreicher Plattformabfrage zeigt `BizTalk Platform` mit `operator_group=` die von BizTalk konfigurierte Operator-Gruppe.
|
||||
- Fehlgeschlagene Pflichtabfragen werden nie als leerer, erfolgreicher Datenbestand gewertet. Der betroffene Service wird `UNKNOWN`.
|
||||
|
||||
`Sql/Configuration` weist je nach Detailtext entweder auf eine unvollstaendige WMI-Zielermittlung oder auf TLS-, Zertifikats-, SPN-/SSPI-Probleme hin. Die Diagnose empfiehlt bewusst nicht, SQL-Verschluesselung pauschal abzuschalten.
|
||||
|
||||
`Wmi/Schema` kennzeichnet `InvalidClass` oder `InvalidQuery`. Das ist kein Berechtigungsfehler: Klassen- und Property-Namen muessen gegen das installierte BizTalk-WMI-Schema geprueft werden; eine Rechteerhoehung kann eine nicht vorhandene Klasse nicht erzeugen. Die technischen Details enthalten die ausgefuehrte WQL-Abfrage und deren Laufzeit bis zum Fehler.
|
||||
|
||||
Der SQL-Zugriffstest ersetzt nicht das Checkmk-MSSQL-Plugin auf dem SQL-Server. Er prueft ausschliesslich, ob genau die Identitaet des BizTalk Local Checks die fuer seine Diagnose benoetigten BizTalk-Datenbankziele erreichen kann.
|
||||
|
||||
Da bei fehlenden Rechten jeder Lauf einen abgewiesenen SQL-Login erzeugen kann, wird fuer den produktiven Betrieb eine asynchrone Ausfuehrung mit 300 Sekunden Cache empfohlen. Das begrenzt SQL-Logeintraege und Last, verzoegert einen Zustandswechsel aber um maximal fuenf Minuten. Beispiel in `%ProgramData%\checkmk\agent\check_mk.user.yml`:
|
||||
|
||||
```yaml
|
||||
local:
|
||||
enabled: yes
|
||||
execution:
|
||||
- pattern: $CUSTOM_LOCAL_PATH$\biztalk_checkmk_pulse.cmd
|
||||
async: yes
|
||||
run: yes
|
||||
cache_age: 300
|
||||
```powershell
|
||||
.\Uninstall-BizTalkCheckmkPulse.ps1 -KeepRuntimeData
|
||||
```
|
||||
|
||||
### Vorgehen bei Berechtigungsfehlern
|
||||
Die AD-Gruppenmitgliedschaft des Provider-Kontos wird bewusst nicht automatisch
|
||||
geaendert und muss separat durch AD-/BizTalk-Administration entfernt werden.
|
||||
|
||||
1. Den Agent-Dienst und sein Startkonto kontrollieren:
|
||||
## Certutil-Transport
|
||||
|
||||
```powershell
|
||||
Get-CimInstance Win32_Service |
|
||||
Where-Object { $_.Name -match 'check|cmk' -or $_.DisplayName -match 'checkmk' } |
|
||||
Select-Object Name, DisplayName, State, StartName
|
||||
```
|
||||
Zu jeder Uebergabe wird ein Source-ZIP und eine certutil-kompatible
|
||||
Base64-Textdatei erzeugt. Auf Windows:
|
||||
|
||||
2. Die exakt konfigurierte Operator-Gruppe in der BizTalk Administration Console unter `BizTalk Group` > `Properties` > `General` > `BizTalk Operators Group` ablesen. Nicht blind vom Standardnamen ausgehen. Alternativ kann ein bereits berechtigtes Konto abfragen:
|
||||
```cmd
|
||||
certutil -decode biztalk-checkmk-pulse-source-<datum>-<commit>.zip.b64.txt biztalk-checkmk-pulse-source.zip
|
||||
certutil -hashfile biztalk-checkmk-pulse-source.zip SHA256
|
||||
tar -xf biztalk-checkmk-pulse-source.zip
|
||||
```
|
||||
|
||||
```powershell
|
||||
Get-CimInstance -Namespace root/MicrosoftBizTalkServer -ClassName MSBTS_GroupSetting |
|
||||
Select-Object Name, BizTalkOperatorGroup, MgmtDbServerName, MgmtDbName
|
||||
```
|
||||
|
||||
3. Ein AD-Administrator nimmt das Computerobjekt in genau diese Gruppe auf. Fuer ACC ist das Computerobjekt `AV23AGPWBIO1`, dessen Netzwerkprincipal `BEW\AV23AGPWBIO1$` ist:
|
||||
|
||||
```powershell
|
||||
Import-Module ActiveDirectory
|
||||
$computer = Get-ADComputer -Identity 'AV23AGPWBIO1'
|
||||
Add-ADGroupMember -Identity '<EXAKTE_BIZTALK_OPERATOR_GRUPPE>' -Members $computer -WhatIf
|
||||
```
|
||||
|
||||
Nach Kontrolle der aufgeloesten Ziele wird derselbe Befehl ohne `-WhatIf` ausgefuehrt. Anschliessend die Mitgliedschaft mit `Get-ADPrincipalGroupMembership -Identity 'AV23AGPWBIO1'` pruefen.
|
||||
|
||||
4. AD-Replikation abwarten. Die sicherste Aktivierung ist ein Neustart des BizTalk-Servers im Wartungsfenster. Ohne Neustart kann ein Administrator die Maschinen-Tickets mit `klist purge -li 0x3e7` verwerfen und danach den zuvor ermittelten Checkmk-Agent-Dienst neu starten.
|
||||
|
||||
5. Den Test im echten Agent-Kontext wiederholen:
|
||||
|
||||
```powershell
|
||||
& "C:\Program Files (x86)\checkmk\service\cmk-agent-ctl.exe" dump |
|
||||
Select-String -Pattern "BizTalk|Login failed|Wmi/Permission|Sql/Permission|UNKNOWN" -Context 0,1
|
||||
```
|
||||
|
||||
6. Erwartet werden Platform und SQL Access mit vollstaendiger Zielermittlung, zwei erreichbaren Datenbankzielen und keine berechtigungsbedingten `UNKNOWN`-Services. Erst wenn eine konkret benannte Klasse danach weiter scheitert, wird deren Rollenanforderung mit BizTalk- und SQL-Administration untersucht.
|
||||
|
||||
Die Operator-Rolle ist fuer grundlegende Administration und Monitoring vorgesehen und kann Zustands- und Message-Flow-Informationen lesen, ohne BizTalk-Konfiguration oder Nachrichteninhalte einzusehen. Das Plugin fuehrt ausschliesslich Leseabfragen aus. Direkte manuelle Aenderungen an BizTalk-SQL-Datenbankrollen sollten nicht vorgenommen werden; die von BizTalk konfigurierte Windows-Gruppe ist die vorgesehene Berechtigungsgrenze.
|
||||
|
||||
Bleibt `Login failed` nach bestaetigter Gruppenmitgliedschaft, AD-Replikation und Token-Erneuerung bestehen, pruefen BizTalk- und SQL-Administration die Gruppenabbildung auf dem SQL Server. Die konfigurierte Domain-Gruppe muss als Windows-Gruppenlogin aufloesbar sein und ihre Datenbankbenutzer muessen den von BizTalk vorgesehenen Rollen angehoeren, insbesondere `BTS_OPERATORS` in `BizTalkMgmtDb` und `BizTalkMsgBoxDb`. Eine fehlende oder abweichende Abbildung wird mit der BizTalk-Konfiguration abgeglichen und fuer die Gruppe repariert, nicht durch einen ad-hoc Einzel-Login fuer das Maschinenkonto umgangen.
|
||||
|
||||
Entscheidungsmatrix:
|
||||
|
||||
| Ergebnis im Agent-Dump | Massnahme |
|
||||
| --- | --- |
|
||||
| Alle BizTalk-Services liefern plausible Daten | Keine Berechtigungsaenderung erforderlich. |
|
||||
| Verbindung zu `root\MicrosoftBizTalkServer` scheitert | Namespace, BizTalk-WMI-Provider, WMI-Dienst und Namespace-ACL gezielt pruefen. Keine pauschalen WMI-Rechte vergeben. |
|
||||
| Service meldet `Wmi/Schema` beziehungsweise `InvalidClass`/`InvalidQuery` | WMI-Klasse und Properties gegen das BizTalk-Schema pruefen. Keine Berechtigungen erweitern. |
|
||||
| `BizTalk SQL Access` meldet `Sql/Permission` | Computerkonto `DOMAIN\BIZTALKSERVER$` in die konfigurierte BizTalk-Operator-Gruppe aufnehmen, Kerberos erneuern und erneut testen. Keine direkten BizTalk-DB-Rollen vergeben. |
|
||||
| `BizTalk SQL Access` meldet `Sql/Connectivity` oder `Sql/Timeout` | Server-/Instanzname, DNS, SQL-Dienst, TCP-Protokoll, Port und Firewall aus Sicht des BizTalk-Servers pruefen. |
|
||||
| SQL-gestuetzte BizTalk-Klassen liefern `0x80131904`, `Login failed for user` oder `UNKNOWN` | Das in der Meldung genannte Computerkonto in die exakt konfigurierte BizTalk-Operator-Gruppe aufnehmen. |
|
||||
| `BizTalk Event Log` ist `UNKNOWN` | Zugriff auf das lokale Application Log pruefen; `LocalSystem` kann es normalerweise lesen. |
|
||||
| Fehler bleibt trotz Operator-Rolle bestehen | Betroffene WMI-Klasse und BizTalk-/SQL-Rollenzuordnung mit den Fachadministratoren untersuchen. |
|
||||
|
||||
### Sicherheitsabwaegung und Alternative
|
||||
|
||||
Wird das Computerkonto in `BizTalk Server Operators` aufgenommen, erhalten alle Dienste, die auf diesem BizTalk-Server als `LocalSystem` laufen, diese Netzwerkberechtigung. Das ist vor dem Rollout mit der Security- und BizTalk-Administration abzustimmen.
|
||||
|
||||
Falls diese Freigabe nicht zulaessig ist, ist die sauberere Alternative ein separater Collector unter einem dedizierten gMSA- oder Dienstkonto mit Operator-Rechten. Dieser Collector kann seine Checkmk-Ausgabe in eine Spool-Datei schreiben. Der Checkmk-Agent liest dann nur die bereits erzeugten Daten ein. Den gesamten Checkmk-Agent-Dienst sollte man nicht allein fuer dieses Plugin von `LocalSystem` auf ein anderes Konto umstellen, weil dadurch alle Agent-Sektionen und Local Checks betroffen sind.
|
||||
|
||||
Empfohlene Alarmierung:
|
||||
|
||||
- `BizTalk Platform`: `UNKNOWN` immer untersuchen, da dann WMI, Rechte oder Deployment betroffen sind.
|
||||
- `BizTalk SQL Access`: `UNKNOWN` als Integrations-, Berechtigungs- oder Verbindungsproblem behandeln und die eingebettete Massnahme abarbeiten.
|
||||
- `BizTalk Suspended Instances`: in `PRD` direkt alarmieren; in Nicht-PRD nach Betriebsbedarf.
|
||||
- `BizTalk Host Instances`: `CRIT` alarmieren, weil gestoppte Host Instances Laufzeitverarbeitung verhindern koennen.
|
||||
- `BizTalk Runtime Artifacts`: zunaechst beobachten; strengere Alarmierung erst aktivieren, wenn deaktivierte Artefakte nicht fachlich gewollt sind.
|
||||
- `BizTalk Event Log`: Schwellwerte nach Beobachtungsphase anpassen.
|
||||
Die konkrete Datei und SHA-256-Summe werden bei der Uebergabe genannt.
|
||||
|
||||
## Quellen
|
||||
|
||||
- Checkmk Local Checks: https://docs.checkmk.com/latest/en/localchecks.html
|
||||
- Checkmk Windows Agent und Plugin-Pfade: https://docs.checkmk.com/latest/en/agent_windows.html
|
||||
- Checkmk Agent-Based Plugin-Entwicklung: https://docs.checkmk.com/latest/en/devel_check_plugins.html
|
||||
- Checkmk Bakery API: https://docs.checkmk.com/latest/en/bakery_api.html
|
||||
- Checkmk MKP-Pakete: https://docs.checkmk.com/latest/en/mkps.html
|
||||
- Microsoft BizTalk WMI `MSBTS_ServiceInstance`: https://learn.microsoft.com/en-us/biztalk/core/technical-reference/msbts-serviceinstance-wmi
|
||||
- Microsoft BizTalk WMI `ServiceStatus`: https://learn.microsoft.com/en-us/biztalk/core/technical-reference/msbts-serviceinstance-servicestatus-property-wmi
|
||||
- Microsoft BizTalk WMI `MSBTS_GroupSetting`: https://learn.microsoft.com/en-us/biztalk/core/technical-reference/msbts-groupsetting-wmi
|
||||
- Microsoft BizTalk Windows-Gruppen und SQL-Rollen: https://learn.microsoft.com/en-us/biztalk/core/windows-groups-and-user-accounts-in-biztalk-server
|
||||
- Microsoft BizTalk administrative Rollen: https://learn.microsoft.com/en-us/biztalk/core/access-control-for-administrative-roles
|
||||
- Microsoft BizTalk Gruppen-Eigenschaften: https://learn.microsoft.com/en-us/biztalk/core/how-to-modify-group-properties
|
||||
- Microsoft BizTalk WMI Core Server Classes: https://learn.microsoft.com/en-us/biztalk/core/technical-reference/core-server-classes
|
||||
- Microsoft BizTalk Mindestberechtigungen: https://learn.microsoft.com/en-us/biztalk/core/minimum-security-user-rights
|
||||
- Microsoft BizTalk Access Control: https://learn.microsoft.com/en-us/biztalk/core/access-control-and-data-security
|
||||
- Microsoft LocalSystem und Computerkonten: https://learn.microsoft.com/en-us/entra/architecture/service-accounts-computer
|
||||
- Microsoft `Add-ADGroupMember`: https://learn.microsoft.com/en-us/powershell/module/activedirectory/add-adgroupmember
|
||||
- Microsoft `klist`: https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/klist
|
||||
- Microsoft WMI Namespace Security: https://learn.microsoft.com/en-us/windows/win32/wmisdk/access-to-wmi-namespaces
|
||||
- Microsoft: BizTalk `MSBTS_GroupSetting.BizTalkReadOnlyUserGroup`
|
||||
- Microsoft: Windows Groups and User Accounts in BizTalk Server
|
||||
- Microsoft: Managing BizTalk Server Security
|
||||
- Checkmk: Windows Agent und Local Checks
|
||||
|
||||
Die genauen Links stehen in [Dokumentation.md](Dokumentation.md).
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string]$CollectorAccount,
|
||||
|
||||
[switch]$Gmsa,
|
||||
|
||||
[ValidateSet("", "ACC", "DEV", "TST", "PRD")]
|
||||
[string]$EnvironmentName = "",
|
||||
|
||||
[string]$InstallDirectory = "$env:ProgramFiles\BizTalkCheckmkPulse",
|
||||
|
||||
[string]$CheckmkLocalDirectory = "$env:ProgramData\checkmk\agent\local"
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
Set-StrictMode -Version 2.0
|
||||
|
||||
$taskName = "BizTalk Checkmk Pulse Provider"
|
||||
$runtimeRoot = Join-Path $env:ProgramData "BizTalkCheckmkPulse"
|
||||
$dataDirectory = Join-Path $runtimeRoot "data"
|
||||
$logDirectory = Join-Path $runtimeRoot "logs"
|
||||
$sourceApplication = Join-Path $PSScriptRoot "application"
|
||||
$sourceWrapper = Join-Path $PSScriptRoot "biztalk_checkmk_pulse.cmd"
|
||||
$targetExe = Join-Path $InstallDirectory "BizTalkCheckmkPulse.exe"
|
||||
$targetConfig = "$targetExe.config"
|
||||
$targetWrapper = Join-Path $CheckmkLocalDirectory "biztalk_checkmk_pulse.cmd"
|
||||
|
||||
function Assert-Administrator {
|
||||
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
|
||||
$principal = New-Object Security.Principal.WindowsPrincipal($identity)
|
||||
if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
|
||||
throw "Die Installation muss in einer administrativen Windows PowerShell ausgefuehrt werden."
|
||||
}
|
||||
}
|
||||
|
||||
function Invoke-Icacls {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)][string]$Path,
|
||||
[Parameter(Mandatory = $true)][string[]]$Arguments
|
||||
)
|
||||
|
||||
& icacls.exe $Path @Arguments | Out-Host
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "icacls ist fuer '$Path' mit Exitcode $LASTEXITCODE fehlgeschlagen."
|
||||
}
|
||||
}
|
||||
|
||||
Assert-Administrator
|
||||
|
||||
if (-not (Test-Path -LiteralPath $sourceApplication -PathType Container)) {
|
||||
throw "Quellverzeichnis fehlt: $sourceApplication"
|
||||
}
|
||||
if (-not (Test-Path -LiteralPath $sourceWrapper -PathType Leaf)) {
|
||||
throw "Checkmk-Wrapper fehlt: $sourceWrapper"
|
||||
}
|
||||
|
||||
if ($Gmsa -and -not $CollectorAccount.EndsWith('$')) {
|
||||
throw "Ein gMSA-Kontoname muss mit '$' enden, z.B. DOMAIN\svc_biztalk_cmk$."
|
||||
}
|
||||
|
||||
Write-Host "Installiere Programmdateien nach $InstallDirectory ..."
|
||||
New-Item -ItemType Directory -Path $InstallDirectory -Force | Out-Null
|
||||
Copy-Item -Path (Join-Path $sourceApplication "*") -Destination $InstallDirectory -Force
|
||||
|
||||
[xml]$configuration = Get-Content -LiteralPath $targetConfig
|
||||
$environmentSetting = $configuration.configuration.appSettings.add |
|
||||
Where-Object { $_.key -eq "EnvironmentName" } |
|
||||
Select-Object -First 1
|
||||
if ($null -eq $environmentSetting) {
|
||||
throw "EnvironmentName fehlt in $targetConfig."
|
||||
}
|
||||
$environmentSetting.value = $EnvironmentName
|
||||
$configuration.Save($targetConfig)
|
||||
|
||||
Write-Host "Erzeuge Runtime-Verzeichnisse und Least-Privilege-ACLs ..."
|
||||
New-Item -ItemType Directory -Path $runtimeRoot, $dataDirectory, $logDirectory -Force | Out-Null
|
||||
|
||||
# Runtime-Root: Provider lesen, SYSTEM lesen, lokale Administratoren verwalten.
|
||||
Invoke-Icacls -Path $runtimeRoot -Arguments @(
|
||||
"/inheritance:r",
|
||||
"/grant:r",
|
||||
"*S-1-5-18:(OI)(CI)(RX)",
|
||||
"*S-1-5-32-544:(OI)(CI)(F)",
|
||||
"${CollectorAccount}:(OI)(CI)(RX)"
|
||||
)
|
||||
|
||||
# Daten: ausschliesslich Provider schreibt, LocalSystem liest den Snapshot.
|
||||
Invoke-Icacls -Path $dataDirectory -Arguments @(
|
||||
"/inheritance:r",
|
||||
"/grant:r",
|
||||
"*S-1-5-18:(OI)(CI)(RX)",
|
||||
"*S-1-5-32-544:(OI)(CI)(F)",
|
||||
"${CollectorAccount}:(OI)(CI)(M)"
|
||||
)
|
||||
|
||||
# Logs: Provider und LocalSystem-Consumer duerfen getrennte Diagnosezeilen anhaengen.
|
||||
Invoke-Icacls -Path $logDirectory -Arguments @(
|
||||
"/inheritance:r",
|
||||
"/grant:r",
|
||||
"*S-1-5-18:(OI)(CI)(M)",
|
||||
"*S-1-5-32-544:(OI)(CI)(F)",
|
||||
"${CollectorAccount}:(OI)(CI)(M)"
|
||||
)
|
||||
|
||||
Write-Host "Installiere Checkmk Local Check nach $CheckmkLocalDirectory ..."
|
||||
New-Item -ItemType Directory -Path $CheckmkLocalDirectory -Force | Out-Null
|
||||
Copy-Item -LiteralPath $sourceWrapper -Destination $targetWrapper -Force
|
||||
|
||||
Write-Host "Registriere Scheduled Task '$taskName' ..."
|
||||
$action = New-ScheduledTaskAction `
|
||||
-Execute $targetExe `
|
||||
-Argument "--collect" `
|
||||
-WorkingDirectory $InstallDirectory
|
||||
$trigger = New-ScheduledTaskTrigger `
|
||||
-Once `
|
||||
-At (Get-Date).AddMinutes(1) `
|
||||
-RepetitionInterval (New-TimeSpan -Minutes 1)
|
||||
$settings = New-ScheduledTaskSettingsSet `
|
||||
-MultipleInstances IgnoreNew `
|
||||
-ExecutionTimeLimit (New-TimeSpan -Minutes 5) `
|
||||
-StartWhenAvailable `
|
||||
-RestartCount 2 `
|
||||
-RestartInterval (New-TimeSpan -Minutes 1)
|
||||
|
||||
if ($Gmsa) {
|
||||
$principal = New-ScheduledTaskPrincipal `
|
||||
-UserId $CollectorAccount `
|
||||
-LogonType Password `
|
||||
-RunLevel Highest
|
||||
$task = New-ScheduledTask -Action $action -Trigger $trigger -Settings $settings -Principal $principal
|
||||
Register-ScheduledTask -TaskName $taskName -InputObject $task -Force | Out-Null
|
||||
}
|
||||
else {
|
||||
$credential = Get-Credential `
|
||||
-UserName $CollectorAccount `
|
||||
-Message "Kennwort fuer das dedizierte BizTalk-Monitoringkonto eingeben"
|
||||
if ($credential.UserName -ne $CollectorAccount) {
|
||||
throw "Das eingegebene Konto stimmt nicht mit CollectorAccount ueberein."
|
||||
}
|
||||
|
||||
$principal = New-ScheduledTaskPrincipal `
|
||||
-UserId $CollectorAccount `
|
||||
-LogonType Password `
|
||||
-RunLevel Highest
|
||||
$task = New-ScheduledTask -Action $action -Trigger $trigger -Settings $settings -Principal $principal
|
||||
Register-ScheduledTask `
|
||||
-TaskName $taskName `
|
||||
-InputObject $task `
|
||||
-User $CollectorAccount `
|
||||
-Password $credential.GetNetworkCredential().Password `
|
||||
-Force | Out-Null
|
||||
}
|
||||
|
||||
Write-Host "Fuehre formatseitigen Self-Test aus ..."
|
||||
$selfTest = & $targetExe --self-test
|
||||
if ($LASTEXITCODE -ne 0 -or @($selfTest).Count -ne 6) {
|
||||
throw "Self-Test fehlgeschlagen. Erwartet wurden sechs Checkmk-Zeilen."
|
||||
}
|
||||
|
||||
Write-Host "Starte den Provider einmalig ..."
|
||||
Start-ScheduledTask -TaskName $taskName
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "Installation abgeschlossen."
|
||||
Write-Host "Naechste Pruefungen:"
|
||||
Write-Host " Get-ScheduledTaskInfo -TaskName '$taskName'"
|
||||
Write-Host " Get-Content '$logDirectory\biztalk-checkmk-pulse-*.log' -Tail 50"
|
||||
Write-Host " & '$targetExe' --consume"
|
||||
Write-Host " & 'C:\Program Files (x86)\checkmk\service\cmk-agent-ctl.exe' dump"
|
||||
@@ -0,0 +1,30 @@
|
||||
[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High")]
|
||||
param(
|
||||
[string]$InstallDirectory = "$env:ProgramFiles\BizTalkCheckmkPulse",
|
||||
[string]$CheckmkLocalDirectory = "$env:ProgramData\checkmk\agent\local",
|
||||
[switch]$KeepRuntimeData
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$taskName = "BizTalk Checkmk Pulse Provider"
|
||||
$runtimeRoot = Join-Path $env:ProgramData "BizTalkCheckmkPulse"
|
||||
$wrapper = Join-Path $CheckmkLocalDirectory "biztalk_checkmk_pulse.cmd"
|
||||
|
||||
if ($PSCmdlet.ShouldProcess($taskName, "Scheduled Task entfernen")) {
|
||||
Unregister-ScheduledTask -TaskName $taskName -Confirm:$false -ErrorAction SilentlyContinue
|
||||
}
|
||||
if (Test-Path -LiteralPath $wrapper) {
|
||||
if ($PSCmdlet.ShouldProcess($wrapper, "Checkmk Local Check entfernen")) {
|
||||
Remove-Item -LiteralPath $wrapper -Force
|
||||
}
|
||||
}
|
||||
if (Test-Path -LiteralPath $InstallDirectory) {
|
||||
if ($PSCmdlet.ShouldProcess($InstallDirectory, "Programmdateien entfernen")) {
|
||||
Remove-Item -LiteralPath $InstallDirectory -Recurse -Force
|
||||
}
|
||||
}
|
||||
if (-not $KeepRuntimeData -and (Test-Path -LiteralPath $runtimeRoot)) {
|
||||
if ($PSCmdlet.ShouldProcess($runtimeRoot, "Snapshot und Logs entfernen")) {
|
||||
Remove-Item -LiteralPath $runtimeRoot -Recurse -Force
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
@echo off
|
||||
setlocal
|
||||
|
||||
set "BASE=%~dp0BizTalkCheckmkPulse"
|
||||
set "BASE=%ProgramFiles%\BizTalkCheckmkPulse"
|
||||
set "EXE=%BASE%\BizTalkCheckmkPulse.exe"
|
||||
|
||||
if not exist "%EXE%" (
|
||||
echo 3 "BizTalk Platform" - BizTalkCheckmkPulse.exe fehlt unter %BASE%. Massnahme: Deployment-Paket und Dateirechte pruefen und danach cmk-agent-ctl.exe dump erneut ausfuehren.
|
||||
echo 3 "BizTalk Platform" - BizTalkCheckmkPulse.exe fehlt unter %BASE%. Massnahme: Install-BizTalkCheckmkPulse.ps1 erneut als Administrator ausfuehren und danach cmk-agent-ctl.exe dump erneut ausfuehren.
|
||||
echo 3 "BizTalk SQL Access" - BizTalkCheckmkPulse.exe fehlt unter %BASE%. Massnahme: Deployment-Paket und Dateirechte pruefen.
|
||||
echo 3 "BizTalk Suspended Instances" - BizTalkCheckmkPulse.exe fehlt unter %BASE%. Massnahme: Deployment-Paket und Dateirechte pruefen.
|
||||
echo 3 "BizTalk Host Instances" - BizTalkCheckmkPulse.exe fehlt unter %BASE%. Massnahme: Deployment-Paket und Dateirechte pruefen.
|
||||
@@ -14,10 +14,10 @@ if not exist "%EXE%" (
|
||||
exit /b 0
|
||||
)
|
||||
|
||||
"%EXE%" %*
|
||||
"%EXE%" --consume %*
|
||||
set "EXITCODE=%ERRORLEVEL%"
|
||||
if not "%EXITCODE%"=="0" (
|
||||
echo 3 "BizTalk Platform" - BizTalkCheckmkPulse.exe konnte nicht erfolgreich gestartet werden, Exitcode=%EXITCODE%. Massnahme: .NET Framework 4.7.2, EXE/Config-Dateien und Windows Application Log pruefen.
|
||||
echo 3 "BizTalk Platform" - Snapshot-Consumer konnte nicht erfolgreich gestartet werden, Exitcode=%EXITCODE%. Massnahme: .NET Framework 4.7.2, Installation und Provider-Log pruefen.
|
||||
echo 3 "BizTalk SQL Access" - BizTalkCheckmkPulse.exe endete mit Exitcode=%EXITCODE%. Massnahme: .NET Framework 4.7.2, EXE/Config-Dateien und Windows Application Log pruefen.
|
||||
echo 3 "BizTalk Suspended Instances" - BizTalkCheckmkPulse.exe endete mit Exitcode=%EXITCODE%. Massnahme: Programmstart und Windows Application Log pruefen.
|
||||
echo 3 "BizTalk Host Instances" - BizTalkCheckmkPulse.exe endete mit Exitcode=%EXITCODE%. Massnahme: Programmstart und Windows Application Log pruefen.
|
||||
|
||||
@@ -1,152 +1,115 @@
|
||||
BizTalk Checkmk Pulse - ACC Berechtigungsfreigabe
|
||||
Stand: 29.07.2026
|
||||
BizTalk Checkmk Pulse - ACC Berechtigung und Architektur
|
||||
Stand: 30.07.2026
|
||||
|
||||
BEFUND
|
||||
======
|
||||
|
||||
Host: AV23AGPWBIO1
|
||||
Lokale Identitaet: NT AUTHORITY\SYSTEM (LocalSystem)
|
||||
Netzwerkidentitaet: BEW\AV23AGPWBIO1$
|
||||
Checkmk-Identitaet: NT AUTHORITY\SYSTEM
|
||||
Alte Netzwerkidentitaet: BEW\AV23AGPWBIO1$
|
||||
Fehler: COMException HRESULT=0x80131904
|
||||
Innerer Fehler: Login failed for user 'BEW\AV23AGPWBIO1$'
|
||||
|
||||
Der lokale BizTalk-WMI-Namespace ist erreichbar. Der BizTalk-WMI-Provider wird
|
||||
aber bei seinem SQL-Zugriff als Maschinenkonto abgewiesen. Deshalb sind
|
||||
Platform, Host Instances, Runtime Artifacts und Suspended Instances UNKNOWN.
|
||||
BizTalk SQL Access kann ohne MSBTS_GroupSetting keine Ziele ermitteln.
|
||||
Der lokale BizTalk-WMI-Namespace war erreichbar. Datenbankgestuetzte
|
||||
BizTalk-WMI-Abfragen scheiterten am SQL-Login des Maschinenkontos.
|
||||
|
||||
Keine DCOM-, Firewall- oder WMI-Namespace-Rechte erweitern. Keinen direkten
|
||||
SQL-Login, keine manuellen BizTalk-Datenbankrollen und kein sysadmin fuer das
|
||||
Maschinenkonto anlegen.
|
||||
NEUE VERBINDLICHE ARCHITEKTUR
|
||||
=============================
|
||||
|
||||
WICHTIG ZUR NEUEN VERSION
|
||||
=========================
|
||||
Das Maschinenkonto erhaelt keine BizTalk-Berechtigung.
|
||||
|
||||
Die neue EXE repariert keine Berechtigungen. Sie erkennt den eingebetteten
|
||||
SQL-Loginfehler korrekt, zeigt BEW\AV23AGPWBIO1$ und klassifiziert die
|
||||
Folgediagnose als Berechtigungsproblem. Ohne die nachfolgende Freigabe bleiben
|
||||
die betroffenen Checks auch mit der neuen Version UNKNOWN.
|
||||
1. Ein dediziertes Dienstkonto oder bevorzugt gMSA laeuft minuetlich als
|
||||
Scheduled Task "BizTalk Checkmk Pulse Provider".
|
||||
2. Dieses Konto fuehrt BizTalkCheckmkPulse.exe --collect aus.
|
||||
3. Es schreibt atomar einen SHA-256-geschuetzten Snapshot nach:
|
||||
|
||||
Die alte Version kann nach korrekter Freigabe grundsaetzlich funktionieren.
|
||||
Empfohlen ist die neue Version zusammen mit der Gruppenfreigabe, weil sie
|
||||
Fehlerursache, Konto und Massnahme eindeutig anzeigt.
|
||||
%ProgramData%\BizTalkCheckmkPulse\data\biztalk-checkmk-pulse.snapshot
|
||||
|
||||
FREIGABE
|
||||
========
|
||||
4. Der Checkmk-Agent bleibt LocalSystem und startet nur den Consumer.
|
||||
5. Der Consumer liest und validiert den Snapshot. Er fuehrt kein WMI und
|
||||
keinen SQL-Zugriff aus.
|
||||
|
||||
1. Exakte Operator-Gruppe ermitteln
|
||||
BERECHTIGUNG DES PROVIDER-KONTOS
|
||||
================================
|
||||
|
||||
In der BizTalk Administration Console:
|
||||
Bevorzugte Rolle fuer BizTalk Server 2020:
|
||||
|
||||
BizTalk Group > Properties > General > BizTalk Operators Group
|
||||
BizTalk Server Read Only Users
|
||||
SQL-Datenbankrolle: BTS_READONLY_USERS
|
||||
|
||||
Nicht ungeprueft den Standardnamen verwenden. Mit einem bereits berechtigten
|
||||
Konto ist alternativ folgende Abfrage moeglich:
|
||||
Die exakt konfigurierte Gruppe mit einem bereits berechtigten Konto ermitteln:
|
||||
|
||||
Get-CimInstance -Namespace root/MicrosoftBizTalkServer `
|
||||
-ClassName MSBTS_GroupSetting |
|
||||
Select-Object Name, BizTalkOperatorGroup, MgmtDbServerName, MgmtDbName
|
||||
Select-Object Name, BizTalkReadOnlyUserGroup, BizTalkOperatorGroup,
|
||||
MgmtDbServerName, MgmtDbName
|
||||
|
||||
2. Computerobjekt durch einen AD-Administrator aufnehmen
|
||||
Das Provider-Konto durch AD-Administration in BizTalkReadOnlyUserGroup
|
||||
aufnehmen. Nicht vom Standardnamen ausgehen.
|
||||
|
||||
Import-Module ActiveDirectory
|
||||
$computer = Get-ADComputer -Identity 'AV23AGPWBIO1'
|
||||
Add-ADGroupMember `
|
||||
-Identity '<EXAKTE_BIZTALK_OPERATOR_GRUPPE>' `
|
||||
-Members $computer `
|
||||
-WhatIf
|
||||
Die Operator-Gruppe ist nur eine begruendungspflichtige Eskalation, falls eine
|
||||
konkret benoetigte WMI-Klasse trotz bestaetigter Read-Only-Zuordnung und neuem
|
||||
Anmeldetoken verweigert wird.
|
||||
|
||||
Ziele kontrollieren und danach denselben Add-ADGroupMember-Befehl ohne
|
||||
-WhatIf ausfuehren.
|
||||
NICHT DURCHFUEHREN
|
||||
==================
|
||||
|
||||
Mitgliedschaft pruefen:
|
||||
- keinen SQL-Einzellogin fuer Provider- oder Maschinenkonto
|
||||
- keine ad-hoc BizTalk-Datenbankrollen
|
||||
- kein db_owner oder sysadmin
|
||||
- keine pauschalen DCOM-/WMI-ACL-Erweiterungen
|
||||
- Checkmk-Agentdienst nicht auf das Provider-Konto umstellen
|
||||
- Maschinenkonto BEW\AV23AGPWBIO1$ nicht fuer diese Loesung berechtigen
|
||||
|
||||
Get-ADPrincipalGroupMembership -Identity 'AV23AGPWBIO1' |
|
||||
Select-Object Name, SamAccountName
|
||||
INSTALLATION
|
||||
============
|
||||
|
||||
3. Neue Gruppenzugehoerigkeit aktivieren
|
||||
gMSA:
|
||||
|
||||
AD-Replikation abwarten. Bevorzugt den BizTalk-Server in einem Wartungsfenster
|
||||
neu starten. Alternativ in einer administrativen Shell:
|
||||
.\Install-BizTalkCheckmkPulse.ps1 `
|
||||
-CollectorAccount 'BEW\svc_biztalk_cmk$' `
|
||||
-Gmsa `
|
||||
-EnvironmentName ACC
|
||||
|
||||
klist purge -li 0x3e7
|
||||
Regulaeres Dienstkonto:
|
||||
|
||||
Danach den zuvor eindeutig ermittelten Checkmk-Agent-Dienst neu starten. Falls
|
||||
die neue Mitgliedschaft nicht wirksam wird, den Server neu starten.
|
||||
.\Install-BizTalkCheckmkPulse.ps1 `
|
||||
-CollectorAccount 'BEW\svc_biztalk_cmk' `
|
||||
-EnvironmentName ACC
|
||||
|
||||
4. Im echten Checkmk-Kontext verifizieren
|
||||
VERIFIKATION
|
||||
============
|
||||
|
||||
Get-ScheduledTaskInfo -TaskName 'BizTalk Checkmk Pulse Provider'
|
||||
|
||||
Get-Content `
|
||||
"$env:ProgramData\BizTalkCheckmkPulse\logs\*.log" `
|
||||
-Tail 100
|
||||
|
||||
Get-Item `
|
||||
"$env:ProgramData\BizTalkCheckmkPulse\data\biztalk-checkmk-pulse.snapshot"
|
||||
|
||||
& "$env:ProgramFiles\BizTalkCheckmkPulse\BizTalkCheckmkPulse.exe" --consume
|
||||
|
||||
& "C:\Program Files (x86)\checkmk\service\cmk-agent-ctl.exe" dump |
|
||||
Select-String `
|
||||
-Pattern "BizTalk|Login failed|Wmi/Permission|Sql/Permission|UNKNOWN" `
|
||||
-Context 0,1
|
||||
Select-String -Pattern "BizTalk|UNKNOWN|Permission|snapshot" -Context 0,1
|
||||
|
||||
Erwartetes Ergebnis:
|
||||
ERWARTET
|
||||
========
|
||||
|
||||
- BizTalk Platform ist OK und zeigt operator_group=<exakte Gruppe>.
|
||||
- BizTalk SQL Access zeigt targets=2, available=2,
|
||||
discovery_complete=True.
|
||||
- Host Instances, Runtime Artifacts und Suspended Instances sind nicht mehr
|
||||
wegen WMI-/SQL-Berechtigungen UNKNOWN.
|
||||
- Kein Login failed fuer BEW\AV23AGPWBIO1$.
|
||||
|
||||
Der separate Event-Log-Service zeigte im Screenshot zwei Fehler und sechs
|
||||
Warnungen. Diese Ereignisse bleiben nach der Berechtigungsfreigabe fachlich zu
|
||||
pruefen.
|
||||
|
||||
WENN ES DANACH WEITERHIN SCHEITERT
|
||||
==================================
|
||||
|
||||
Normalerweise wurde die konfigurierte BizTalk-Operator-Gruppe bereits durch die
|
||||
BizTalk-Konfiguration am SQL Server eingerichtet. Das Maschinenkonto erbt ueber
|
||||
seine AD-Gruppenmitgliedschaft die Rolle BTS_OPERATORS und benoetigt keinen
|
||||
eigenen SQL-Login.
|
||||
|
||||
Bleibt "Login failed" trotz bestaetigter Mitgliedschaft, AD-Replikation und
|
||||
Token-Erneuerung bestehen, prueft die SQL-Administration read-only:
|
||||
|
||||
SELECT name, type_desc
|
||||
FROM sys.server_principals
|
||||
WHERE name = N'BEW\<EXAKTE_BIZTALK_OPERATOR_GRUPPE>';
|
||||
|
||||
Danach jeweils in BizTalkMgmtDb und BizTalkMsgBoxDb:
|
||||
|
||||
SELECT
|
||||
member_principal.name AS member_name,
|
||||
role_principal.name AS role_name
|
||||
FROM sys.database_role_members AS drm
|
||||
JOIN sys.database_principals AS role_principal
|
||||
ON role_principal.principal_id = drm.role_principal_id
|
||||
JOIN sys.database_principals AS member_principal
|
||||
ON member_principal.principal_id = drm.member_principal_id
|
||||
WHERE member_principal.name =
|
||||
N'BEW\<EXAKTE_BIZTALK_OPERATOR_GRUPPE>'
|
||||
AND role_principal.name = N'BTS_OPERATORS';
|
||||
|
||||
Fehlt die Abbildung, gleichen BizTalk- und SQL-Administration sie mit der
|
||||
BizTalk-Konfiguration ab und reparieren sie fuer die konfigurierte Gruppe.
|
||||
Keinen Einzel-Login oder Sonderrollen fuer BEW\AV23AGPWBIO1$ als Umgehung
|
||||
anlegen.
|
||||
|
||||
SICHERHEIT
|
||||
==========
|
||||
|
||||
Die Gruppenmitgliedschaft des Computerobjekts steht allen Diensten zur
|
||||
Verfuegung, die auf AV23AGPWBIO1 als LocalSystem laufen und auf
|
||||
Netzwerkressourcen zugreifen. Mit Security und BizTalk-Betrieb abstimmen.
|
||||
|
||||
Ist dies nicht akzeptabel, ist ein separater Collector unter einem dedizierten
|
||||
gMSA-/Dienstkonto mit BizTalk-Operator-Rechten die sauberere Architektur. Nicht
|
||||
allein fuer dieses Plugin die Identitaet des gesamten Checkmk-Agent-Dienstes
|
||||
aendern.
|
||||
- LastTaskResult = 0
|
||||
- Snapshot wird minuetlich aktualisiert
|
||||
- Provider-Log nennt das dedizierte Konto
|
||||
- BizTalk Platform zeigt read_only_group=<exakte Gruppe>
|
||||
- BizTalk SQL Access zeigt targets=2 und available=2
|
||||
- keine berechtigungsbedingten UNKNOWN-Services
|
||||
- LocalSystem besitzt auf data nur Lesezugriff
|
||||
|
||||
QUELLEN
|
||||
=======
|
||||
|
||||
https://learn.microsoft.com/en-us/windows/win32/services/localsystem-account
|
||||
https://learn.microsoft.com/en-us/entra/architecture/service-accounts-computer
|
||||
https://learn.microsoft.com/en-us/biztalk/core/managing-biztalk-server-security
|
||||
https://learn.microsoft.com/en-us/biztalk/core/windows-groups-and-user-accounts-in-biztalk-server
|
||||
https://learn.microsoft.com/en-us/biztalk/core/access-control-for-administrative-roles
|
||||
https://learn.microsoft.com/en-us/biztalk/core/how-to-modify-group-properties
|
||||
https://learn.microsoft.com/en-us/powershell/module/activedirectory/add-adgroupmember
|
||||
https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/klist
|
||||
https://learn.microsoft.com/en-us/biztalk/core/technical-reference/msbts-groupsetting-biztalkreadonlyusergroup-property-wmi
|
||||
https://docs.checkmk.com/latest/en/agent_windows.html
|
||||
https://docs.checkmk.com/latest/en/localchecks.html
|
||||
|
||||
@@ -8,12 +8,17 @@ Alle Services werden als Checkmk Local Checks ausgegeben. Das Format ist:
|
||||
|
||||
Die Ausgabe ist so gebaut, dass sie auch mit Checkmk 2.4.0p4 kompatibel bleibt: zwischen den vier Feldern steht genau ein normales Leerzeichen.
|
||||
|
||||
Die fachlichen Daten werden minuetlich vom privilegierten Scheduled-Task-
|
||||
Provider erzeugt. Der Checkmk-Agent liest als `LocalSystem` nur den
|
||||
integritaets- und altersgeprueften Snapshot. Ist dieser Transport nicht
|
||||
verfuegbar, liefern alle sechs stabilen Services `UNKNOWN`.
|
||||
|
||||
## BizTalk Platform
|
||||
|
||||
Zweck:
|
||||
|
||||
- Prueft, ob der BizTalk-WMI-Namespace erreichbar ist.
|
||||
- Zeigt Server, Gruppe, konfigurierte BizTalk-Operator-Gruppe, Management-DB und Master-MessageBox-DB aus `MSBTS_GroupSetting`.
|
||||
- Zeigt Server, Gruppe, konfigurierte BizTalk-Read-Only-/Operator-Gruppe, Management-DB und Master-MessageBox-DB aus `MSBTS_GroupSetting`.
|
||||
|
||||
Status:
|
||||
|
||||
@@ -31,11 +36,12 @@ Metriken:
|
||||
Zweck:
|
||||
|
||||
- Ermittelt Management- und Master-MessageBox-Datenbank ueber `MSBTS_GroupSetting`.
|
||||
- Oeffnet mit der Windows-Identitaet des Checkmk-Agentprozesses eine integrierte SQL-Verbindung.
|
||||
- Oeffnet mit der Windows-Identitaet des privilegierten Provider-Tasks eine integrierte SQL-Verbindung.
|
||||
- Fuehrt die harmlose Abfrage `SELECT 1` aus und schliesst die Verbindung sofort wieder.
|
||||
- Zeigt lokale Ausfuehrungsidentitaet und erwartete Netzwerkidentitaet an.
|
||||
|
||||
Der Service ersetzt nicht das Checkmk-MSSQL-Plugin. Er prueft den fuer das BizTalk-Monitoring relevanten Zugriff des Local Checks.
|
||||
Der Service ersetzt nicht das Checkmk-MSSQL-Plugin. Er prueft den fuer das
|
||||
BizTalk-Monitoring relevanten Zugriff des dedizierten Provider-Kontos.
|
||||
|
||||
Status:
|
||||
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
## Gesunder BizTalk-Server
|
||||
|
||||
```text
|
||||
0 "BizTalk Platform" - BizTalk WMI and platform data reachable, server=BIZTALK01, group=BizTalkMgmtDb, operator_group=CONTOSO\BizTalk Server Operators, mgmt_db=SQL01\BizTalkMgmtDb, msgbox_db=SQL01\BizTalkMsgBoxDb
|
||||
0 "BizTalk SQL Access" biztalk_sql_targets_total=2;;;0|biztalk_sql_targets_available=2;;;0|biztalk_sql_targets_failed=0;;1;0 SQL access targets=2, available=2, discovery_complete=True, execution_identity=NT AUTHORITY\SYSTEM, network_identity=<DOMAIN>\BIZTALK01$; databases=Management=SQL01\BizTalkMgmtDb:OK, MasterMessageBox=SQL01\BizTalkMsgBoxDb:OK
|
||||
0 "BizTalk Platform" - BizTalk WMI and platform data reachable, server=BIZTALK01, group=BizTalkMgmtDb, read_only_group=CONTOSO\BizTalk Server Read Only Users, operator_group=CONTOSO\BizTalk Server Operators, mgmt_db=SQL01\BizTalkMgmtDb, msgbox_db=SQL01\BizTalkMsgBoxDb
|
||||
0 "BizTalk SQL Access" biztalk_sql_targets_total=2;;;0|biztalk_sql_targets_available=2;;;0|biztalk_sql_targets_failed=0;;1;0 SQL access targets=2, available=2, discovery_complete=True, execution_identity=CONTOSO\svc_biztalk_cmk$, network_identity=CONTOSO\svc_biztalk_cmk$; databases=Management=SQL01\BizTalkMgmtDb:OK, MasterMessageBox=SQL01\BizTalkMsgBoxDb:OK
|
||||
0 "BizTalk Suspended Instances" biztalk_suspended_total=0;;;0|biztalk_suspended_resumable=0;1;;0|biztalk_suspended_nonresumable=0;;1;0 No suspended BizTalk service instances found.
|
||||
0 "BizTalk Host Instances" biztalk_host_instances_total=4;;;0|biztalk_host_instances_started=4;;;0|biztalk_host_instances_stopped=0;;1;0|biztalk_host_instances_pending=0;1;;0|biztalk_host_instances_unknown=0;;1;0 Host instances total=4, started=4, stopped=0, pending=0, unknown=0
|
||||
0 "BizTalk Runtime Artifacts" biztalk_applications=8;;;0|biztalk_receive_locations=24;;;0|biztalk_receive_locations_disabled=0;1;;0|biztalk_send_ports=31;;;0|biztalk_send_ports_started=31;;;0|biztalk_send_ports_inactive=0;1;;0|biztalk_send_ports_unknown=0;;1;0|biztalk_orchestrations=12;;;0|biztalk_orchestrations_started=12;;;0|biztalk_orchestrations_inactive=0;1;;0|biztalk_orchestrations_unknown=0;;1;0 Applications=8; receive_locations total=24, disabled=0; send_ports total=31, started=31, inactive=0, unknown=0; orchestrations total=12, started=12, inactive=0, unknown=0
|
||||
@@ -31,20 +31,20 @@
|
||||
## SQL-Berechtigungsfehler
|
||||
|
||||
```text
|
||||
3 "BizTalk SQL Access" biztalk_sql_targets_total=2;;;0|biztalk_sql_targets_available=0;;;0|biztalk_sql_targets_failed=2;;1;0 SQL access targets=2, available=0, discovery_complete=True, execution_identity=NT AUTHORITY\SYSTEM, network_identity=<DOMAIN>\BIZTALK01$; databases=Management=SQL01\BizTalkMgmtDb:FAILED, MasterMessageBox=SQL01\BizTalkMsgBoxDb:FAILED; diagnostics: Sql/Permission [Management SQL01\BizTalkMgmtDb]: SQL Server hat die integrierte Anmeldung abgelehnt. Erwartete Netzwerkidentitaet: <DOMAIN>\BIZTALK01$. Massnahme: <DOMAIN>\BIZTALK01$ in die konfigurierte BizTalk-Operator-Gruppe aufnehmen; keine direkten BizTalk-DB-Rollen vergeben. Danach Kerberos-Tickets erneuern und den Agent-Dump wiederholen. Technik: SqlException Number=18456: Login failed for user
|
||||
3 "BizTalk SQL Access" biztalk_sql_targets_total=2;;;0|biztalk_sql_targets_available=0;;;0|biztalk_sql_targets_failed=2;;1;0 SQL access targets=2, available=0, discovery_complete=True, execution_identity=CONTOSO\svc_biztalk_cmk$, network_identity=CONTOSO\svc_biztalk_cmk$; databases=Management=SQL01\BizTalkMgmtDb:FAILED, MasterMessageBox=SQL01\BizTalkMsgBoxDb:FAILED; diagnostics: Sql/Permission [Management SQL01\BizTalkMgmtDb]: SQL Server hat die integrierte Anmeldung abgelehnt. Massnahme: Provider-Konto und konfigurierte BizTalk-Read-Only-Gruppe mit BTS_READONLY_USERS pruefen; keine direkten BizTalk-DB-Rollen vergeben. Technik: SqlException Number=18456: Login failed for user
|
||||
```
|
||||
|
||||
## WMI-Berechtigungsfehler
|
||||
|
||||
```text
|
||||
3 "BizTalk Suspended Instances" - Suspendierte Instanzen konnten nicht verlaesslich gelesen werden. Ein leerer Datenbestand wird nicht als OK gewertet.; diagnostics: Wmi/Permission [MSBTS_ServiceInstance]: Zugriff auf MSBTS_ServiceInstance wurde im LocalSystem-Kontext verweigert. Massnahme: Agent-Dump pruefen und Computerkonto <DOMAIN>\BIZTALK01$ zunaechst der konfigurierten BizTalk-Operator-Gruppe zuordnen. Technik: UnauthorizedAccessException HRESULT=0x80070005: Access is denied
|
||||
3 "BizTalk Suspended Instances" - Suspendierte Instanzen konnten nicht verlaesslich gelesen werden. Ein leerer Datenbestand wird nicht als OK gewertet.; diagnostics: Wmi/Permission [MSBTS_ServiceInstance]: Zugriff des Provider-Kontos auf MSBTS_ServiceInstance wurde verweigert. Massnahme: Provider-Identitaet und konfigurierte BizTalk-Read-Only-Gruppe pruefen. Technik: UnauthorizedAccessException HRESULT=0x80070005: Access is denied
|
||||
```
|
||||
|
||||
## SQL-Loginfehler innerhalb des BizTalk-WMI-Providers
|
||||
## Fehlender oder veralteter Provider-Snapshot
|
||||
|
||||
```text
|
||||
3 "BizTalk Platform" - BizTalk-Plattformdaten konnten nicht vollstaendig gelesen werden.; diagnostics: Wmi/Permission [MSBTS_GroupSetting]: BizTalk-WMI-Provider konnte den SQL-Zugriff fuer BEW\AV23AGPWBIO1$ nicht anmelden. Massnahme: Computerkonto BEW\AV23AGPWBIO1$ der in der BizTalk-Gruppe konfigurierten BizTalk-Operator-Gruppe zuordnen; keine direkten SQL-Logins oder Datenbankrollen vergeben. Technik: COMException HRESULT=0x80131904: Internal error from OLEDB provider: 'Login failed for user 'BEW\AV23AGPWBIO1$'.'
|
||||
3 "BizTalk SQL Access" biztalk_sql_targets_total=0;;;0|biztalk_sql_targets_available=0;;;0|biztalk_sql_targets_failed=0;;1;0 SQL access targets=0, available=0, discovery_complete=False, execution_identity=NT AUTHORITY\SYSTEM, network_identity=BEW\AV23AGPWBIO1$; diagnostics: Sql/Permission [BizTalk database discovery]: SQL-Zielermittlung ist unvollstaendig, weil der BizTalk-WMI-Provider beim SQL-Zugriff abgewiesen wurde.
|
||||
3 "BizTalk Platform" - Privilegierter BizTalk-Datensnapshot nicht verfuegbar: Snapshot is stale: age=241s, maximum=180s. Massnahme: Scheduled Task 'BizTalk Checkmk Pulse Provider', Provider-Log, Snapshot-Alter und ACL pruefen.
|
||||
3 "BizTalk SQL Access" - Privilegierter BizTalk-Datensnapshot nicht verfuegbar: Snapshot is stale: age=241s, maximum=180s. Massnahme: Scheduled Task 'BizTalk Checkmk Pulse Provider', Provider-Log, Snapshot-Alter und ACL pruefen.
|
||||
```
|
||||
|
||||
## WMI-Schemafehler
|
||||
@@ -56,5 +56,5 @@
|
||||
## Fehlendes Deployment
|
||||
|
||||
```text
|
||||
3 "BizTalk Platform" - BizTalkCheckmkPulse.exe fehlt unter C:\ProgramData\checkmk\agent\local\BizTalkCheckmkPulse. Massnahme: Deployment-Paket und Dateirechte pruefen und danach cmk-agent-ctl.exe dump erneut ausfuehren.
|
||||
3 "BizTalk Platform" - BizTalkCheckmkPulse.exe fehlt unter C:\Program Files\BizTalkCheckmkPulse. Massnahme: Install-BizTalkCheckmkPulse.ps1 erneut als Administrator ausfuehren und danach cmk-agent-ctl.exe dump erneut ausfuehren.
|
||||
```
|
||||
|
||||
+112
-217
@@ -1,247 +1,142 @@
|
||||
# Integration in Checkmk
|
||||
# Checkmk-Integration und Betriebsuebergabe
|
||||
|
||||
## Zielstruktur auf dem BizTalk-Server
|
||||
## Verantwortungsgrenzen
|
||||
|
||||
| Team | Aufgabe |
|
||||
| --- | --- |
|
||||
| AD/Security | dediziertes Dienstkonto oder gMSA; Aufnahme in exakt konfigurierte BizTalk-Read-Only-Gruppe |
|
||||
| BizTalk/SQL | Gruppenabbildung und `BTS_READONLY_USERS` bestaetigen; keine Einzelrechte |
|
||||
| Windows | Paket installieren, ACL und Scheduled Task pruefen |
|
||||
| Checkmk | Wrapper verteilen beziehungsweise Installation koordinieren, Discovery und Alarmierung |
|
||||
|
||||
Der Checkmk-Agent bleibt `LocalSystem`. Er greift weder auf BizTalk-WMI noch
|
||||
auf SQL zu. Der minuetliche Scheduled Task sammelt unter dem privilegierten
|
||||
Provider-Konto und publiziert einen validierbaren Snapshot.
|
||||
|
||||
## Installation
|
||||
|
||||
gMSA:
|
||||
|
||||
```powershell
|
||||
.\Install-BizTalkCheckmkPulse.ps1 `
|
||||
-CollectorAccount 'BEW\svc_biztalk_cmk$' `
|
||||
-Gmsa `
|
||||
-EnvironmentName ACC
|
||||
```
|
||||
|
||||
Regulaeres Dienstkonto:
|
||||
|
||||
```powershell
|
||||
.\Install-BizTalkCheckmkPulse.ps1 `
|
||||
-CollectorAccount 'BEW\svc_biztalk_cmk' `
|
||||
-EnvironmentName ACC
|
||||
```
|
||||
|
||||
Der Installer legt den Local Check hier ab:
|
||||
|
||||
```text
|
||||
%ProgramData%\checkmk\agent\local\biztalk_checkmk_pulse.cmd
|
||||
%ProgramData%\checkmk\agent\local\BizTalkCheckmkPulse\BizTalkCheckmkPulse.exe
|
||||
%ProgramData%\checkmk\agent\local\BizTalkCheckmkPulse\BizTalkCheckmkPulse.exe.config
|
||||
```
|
||||
|
||||
## Manuelle Installation
|
||||
|
||||
1. Release-Paket auf den BizTalk-Server kopieren.
|
||||
2. Inhalt nach `%ProgramData%\checkmk\agent\local` kopieren.
|
||||
3. Optional `EnvironmentName` in der `.exe.config` setzen.
|
||||
4. Test ausfuehren:
|
||||
|
||||
```cmd
|
||||
"%ProgramData%\checkmk\agent\local\biztalk_checkmk_pulse.cmd" --self-test
|
||||
"%ProgramData%\checkmk\agent\local\biztalk_checkmk_pulse.cmd"
|
||||
```
|
||||
|
||||
5. Agent-Ausgabe pruefen:
|
||||
|
||||
```cmd
|
||||
"C:\Program Files (x86)\checkmk\service\cmk-agent-ctl.exe" dump
|
||||
```
|
||||
|
||||
6. In Checkmk:
|
||||
|
||||
- Host oeffnen
|
||||
- Service Discovery ausfuehren
|
||||
- gefundene `BizTalk ...` Services aufnehmen
|
||||
- Changes aktivieren
|
||||
|
||||
## Aufgaben in Checkmk
|
||||
|
||||
Die Checkmk-Kollegen muessen kein serverseitiges Python-Check-Plugin installieren. Das Plugin liefert bereits fertige Local-Check-Services ueber den Windows Agent. In Checkmk selbst sind diese Schritte erforderlich:
|
||||
|
||||
1. BizTalk-Hosts fuer `ACC`, `DEV`, `TST` und `PRD` anlegen oder vorhandene Hosts pruefen.
|
||||
2. Windows-Agent-Status pruefen: Host muss Agent-Daten liefern.
|
||||
3. Nach Installation des Local Checks den Agent-Dump pruefen.
|
||||
4. Service Discovery fuer jeden BizTalk-Host ausfuehren.
|
||||
5. Gefundene `BizTalk ...` Services aufnehmen.
|
||||
6. Changes aktivieren.
|
||||
7. Views oder Dashboards mit Filter `Service starts with: BizTalk` anlegen.
|
||||
8. Benachrichtigungen und Eskalationen je Umgebung definieren.
|
||||
|
||||
Empfohlene Service-Behandlung:
|
||||
|
||||
| Service | Empfehlung |
|
||||
| --- | --- |
|
||||
| `BizTalk Platform` | `UNKNOWN` immer als Integrationsproblem behandeln. |
|
||||
| `BizTalk SQL Access` | `UNKNOWN` anhand der Kategorie `Permission`, `Connectivity`, `Timeout`, `Configuration` oder `Provider` bearbeiten. |
|
||||
| `BizTalk Suspended Instances` | In `PRD` alarmieren; in `ACC`/`TST`/`DEV` nach Teamvereinbarung. |
|
||||
| `BizTalk Host Instances` | `CRIT` alarmieren. |
|
||||
| `BizTalk Runtime Artifacts` | Erst beobachten; strenge Alarmierung nur bei klar definiertem Runtime-Sollzustand. |
|
||||
| `BizTalk Event Log` | Schwellwerte nach Beobachtungsphase feinjustieren. |
|
||||
|
||||
## Agent Bakery
|
||||
|
||||
Checkmk Managed Services Edition 2.4 enthaelt die kommerziellen Mechanismen fuer Agent Bakery. Fuer einen sauberen Rollout:
|
||||
|
||||
1. Deployment-Dateien als Custom Files oder ueber ein spaeteres MKP bereitstellen.
|
||||
2. Regel nur auf BizTalk-Hosts anwenden, z.B. Host-Tag `application:biztalk`.
|
||||
3. Gebackenen Windows-Agenten fuer die BizTalk-Hosts installieren.
|
||||
4. Service Discovery ausfuehren.
|
||||
|
||||
Hinweis: Dieses Repository enthaelt bewusst noch kein Bakery-Python-Plugin. Die manuelle bzw. dateibasierte Verteilung ist die risikoarme erste Version. Eine Bakery-Erweiterung ist eine sinnvolle Version-2-Ausbaustufe.
|
||||
|
||||
## Berechtigungen fuer WMI und BizTalk-Datenbanken
|
||||
|
||||
### Ausfuehrung als LocalSystem
|
||||
|
||||
Der Checkmk Windows Agent und der Agent Controller laufen standardmaessig unter `LocalSystem` (`NT AUTHORITY\SYSTEM`). Dadurch werden auch `biztalk_checkmk_pulse.cmd` und `BizTalkCheckmkPulse.exe` in diesem Kontext gestartet.
|
||||
|
||||
Das Plugin verbindet sich mit dem lokalen Namespace `\\<eigener-server>\root\MicrosoftBizTalkServer`, setzt keine separaten Anmeldedaten und verwendet kein Remote-WMI. Es fuehrt ausschliesslich WMI-Leseabfragen aus und liest das lokale Windows Application Event Log. Fuer diese lokalen Zugriffe sind normalerweise keine zusaetzlichen DCOM-, Firewall- oder WMI-Namespace-Freigaben erforderlich.
|
||||
|
||||
### Besonderheit bei getrenntem SQL Server
|
||||
|
||||
Mehrere BizTalk-WMI-Klassen lesen Daten aus der BizTalk Management- oder MessageBox-Datenbank. Bei einem getrennten SQL Server verwendet `LocalSystem` fuer diesen Netzwerkzugriff das Active-Directory-Computerkonto des BizTalk-Servers:
|
||||
Die EXE liegt zentral hier:
|
||||
|
||||
```text
|
||||
DOMAIN\BIZTALKSERVER$
|
||||
%ProgramFiles%\BizTalkCheckmkPulse\BizTalkCheckmkPulse.exe
|
||||
```
|
||||
|
||||
Lokale Administratorrechte von `LocalSystem` gelten nicht automatisch auf dem entfernten SQL Server. Daher koennen SQL-gestuetzte WMI-Abfragen fehlschlagen, obwohl der lokale WMI-Namespace grundsaetzlich erreichbar ist.
|
||||
## Checkmk-Agent-Konfiguration
|
||||
|
||||
### Konkreter ACC-Befund
|
||||
Der Wrapper darf synchron laufen, da er nur einen kleinen lokalen Snapshot
|
||||
liest. Eine alte Async-Regel mit `cache_age: 300` aus der direkten
|
||||
WMI-Architektur soll entfernt werden; andernfalls addiert sie eine unnoetige
|
||||
Verzoegerung zum minuetlichen Provider.
|
||||
|
||||
Auf `AV23AGPWBIO1` meldet der Provider `COMException HRESULT=0x80131904` und als inneren SQL-Fehler `Login failed for user 'BEW\AV23AGPWBIO1$'`. Das beweist zugleich:
|
||||
Aktive Agentkonfiguration pruefen:
|
||||
|
||||
- der Check laeuft als `NT AUTHORITY\SYSTEM`
|
||||
- der lokale BizTalk-WMI-Namespace ist erreichbar
|
||||
- der Netzwerkzugriff erfolgt als Maschinenkonto `BEW\AV23AGPWBIO1$`
|
||||
- dieses Konto besitzt noch nicht die durch BizTalk vermittelte SQL-Berechtigung
|
||||
```powershell
|
||||
& "C:\Program Files (x86)\checkmk\service\check_mk_agent.exe" showconfig local
|
||||
```
|
||||
|
||||
Die vielen `UNKNOWN`-Services sind Folgewirkungen desselben Fehlers. WMI-Namespace-ACL, DCOM und Firewall muessen fuer diesen Befund nicht erweitert werden. Die zwei Fehler und sechs Warnungen in `BizTalk Event Log` sind davon unabhaengig und separat auszuwerten.
|
||||
|
||||
### Was die neue Version aendert - und was nicht
|
||||
|
||||
Die neue Version verbessert Diagnose und Betriebsfuehrung:
|
||||
|
||||
- `0x80131904` mit `Login failed for user` wird als `Wmi/Permission` erkannt.
|
||||
- Das wirklich abgewiesene Konto wird aus der Providerfehlermeldung uebernommen.
|
||||
- Die unvollstaendige SQL-Zielermittlung wird als `Sql/Permission` statt als allgemeine Konfigurationstoerung ausgegeben.
|
||||
- Nach erfolgreichem Zugriff wird die konfigurierte Gruppe als `operator_group=` sichtbar.
|
||||
|
||||
Die EXE kann und darf jedoch keine AD-Gruppenmitgliedschaft, SQL-Logins oder Datenbankrollen anlegen. Die alte Version kann nach korrekter Berechtigungsfreigabe grundsaetzlich arbeiten; die neue Version ohne Freigabe bleibt weiterhin `UNKNOWN`. Der Sollzustand ist daher: neue Version plus korrekte Gruppenberechtigung.
|
||||
|
||||
### Verbindlicher Funktionstest
|
||||
|
||||
Der direkte Start des Wrappers in einer administrativen Eingabeaufforderung ist nur ein Vorabtest, weil er mit dem angemeldeten Benutzer laeuft. Fuer die Berechtigungspruefung ist die Ausgabe unter dem echten Checkmk-Kontext massgeblich:
|
||||
Verbindlicher Agent-Dump:
|
||||
|
||||
```powershell
|
||||
& "C:\Program Files (x86)\checkmk\service\cmk-agent-ctl.exe" dump |
|
||||
Select-String -Pattern "BizTalk|Login failed|Access denied|Unauthorized|UNKNOWN" -Context 0,1
|
||||
Select-String -Pattern "BizTalk|UNKNOWN|snapshot|Permission" -Context 0,1
|
||||
```
|
||||
|
||||
Die Ausgabe muss die sechs stabilen `BizTalk ...` Services mit plausiblen Daten enthalten. `Access denied`, `UnauthorizedAccessException`, `Sql/Permission` und ein berechtigungsbedingtes `UNKNOWN` weisen auf eine fehlende Rollenzuordnung hin.
|
||||
## Service Discovery
|
||||
|
||||
Die EXE klassifiziert WMI- und SQL-Probleme und schreibt zu jeder Diagnose `Massnahme:` und `Technik:`. WMI-Schemafehler (`InvalidClass`/`InvalidQuery`) werden separat als `Wmi/Schema` ausgewiesen und duerfen nicht durch Rechteerweiterungen behandelt werden. Eine fehlgeschlagene erforderliche WMI-Abfrage erzeugt beim betroffenen Service immer `UNKNOWN`; fehlende Daten werden nicht als Nullbestand und damit nicht als `OK` ausgegeben.
|
||||
1. Provider dreimal erfolgreich laufen lassen.
|
||||
2. `LastTaskResult=0`, frischen Snapshot und Log pruefen.
|
||||
3. Agent-Dump im `LocalSystem`-Kontext pruefen.
|
||||
4. Service Discovery fuer den BizTalk-Host ausfuehren.
|
||||
5. sechs stabile Services aufnehmen.
|
||||
6. Changes aktivieren.
|
||||
7. Views, Servicegruppen und Benachrichtigungen einrichten.
|
||||
|
||||
Fuer den produktiven Betrieb wird empfohlen, den Local Check alle 300 Sekunden asynchron auszufuehren. Dadurch erzeugen fehlende SQL-Rechte nicht bei jedem Agent-Abruf neue fehlgeschlagene Logins. In `%ProgramData%\checkmk\agent\check_mk.user.yml`:
|
||||
Host-Tags:
|
||||
|
||||
```yaml
|
||||
local:
|
||||
enabled: yes
|
||||
execution:
|
||||
- pattern: $CUSTOM_LOCAL_PATH$\biztalk_checkmk_pulse.cmd
|
||||
async: yes
|
||||
run: yes
|
||||
cache_age: 300
|
||||
```text
|
||||
env:ACC|DEV|TST|PRD
|
||||
app:biztalk
|
||||
```
|
||||
|
||||
Alternativ koennen die Checkmk-Kollegen in der Agent Bakery die Regeln `Set execution mode for plug-ins and local checks` und `Set cache age for plug-ins and local checks` verwenden. Der Cache reduziert Last und SQL-Fehlerlogs; ein Zustandswechsel wird dadurch um maximal die konfigurierte Cache-Zeit verzoegert.
|
||||
|
||||
### Least-Privilege-Vorgehen
|
||||
|
||||
1. Dienstkonto mit `Get-CimInstance Win32_Service` kontrollieren; der ACC-Dump bestaetigt bereits `NT AUTHORITY\SYSTEM`.
|
||||
2. In der BizTalk Administration Console unter `BizTalk Group` > `Properties` > `General` den Wert `BizTalk Operators Group` ablesen. Alternativ kann ein berechtigtes Konto `MSBTS_GroupSetting.BizTalkOperatorGroup` abfragen.
|
||||
3. Ein AD-Administrator fuegt das Computerobjekt `AV23AGPWBIO1` genau dieser Gruppe hinzu:
|
||||
|
||||
```powershell
|
||||
Import-Module ActiveDirectory
|
||||
$computer = Get-ADComputer -Identity 'AV23AGPWBIO1'
|
||||
Add-ADGroupMember -Identity '<EXAKTE_BIZTALK_OPERATOR_GRUPPE>' -Members $computer -WhatIf
|
||||
```
|
||||
|
||||
Erst nach Kontrolle ohne `-WhatIf` ausfuehren. Keine direkten SQL-Logins, Datenbankrollen oder `sysadmin`-Rechte fuer `BEW\AV23AGPWBIO1$` anlegen.
|
||||
|
||||
4. AD-Replikation abwarten und den Server im Wartungsfenster neu starten. Alternativ Maschinen-Tickets in einer administrativen Shell mit `klist purge -li 0x3e7` verwerfen und den Checkmk-Agent-Dienst neu starten.
|
||||
5. Agent-Dump erneut ausfuehren. Erwartet werden `operator_group=<...>`, `targets=2`, `available=2`, `discovery_complete=True` und keine berechtigungsbedingten `UNKNOWN`-Services.
|
||||
6. Nur bei weiterhin abgelehnten, konkret identifizierten WMI-Klassen mit BizTalk- und SQL-Administration klaeren, ob erweiterte Rechte erforderlich sind.
|
||||
|
||||
Die BizTalk-Operator-Rolle ist fuer grundlegende Administration und Monitoring vorgesehen. Sie kann Zustands- und Message-Flow-Informationen lesen, darf aber keine Konfiguration oder Nachrichteninhalte einsehen. Das entspricht dem lesenden Funktionsumfang dieses Plugins. Keine direkten SQL-Rollen in den BizTalk-Datenbanken hinzufuegen; die von BizTalk konfigurierte Windows-Gruppe soll die Berechtigungen vermitteln.
|
||||
|
||||
### Wenn die Gruppenmitgliedschaft allein nicht genuegt
|
||||
|
||||
Normalerweise hat die BizTalk-Konfiguration die konfigurierte Domain-Gruppe bereits auf dem SQL Server abgebildet und ihren Datenbankbenutzern die Rolle `BTS_OPERATORS` zugeordnet. Das Maschinenkonto benoetigt dann keinen eigenen SQL-Login, sondern erbt den Zugriff ueber die AD-Gruppe.
|
||||
|
||||
Bleibt der Loginfehler nach AD-Replikation und Token-Erneuerung bestehen, prueft die SQL-Administration zunaechst read-only den Gruppenlogin:
|
||||
|
||||
```sql
|
||||
SELECT name, type_desc
|
||||
FROM sys.server_principals
|
||||
WHERE name = N'BEW\<EXAKTE_BIZTALK_OPERATOR_GRUPPE>';
|
||||
```
|
||||
|
||||
Anschliessend wird in `BizTalkMgmtDb` und `BizTalkMsgBoxDb` die Rollenzuordnung geprueft:
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
member_principal.name AS member_name,
|
||||
role_principal.name AS role_name
|
||||
FROM sys.database_role_members AS drm
|
||||
JOIN sys.database_principals AS role_principal
|
||||
ON role_principal.principal_id = drm.role_principal_id
|
||||
JOIN sys.database_principals AS member_principal
|
||||
ON member_principal.principal_id = drm.member_principal_id
|
||||
WHERE member_principal.name = N'BEW\<EXAKTE_BIZTALK_OPERATOR_GRUPPE>'
|
||||
AND role_principal.name = N'BTS_OPERATORS';
|
||||
```
|
||||
|
||||
Liefert die Abfrage keinen passenden Eintrag, wird die fehlende Gruppenabbildung mit der BizTalk-Konfiguration verglichen und durch BizTalk-/SQL-Administration fuer die konfigurierte Gruppe repariert. Kein Einzel-Login fuer `BEW\AV23AGPWBIO1$`, keine ad-hoc Rollenvergabe und kein `sysadmin` als Umgehung.
|
||||
|
||||
Entscheidungsmatrix:
|
||||
|
||||
| Beobachtung | Bewertung und Massnahme |
|
||||
| --- | --- |
|
||||
| Alle sechs stabilen Services liefern plausible Werte | Keine Aenderung erforderlich. |
|
||||
| `root\MicrosoftBizTalkServer` ist nicht erreichbar | BizTalk-WMI-Provider, WMI-Dienst, Namespace und dessen ACL gezielt pruefen. |
|
||||
| `Wmi/Schema` beziehungsweise `InvalidClass`/`InvalidQuery` | Klassen- und Property-Namen gegen das installierte BizTalk-WMI-Schema pruefen; keine Berechtigungen erweitern. |
|
||||
| `BizTalk SQL Access` meldet `Sql/Permission` | Maschinenkonto in die konfigurierte BizTalk-Operator-Gruppe aufnehmen, Kerberos erneuern und Agent-Dump wiederholen. |
|
||||
| `BizTalk SQL Access` meldet `Sql/Connectivity` | SQL-Server-/Instanzname, DNS, Dienst, TCP-Port und Firewall pruefen. |
|
||||
| `BizTalk SQL Access` meldet `Sql/Timeout` | SQL-/Netzwerkauslastung untersuchen; Timeout nur nach Ursachenanalyse anpassen. |
|
||||
| SQL-gestuetzte Klassen melden `0x80131904` oder `Login failed for user` | Das exakt genannte Computerkonto in die konfigurierte BizTalk-Operator-Gruppe aufnehmen. |
|
||||
| Nur `BizTalk Event Log` ist `UNKNOWN` | Lokalen Zugriff auf das Application Event Log pruefen. |
|
||||
| Fehler bleibt nach Operator-Zuweisung bestehen | Exakte WMI-Klasse anhand der Service-Ausgabe bestimmen und deren BizTalk-/SQL-Rollenanforderung pruefen. |
|
||||
|
||||
### Sicherheitsauswirkung und gMSA-Alternative
|
||||
|
||||
Die Mitgliedschaft des Computerkontos in der Operator-Gruppe gilt fuer alle Dienste, die auf dem BizTalk-Server als `LocalSystem` laufen und mit dem Computerkonto auf Netzwerkressourcen zugreifen. Diese Auswirkung muss mit Security und BizTalk-Betrieb abgestimmt werden.
|
||||
|
||||
Falls das Computerkonto keine BizTalk-Rechte erhalten darf, kann ein separater Collector unter einem dedizierten gMSA- oder Dienstkonto mit Operator-Rechten die Checkmk-Ausgabe erzeugen und als Spool-Datei bereitstellen. Das waere eine eigene Betriebsvariante und ist in der aktuellen Local-Check-Version noch nicht implementiert. Den gesamten Checkmk-Agent-Dienst nur fuer dieses Plugin auf ein anderes Konto umzustellen ist nicht empfohlen, da die Identitaetsaenderung alle Agent-Sektionen und Local Checks betrifft.
|
||||
|
||||
Quellen:
|
||||
|
||||
- Checkmk Windows Agent: https://docs.checkmk.com/latest/en/agent_windows.html
|
||||
- Checkmk Local Checks: https://docs.checkmk.com/latest/en/localchecks.html
|
||||
- BizTalk Minimum Security User Rights: https://learn.microsoft.com/en-us/biztalk/core/minimum-security-user-rights
|
||||
- BizTalk Access Control and Data Security: https://learn.microsoft.com/en-us/biztalk/core/access-control-and-data-security
|
||||
- BizTalk `MSBTS_GroupSetting`: https://learn.microsoft.com/en-us/biztalk/core/technical-reference/msbts-groupsetting-wmi
|
||||
- BizTalk Windows-Gruppen und SQL-Rollen: https://learn.microsoft.com/en-us/biztalk/core/windows-groups-and-user-accounts-in-biztalk-server
|
||||
- BizTalk administrative Rollen: https://learn.microsoft.com/en-us/biztalk/core/access-control-for-administrative-roles
|
||||
- BizTalk Gruppen-Eigenschaften: https://learn.microsoft.com/en-us/biztalk/core/how-to-modify-group-properties
|
||||
- BizTalk WMI Core Server Classes: https://learn.microsoft.com/en-us/biztalk/core/technical-reference/core-server-classes
|
||||
- Microsoft LocalSystem und Computerkonten: https://learn.microsoft.com/en-us/entra/architecture/service-accounts-computer
|
||||
- Microsoft `Add-ADGroupMember`: https://learn.microsoft.com/en-us/powershell/module/activedirectory/add-adgroupmember
|
||||
- Microsoft `klist`: https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/klist
|
||||
- Microsoft WMI Namespace Security: https://learn.microsoft.com/en-us/windows/win32/wmisdk/access-to-wmi-namespaces
|
||||
|
||||
## Empfohlene Host-Struktur
|
||||
|
||||
Host-Tags oder Ordner:
|
||||
|
||||
- `env:ACC`
|
||||
- `env:DEV`
|
||||
- `env:TST`
|
||||
- `env:PRD`
|
||||
- `app:biztalk`
|
||||
|
||||
Service-Filter fuer Views:
|
||||
Servicefilter:
|
||||
|
||||
```text
|
||||
Service starts with: BizTalk
|
||||
```
|
||||
|
||||
Dashboard-Kacheln:
|
||||
## Bakery-/Softwareverteilung
|
||||
|
||||
- Host/Service state fuer BizTalk-Host
|
||||
- Service state fuer `BizTalk Suspended Instances`
|
||||
- Service state fuer `BizTalk SQL Access`
|
||||
- Graph `biztalk_suspended_total`
|
||||
- Graph `biztalk_host_instances_stopped`
|
||||
- Graph `biztalk_eventlog_errors`
|
||||
- MSSQL-Services des SQL-Servers daneben
|
||||
Die komplette Installation umfasst mehr als eine Dateiablage:
|
||||
|
||||
- Programmdateien unter `%ProgramFiles%`,
|
||||
- Runtimeverzeichnisse und ACLs,
|
||||
- Scheduled Task mit Providerkonto,
|
||||
- Local-Check-Wrapper.
|
||||
|
||||
Der Wrapper allein kann per Agent Bakery verteilt werden, ersetzt aber nicht
|
||||
die lokale Providerinstallation und die Kontofreigabe. Fuer den ersten Rollout
|
||||
ist das signierte/abgenommene Deployment-Paket mit administrativer
|
||||
Installationsautomation die klarere Variante.
|
||||
|
||||
## Healthchecks
|
||||
|
||||
```powershell
|
||||
Get-ScheduledTaskInfo -TaskName 'BizTalk Checkmk Pulse Provider'
|
||||
Get-Item "$env:ProgramData\BizTalkCheckmkPulse\data\biztalk-checkmk-pulse.snapshot"
|
||||
Get-Content "$env:ProgramData\BizTalkCheckmkPulse\logs\*.log" -Tail 50
|
||||
& "$env:ProgramFiles\BizTalkCheckmkPulse\BizTalkCheckmkPulse.exe" --consume
|
||||
```
|
||||
|
||||
Soll:
|
||||
|
||||
- Task laeuft jede Minute und endet mit `0`.
|
||||
- Snapshot ist kleiner als `SnapshotMaxBytes` und juenger als 180 Sekunden.
|
||||
- Log nennt das dedizierte Providerkonto.
|
||||
- Platform zeigt `read_only_group=...`.
|
||||
- SQL Access zeigt `targets=2`, `available=2`.
|
||||
- keine berechtigungsbedingten `UNKNOWN`-Services.
|
||||
|
||||
## Alarmierung der Transportkette
|
||||
|
||||
Ein Ausfall des Providers wird ueber alle sechs Services als `UNKNOWN`
|
||||
sichtbar. Die Summary nennt fehlenden, unlesbaren, ungueltigen oder stale
|
||||
Snapshot. Als Betriebsregel sollte `UNKNOWN` dieser Services genauso
|
||||
eskaliert werden wie ein technischer Monitoringausfall.
|
||||
|
||||
Optional kann Windows Task Scheduler zusaetzlich durch vorhandene Checkmk
|
||||
Task-/Event-Log-Regeln ueberwacht werden. Das ist eine Ergaenzung, kein Ersatz
|
||||
fuer die eingebaute Stale-Pruefung.
|
||||
|
||||
## Rollout
|
||||
|
||||
Empfohlene Reihenfolge:
|
||||
|
||||
1. ACC: Berechtigung, Task, Snapshot, Stale-Test und Discovery abnehmen.
|
||||
2. DEV/TST: gleiche Automatisierung und umgebungsspezifische Config.
|
||||
3. PRD: Change, Wartungsfenster, Healthcheck und fachliche Plausibilitaet.
|
||||
|
||||
Ausfuehrliche Architektur, ACL, Fehlerbilder und Abnahmekriterien:
|
||||
[Dokumentation.md](../Dokumentation.md).
|
||||
|
||||
@@ -6,15 +6,16 @@ if errorlevel 1 exit /b 1
|
||||
|
||||
set "ROOT=%~dp0.."
|
||||
set "OUT=%ROOT%\artifacts\BizTalkCheckmkPulse-deploy"
|
||||
set "APP=%OUT%\BizTalkCheckmkPulse"
|
||||
set "APP=%OUT%\application"
|
||||
|
||||
if exist "%OUT%" rmdir /s /q "%OUT%"
|
||||
mkdir "%APP%"
|
||||
|
||||
copy "%ROOT%\deployment\checkmk\biztalk_checkmk_pulse.cmd" "%OUT%\" >nul
|
||||
copy "%ROOT%\deployment\Install-BizTalkCheckmkPulse.ps1" "%OUT%\" >nul
|
||||
copy "%ROOT%\deployment\Uninstall-BizTalkCheckmkPulse.ps1" "%OUT%\" >nul
|
||||
copy "%ROOT%\src\BizTalkCheckmkPulse\bin\Release\BizTalkCheckmkPulse.exe" "%APP%\" >nul
|
||||
copy "%ROOT%\src\BizTalkCheckmkPulse\bin\Release\BizTalkCheckmkPulse.exe.config" "%APP%\" >nul
|
||||
|
||||
echo Deployment package written to %OUT%
|
||||
exit /b 0
|
||||
|
||||
|
||||
@@ -6,8 +6,16 @@
|
||||
<add key="ServicePrefix" value="BizTalk" />
|
||||
<add key="EnvironmentName" value="" />
|
||||
|
||||
<!-- Provider/Consumer-Datei. Der Scheduled Task schreibt, LocalSystem liest. -->
|
||||
<add key="SnapshotPath" value="%ProgramData%\BizTalkCheckmkPulse\data\biztalk-checkmk-pulse.snapshot" />
|
||||
<!-- Bei minuetlicher Provider-Ausfuehrung nach drei Minuten als UNKNOWN bewerten. -->
|
||||
<add key="SnapshotMaxAgeSeconds" value="180" />
|
||||
<add key="SnapshotMaxBytes" value="1048576" />
|
||||
<add key="LogDirectory" value="%ProgramData%\BizTalkCheckmkPulse\logs" />
|
||||
<add key="LogRetentionDays" value="30" />
|
||||
|
||||
<add key="QueryTimeoutSeconds" value="25" />
|
||||
<!-- Testet integrierte Windows-Anmeldung an den per WMI ermittelten BizTalk-Datenbanken. -->
|
||||
<!-- Testet die Anmeldung des privilegierten Provider-Kontos an den ermittelten BizTalk-Datenbanken. -->
|
||||
<add key="ProbeSqlConnectivity" value="true" />
|
||||
<add key="SqlConnectionTimeoutSeconds" value="5" />
|
||||
<add key="WarnResumableThreshold" value="1" />
|
||||
|
||||
@@ -43,10 +43,12 @@
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="CheckmkLocalFormatter.cs" />
|
||||
<Compile Include="EventLogProbe.cs" />
|
||||
<Compile Include="FileLogger.cs" />
|
||||
<Compile Include="MonitoringOptions.cs" />
|
||||
<Compile Include="Models.cs" />
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="SqlConnectivityProbe.cs" />
|
||||
<Compile Include="SnapshotStore.cs" />
|
||||
<Compile Include="WmiBizTalkProbe.cs" />
|
||||
<Compile Include="WmiHelpers.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -83,6 +83,22 @@ namespace BizTalkCheckmkPulse
|
||||
yield return BuildLine(CheckState.Unknown, _options.ServiceName("Event Log"), "-", action);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Liefert stabile UNKNOWN-Services, wenn der Consumer keinen gueltigen Provider-Snapshot lesen kann.
|
||||
/// </summary>
|
||||
public IEnumerable<string> FormatSnapshotFailure(string reason)
|
||||
{
|
||||
var detail = "Privilegierter BizTalk-Datensnapshot nicht verfuegbar: "
|
||||
+ SanitizeDetail(reason)
|
||||
+ " Massnahme: Scheduled Task 'BizTalk Checkmk Pulse Provider', Provider-Log, Snapshot-Alter und ACL pruefen.";
|
||||
yield return BuildLine(CheckState.Unknown, _options.ServiceName("Platform"), "-", detail);
|
||||
yield return BuildLine(CheckState.Unknown, _options.ServiceName("SQL Access"), "-", detail);
|
||||
yield return BuildLine(CheckState.Unknown, _options.ServiceName("Suspended Instances"), "-", detail);
|
||||
yield return BuildLine(CheckState.Unknown, _options.ServiceName("Host Instances"), "-", detail);
|
||||
yield return BuildLine(CheckState.Unknown, _options.ServiceName("Runtime Artifacts"), "-", detail);
|
||||
yield return BuildLine(CheckState.Unknown, _options.ServiceName("Event Log"), "-", detail);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Formatiert Erreichbarkeit und Basisdaten des BizTalk-WMI-Providers.
|
||||
/// </summary>
|
||||
@@ -96,6 +112,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, "read_only_group", result.Platform.ReadOnlyUserGroup);
|
||||
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));
|
||||
@@ -307,7 +324,7 @@ namespace BizTalkCheckmkPulse
|
||||
CheckState.Unknown,
|
||||
_options.ServiceName("Event Log"),
|
||||
"-",
|
||||
"Application Event Log konnte nicht gelesen werden: " + result.EventLog.Failure + " Massnahme: lokalen Event-Log-Zugriff des Checkmk-Agentkontos und den Windows Event Log Dienst pruefen.");
|
||||
"Application Event Log konnte nicht gelesen werden: " + result.EventLog.Failure + " Massnahme: lokalen Event-Log-Zugriff des Provider-Kontos und den Windows Event Log Dienst pruefen.");
|
||||
}
|
||||
|
||||
var state = _options.EventLogCritThreshold > 0 && result.EventLog.Errors >= _options.EventLogCritThreshold
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Security.Principal;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace BizTalkCheckmkPulse
|
||||
{
|
||||
/// <summary>
|
||||
/// Kleine, ausfallsichere Tagesdatei-Protokollierung ohne externe Abhaengigkeiten.
|
||||
/// Logging-Fehler duerfen weder Provider noch Checkmk-Ausgabe blockieren.
|
||||
/// </summary>
|
||||
internal sealed class FileLogger
|
||||
{
|
||||
private readonly string _directory;
|
||||
private readonly int _retentionDays;
|
||||
private readonly string _component;
|
||||
|
||||
public FileLogger(string directory, int retentionDays, string component)
|
||||
{
|
||||
_directory = directory;
|
||||
_retentionDays = retentionDays;
|
||||
_component = string.IsNullOrWhiteSpace(component) ? "application" : component;
|
||||
}
|
||||
|
||||
public void Info(string message)
|
||||
{
|
||||
Write("INFO", message, null);
|
||||
}
|
||||
|
||||
public void Warning(string message)
|
||||
{
|
||||
Write("WARN", message, null);
|
||||
}
|
||||
|
||||
public void Error(string message, Exception exception)
|
||||
{
|
||||
Write("ERROR", message, exception);
|
||||
}
|
||||
|
||||
public void Prune()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!Directory.Exists(_directory))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var cutoff = DateTime.UtcNow.Date.AddDays(-_retentionDays);
|
||||
foreach (var file in Directory.GetFiles(_directory, "biztalk-checkmk-pulse-*.log"))
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.GetLastWriteTimeUtc(file) < cutoff)
|
||||
{
|
||||
File.Delete(file);
|
||||
}
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private void Write(string level, string message, Exception exception)
|
||||
{
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(_directory);
|
||||
var path = Path.Combine(
|
||||
_directory,
|
||||
"biztalk-checkmk-pulse-" + DateTime.UtcNow.ToString("yyyyMMdd", CultureInfo.InvariantCulture) + ".log");
|
||||
var line = new StringBuilder()
|
||||
.Append(DateTime.UtcNow.ToString("o", CultureInfo.InvariantCulture))
|
||||
.Append(" level=").Append(level)
|
||||
.Append(" component=").Append(_component)
|
||||
.Append(" pid=").Append(System.Diagnostics.Process.GetCurrentProcess().Id)
|
||||
.Append(" identity=").Append(CurrentIdentity())
|
||||
.Append(" message=").Append(SingleLine(message));
|
||||
|
||||
if (exception != null)
|
||||
{
|
||||
line.Append(" exception=").Append(SingleLine(exception.ToString()));
|
||||
}
|
||||
|
||||
line.Append(Environment.NewLine);
|
||||
var bytes = new UTF8Encoding(false).GetBytes(line.ToString());
|
||||
for (var attempt = 0; attempt < 3; attempt++)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var stream = new FileStream(path, FileMode.Append, FileAccess.Write, FileShare.Read))
|
||||
{
|
||||
stream.Write(bytes, 0, bytes.Length);
|
||||
stream.Flush();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
if (attempt == 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Thread.Sleep(25 * (attempt + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Logging bleibt bewusst best effort.
|
||||
}
|
||||
}
|
||||
|
||||
private static string CurrentIdentity()
|
||||
{
|
||||
try
|
||||
{
|
||||
var identity = WindowsIdentity.GetCurrent();
|
||||
return identity == null ? "(unknown)" : SingleLine(identity.Name);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return "(unknown)";
|
||||
}
|
||||
}
|
||||
|
||||
private static string SingleLine(string value)
|
||||
{
|
||||
return (value ?? string.Empty)
|
||||
.Replace("\r", "\\r")
|
||||
.Replace("\n", "\\n")
|
||||
.Replace("\t", " ");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -125,6 +125,7 @@ namespace BizTalkCheckmkPulse
|
||||
public string ServerName { get; set; }
|
||||
public string GroupName { get; set; }
|
||||
public string OperatorGroup { get; set; }
|
||||
public string ReadOnlyUserGroup { get; set; }
|
||||
public string ManagementDbServer { get; set; }
|
||||
public string ManagementDbName { get; set; }
|
||||
public string MessageBoxDbServer { get; set; }
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace BizTalkCheckmkPulse
|
||||
@@ -27,6 +28,12 @@ namespace BizTalkCheckmkPulse
|
||||
public int EventLogWarnThreshold { get; set; }
|
||||
public int EventLogCritThreshold { get; set; }
|
||||
public IReadOnlyList<string> EventLogSources { get; set; }
|
||||
public string SnapshotPath { get; set; }
|
||||
public int SnapshotMaxAgeSeconds { get; set; }
|
||||
public int SnapshotMaxBytes { get; set; }
|
||||
public string LogDirectory { get; set; }
|
||||
public int LogRetentionDays { get; set; }
|
||||
public bool Collect { get; set; }
|
||||
public bool SelfTest { get; set; }
|
||||
|
||||
/// <summary>
|
||||
@@ -50,6 +57,12 @@ namespace BizTalkCheckmkPulse
|
||||
EventLogWarnThreshold = 1;
|
||||
EventLogCritThreshold = 10;
|
||||
EventLogSources = new[] { "BizTalk Server", "XLANG/s", "ENTSSO", "BizTalk Server Application", "BizTalk Server EDI" };
|
||||
var commonData = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
|
||||
SnapshotPath = Path.Combine(commonData, "BizTalkCheckmkPulse", "data", "biztalk-checkmk-pulse.snapshot");
|
||||
SnapshotMaxAgeSeconds = 180;
|
||||
SnapshotMaxBytes = 1048576;
|
||||
LogDirectory = Path.Combine(commonData, "BizTalkCheckmkPulse", "logs");
|
||||
LogRetentionDays = 30;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -91,8 +104,17 @@ namespace BizTalkCheckmkPulse
|
||||
options.EventLogWarnThreshold = ReadInt(settings, "EventLogWarnThreshold", options.EventLogWarnThreshold, 0, 1000000);
|
||||
options.EventLogCritThreshold = ReadInt(settings, "EventLogCritThreshold", options.EventLogCritThreshold, 0, 1000000);
|
||||
options.EventLogSources = ReadSources(ReadString(settings, "EventLogSources", string.Join("|", options.EventLogSources)));
|
||||
options.SnapshotPath = Environment.ExpandEnvironmentVariables(ReadString(settings, "SnapshotPath", options.SnapshotPath));
|
||||
options.SnapshotMaxAgeSeconds = ReadInt(settings, "SnapshotMaxAgeSeconds", options.SnapshotMaxAgeSeconds, 60, 86400);
|
||||
options.SnapshotMaxBytes = ReadInt(settings, "SnapshotMaxBytes", options.SnapshotMaxBytes, 4096, 16777216);
|
||||
options.LogDirectory = Environment.ExpandEnvironmentVariables(ReadString(settings, "LogDirectory", options.LogDirectory));
|
||||
options.LogRetentionDays = ReadInt(settings, "LogRetentionDays", options.LogRetentionDays, 1, 365);
|
||||
|
||||
ApplyArguments(options, args ?? new string[0]);
|
||||
if (!options.SelfTest)
|
||||
{
|
||||
Validate(options);
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
@@ -110,6 +132,14 @@ namespace BizTalkCheckmkPulse
|
||||
{
|
||||
options.SelfTest = true;
|
||||
}
|
||||
else if (EqualsAny(arg, "--collect", "/collect"))
|
||||
{
|
||||
options.Collect = true;
|
||||
}
|
||||
else if (EqualsAny(arg, "--consume", "/consume"))
|
||||
{
|
||||
options.Collect = false;
|
||||
}
|
||||
else if (EqualsAny(arg, "--server", "/server") && i + 1 < args.Length)
|
||||
{
|
||||
options.Server = args[++i];
|
||||
@@ -133,6 +163,22 @@ namespace BizTalkCheckmkPulse
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verhindert unsichere oder mehrdeutige Laufzeitpfade.
|
||||
/// </summary>
|
||||
private static void Validate(MonitoringOptions options)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(options.SnapshotPath) || !Path.IsPathRooted(options.SnapshotPath))
|
||||
{
|
||||
throw new ConfigurationErrorsException("SnapshotPath must be an absolute path.");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(options.LogDirectory) || !Path.IsPathRooted(options.LogDirectory))
|
||||
{
|
||||
throw new ConfigurationErrorsException("LogDirectory must be an absolute path.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Liest einen getrimmten Konfigurationswert.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Principal;
|
||||
using System.Text;
|
||||
|
||||
namespace BizTalkCheckmkPulse
|
||||
{
|
||||
/// <summary>
|
||||
/// Einstiegspunkt des Checkmk Local Checks.
|
||||
/// Einstiegspunkt fuer privilegierten Provider und unprivilegierten Checkmk-Consumer.
|
||||
/// </summary>
|
||||
internal static class Program
|
||||
{
|
||||
/// <summary>
|
||||
/// Laedt die Konfiguration, fuehrt die Probes aus und schreibt Checkmk-Zeilen nach STDOUT.
|
||||
/// Laedt die Konfiguration und fuehrt je nach Modus Provider oder Snapshot-Consumer aus.
|
||||
/// </summary>
|
||||
/// <param name="args">Kommandozeilenargumente wie <c>--self-test</c> oder <c>--environment</c>.</param>
|
||||
/// <returns>Immer 0, damit Diagnosefehler als UNKNOWN-Service statt als kaputte Agent-Sektion erscheinen.</returns>
|
||||
/// <returns>Consumer immer 0; Provider 0 bei Erfolg und ungleich 0 bei einem Laufzeitfehler.</returns>
|
||||
private static int Main(string[] args)
|
||||
{
|
||||
MonitoringOptions options = null;
|
||||
@@ -34,38 +37,151 @@ namespace BizTalkCheckmkPulse
|
||||
return 0;
|
||||
}
|
||||
|
||||
var result = new ProbeResult();
|
||||
var wmiProbe = new WmiBizTalkProbe(options);
|
||||
wmiProbe.Query(result);
|
||||
|
||||
// Der SQL-Test verwendet bewusst dieselbe Windows-Identitaet wie der Checkmk-Agent.
|
||||
var sqlProbe = new SqlConnectivityProbe(options);
|
||||
sqlProbe.Query(result);
|
||||
|
||||
if (options.ProbeEventLog)
|
||||
{
|
||||
var eventLogProbe = new EventLogProbe(options);
|
||||
eventLogProbe.Query(result);
|
||||
}
|
||||
|
||||
foreach (var line in formatter.Format(result))
|
||||
{
|
||||
Console.WriteLine(line);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return options.Collect
|
||||
? RunProvider(options, formatter)
|
||||
: RunConsumer(options, formatter);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var fallbackOptions = options ?? new MonitoringOptions();
|
||||
var formatter = new CheckmkLocalFormatter(fallbackOptions);
|
||||
foreach (var line in formatter.FormatFatal("BizTalk Checkmk Pulse failed: " + ex.GetType().Name + ": " + ex.Message))
|
||||
foreach (var line in formatter.FormatSnapshotFailure(
|
||||
"Programmstart fehlgeschlagen: " + ex.GetType().Name + ": " + ex.Message))
|
||||
{
|
||||
Console.WriteLine(line);
|
||||
}
|
||||
|
||||
return options != null && options.Collect ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static int RunProvider(MonitoringOptions options, CheckmkLocalFormatter formatter)
|
||||
{
|
||||
var logger = new FileLogger(options.LogDirectory, options.LogRetentionDays, "provider");
|
||||
logger.Prune();
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
var lockPath = options.SnapshotPath + ".provider.lock";
|
||||
|
||||
try
|
||||
{
|
||||
EnsureProviderIdentity();
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(options.SnapshotPath));
|
||||
using (new FileStream(lockPath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None))
|
||||
{
|
||||
logger.Info("Collection started. snapshot=" + options.SnapshotPath);
|
||||
var result = Collect(options);
|
||||
var lines = formatter.Format(result).ToArray();
|
||||
new SnapshotStore(options.SnapshotPath, options.SnapshotMaxBytes)
|
||||
.Write(lines, DateTime.UtcNow, CurrentIdentity());
|
||||
stopwatch.Stop();
|
||||
logger.Info(
|
||||
"Collection completed. lines="
|
||||
+ lines.Length
|
||||
+ " diagnostics="
|
||||
+ result.Diagnostics.Count
|
||||
+ " elapsed_ms="
|
||||
+ stopwatch.ElapsedMilliseconds);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
logger.Warning("Collection skipped or snapshot I/O failed: " + ex.Message);
|
||||
return 2;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
stopwatch.Stop();
|
||||
logger.Error("Collection failed after " + stopwatch.ElapsedMilliseconds + " ms.", ex);
|
||||
TryWriteFatalSnapshot(options, formatter, ex, logger);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static void EnsureProviderIdentity()
|
||||
{
|
||||
using (var identity = WindowsIdentity.GetCurrent())
|
||||
{
|
||||
if (identity != null
|
||||
&& identity.User != null
|
||||
&& identity.User.IsWellKnown(WellKnownSidType.LocalSystemSid))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Provider mode must not run as LocalSystem. Configure the dedicated Scheduled Task account.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int RunConsumer(MonitoringOptions options, CheckmkLocalFormatter formatter)
|
||||
{
|
||||
var snapshot = new SnapshotStore(options.SnapshotPath, options.SnapshotMaxBytes)
|
||||
.Read(DateTime.UtcNow, TimeSpan.FromSeconds(options.SnapshotMaxAgeSeconds));
|
||||
if (snapshot.IsSuccess)
|
||||
{
|
||||
foreach (var line in snapshot.Lines)
|
||||
{
|
||||
Console.WriteLine(line);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
new FileLogger(options.LogDirectory, options.LogRetentionDays, "consumer")
|
||||
.Warning("Snapshot rejected. reason=" + snapshot.Error + " path=" + options.SnapshotPath);
|
||||
foreach (var line in formatter.FormatSnapshotFailure(snapshot.Error))
|
||||
{
|
||||
Console.WriteLine(line);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static ProbeResult Collect(MonitoringOptions options)
|
||||
{
|
||||
var result = new ProbeResult();
|
||||
new WmiBizTalkProbe(options).Query(result);
|
||||
new SqlConnectivityProbe(options).Query(result);
|
||||
|
||||
if (options.ProbeEventLog)
|
||||
{
|
||||
new EventLogProbe(options).Query(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void TryWriteFatalSnapshot(
|
||||
MonitoringOptions options,
|
||||
CheckmkLocalFormatter formatter,
|
||||
Exception exception,
|
||||
FileLogger logger)
|
||||
{
|
||||
try
|
||||
{
|
||||
var lines = formatter
|
||||
.FormatFatal("Privilegierter Datenprovider fehlgeschlagen: " + exception.GetType().Name + ": " + exception.Message)
|
||||
.ToArray();
|
||||
new SnapshotStore(options.SnapshotPath, options.SnapshotMaxBytes)
|
||||
.Write(lines, DateTime.UtcNow, CurrentIdentity());
|
||||
logger.Warning("A current UNKNOWN snapshot was written after the provider failure.");
|
||||
}
|
||||
catch (Exception snapshotException)
|
||||
{
|
||||
logger.Error("Fatal UNKNOWN snapshot could not be written.", snapshotException);
|
||||
}
|
||||
}
|
||||
|
||||
private static string CurrentIdentity()
|
||||
{
|
||||
try
|
||||
{
|
||||
var identity = WindowsIdentity.GetCurrent();
|
||||
return identity == null ? "(unknown)" : identity.Name;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return "(unknown)";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Reflection;
|
||||
|
||||
[assembly: InternalsVisibleTo("BizTalkCheckmkPulse.Tests")]
|
||||
[assembly: AssemblyTitle("BizTalk Checkmk Pulse")]
|
||||
[assembly: AssemblyDescription("Privileged BizTalk data provider and validated Checkmk snapshot consumer")]
|
||||
[assembly: AssemblyCompany("BEW")]
|
||||
[assembly: AssemblyProduct("BizTalk Checkmk Pulse")]
|
||||
[assembly: AssemblyVersion("2.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("2.0.0.0")]
|
||||
|
||||
@@ -0,0 +1,353 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace BizTalkCheckmkPulse
|
||||
{
|
||||
/// <summary>
|
||||
/// Schreibt und liest einen atomaren, integritaetsgeschuetzten Checkmk-Snapshot.
|
||||
/// </summary>
|
||||
internal sealed class SnapshotStore
|
||||
{
|
||||
internal const string Magic = "BIZTALK_CHECKMK_PULSE_SNAPSHOT_V1";
|
||||
private static readonly UTF8Encoding StrictUtf8 = new UTF8Encoding(false, true);
|
||||
private readonly string _path;
|
||||
private readonly int _maxBytes;
|
||||
|
||||
public SnapshotStore(string path, int maxBytes)
|
||||
{
|
||||
_path = path;
|
||||
_maxBytes = maxBytes;
|
||||
}
|
||||
|
||||
public void Write(IReadOnlyCollection<string> lines, DateTime generatedUtc, string identity)
|
||||
{
|
||||
ValidatePayload(lines);
|
||||
var payload = string.Join("\n", lines) + "\n";
|
||||
var payloadBytes = StrictUtf8.GetBytes(payload);
|
||||
var content = BuildHeader(generatedUtc, identity, lines.Count, Hash(payloadBytes)) + payload;
|
||||
var contentBytes = StrictUtf8.GetBytes(content);
|
||||
if (contentBytes.Length > _maxBytes)
|
||||
{
|
||||
throw new InvalidDataException("Snapshot exceeds configured SnapshotMaxBytes.");
|
||||
}
|
||||
|
||||
var directory = Path.GetDirectoryName(_path);
|
||||
if (string.IsNullOrWhiteSpace(directory))
|
||||
{
|
||||
throw new InvalidOperationException("Snapshot path has no parent directory.");
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(directory);
|
||||
var temporaryPath = Path.Combine(
|
||||
directory,
|
||||
Path.GetFileName(_path) + "." + Process.GetCurrentProcess().Id.ToString(CultureInfo.InvariantCulture) + "." + Guid.NewGuid().ToString("N") + ".tmp");
|
||||
|
||||
try
|
||||
{
|
||||
using (var stream = new FileStream(
|
||||
temporaryPath,
|
||||
FileMode.CreateNew,
|
||||
FileAccess.Write,
|
||||
FileShare.None,
|
||||
4096,
|
||||
FileOptions.WriteThrough))
|
||||
{
|
||||
stream.Write(contentBytes, 0, contentBytes.Length);
|
||||
stream.Flush(true);
|
||||
}
|
||||
|
||||
if (File.Exists(_path))
|
||||
{
|
||||
File.Replace(temporaryPath, _path, null, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
File.Move(temporaryPath, _path);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(temporaryPath))
|
||||
{
|
||||
File.Delete(temporaryPath);
|
||||
}
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public SnapshotReadResult Read(DateTime utcNow, TimeSpan maximumAge)
|
||||
{
|
||||
try
|
||||
{
|
||||
var bytes = ReadBytesWithRetry();
|
||||
var text = StrictUtf8.GetString(bytes).Replace("\r\n", "\n");
|
||||
var separator = text.IndexOf("\n\n", StringComparison.Ordinal);
|
||||
if (separator < 0)
|
||||
{
|
||||
return SnapshotReadResult.Failed("Snapshot header separator is missing.");
|
||||
}
|
||||
|
||||
var headerLines = text.Substring(0, separator).Split('\n');
|
||||
if (headerLines.Length != 6 || !string.Equals(headerLines[0], Magic, StringComparison.Ordinal))
|
||||
{
|
||||
return SnapshotReadResult.Failed("Snapshot format or version is invalid.");
|
||||
}
|
||||
|
||||
DateTime generatedUtc;
|
||||
int expectedLineCount;
|
||||
if (!TryReadDate(headerLines[1], "generatedUtc=", out generatedUtc)
|
||||
|| !TryReadInt(headerLines[4], "payloadLines=", out expectedLineCount))
|
||||
{
|
||||
return SnapshotReadResult.Failed("Snapshot metadata is invalid.");
|
||||
}
|
||||
|
||||
var expectedMachine = DecodeHeader(headerLines[2], "machineBase64=");
|
||||
DecodeHeader(headerLines[3], "identityBase64=");
|
||||
var expectedHash = ReadHeaderValue(headerLines[5], "payloadSha256=");
|
||||
if (!string.Equals(expectedMachine, Environment.MachineName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return SnapshotReadResult.Failed("Snapshot was created for a different machine.");
|
||||
}
|
||||
|
||||
var payload = text.Substring(separator + 2);
|
||||
if (!payload.EndsWith("\n", StringComparison.Ordinal)
|
||||
|| payload.IndexOf("\n\n", StringComparison.Ordinal) >= 0)
|
||||
{
|
||||
return SnapshotReadResult.Failed("Snapshot payload framing is invalid.");
|
||||
}
|
||||
|
||||
var payloadBytes = StrictUtf8.GetBytes(payload);
|
||||
if (!FixedTimeEquals(expectedHash, Hash(payloadBytes)))
|
||||
{
|
||||
return SnapshotReadResult.Failed("Snapshot SHA-256 validation failed.");
|
||||
}
|
||||
|
||||
var lines = payload
|
||||
.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.ToArray();
|
||||
if (lines.Length != expectedLineCount)
|
||||
{
|
||||
return SnapshotReadResult.Failed("Snapshot payload line count is invalid.");
|
||||
}
|
||||
|
||||
ValidatePayload(lines);
|
||||
var age = utcNow - generatedUtc;
|
||||
if (age < TimeSpan.FromMinutes(-5))
|
||||
{
|
||||
return SnapshotReadResult.Failed("Snapshot timestamp is too far in the future.");
|
||||
}
|
||||
|
||||
if (age > maximumAge)
|
||||
{
|
||||
return SnapshotReadResult.Failed(
|
||||
"Snapshot is stale: age="
|
||||
+ Math.Floor(age.TotalSeconds).ToString(CultureInfo.InvariantCulture)
|
||||
+ "s, maximum="
|
||||
+ Math.Floor(maximumAge.TotalSeconds).ToString(CultureInfo.InvariantCulture)
|
||||
+ "s.");
|
||||
}
|
||||
|
||||
return SnapshotReadResult.Success(lines, generatedUtc);
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
return SnapshotReadResult.Failed("Snapshot file does not exist.");
|
||||
}
|
||||
catch (DirectoryNotFoundException)
|
||||
{
|
||||
return SnapshotReadResult.Failed("Snapshot directory does not exist.");
|
||||
}
|
||||
catch (UnauthorizedAccessException ex)
|
||||
{
|
||||
return SnapshotReadResult.Failed("Snapshot cannot be read: " + ex.Message);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
return SnapshotReadResult.Failed("Snapshot I/O failed: " + ex.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return SnapshotReadResult.Failed("Snapshot validation failed: " + ex.GetType().Name + ": " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] ReadBytesWithRetry()
|
||||
{
|
||||
for (var attempt = 0; ; attempt++)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var stream = new FileStream(_path, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete))
|
||||
{
|
||||
if (stream.Length <= 0 || stream.Length > _maxBytes)
|
||||
{
|
||||
throw new InvalidDataException("Snapshot size is outside the allowed range.");
|
||||
}
|
||||
|
||||
var bytes = new byte[(int)stream.Length];
|
||||
var offset = 0;
|
||||
while (offset < bytes.Length)
|
||||
{
|
||||
var read = stream.Read(bytes, offset, bytes.Length - offset);
|
||||
if (read == 0)
|
||||
{
|
||||
throw new EndOfStreamException("Unexpected end of snapshot.");
|
||||
}
|
||||
|
||||
offset += read;
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
if (attempt >= 2)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
Thread.Sleep(25 * (attempt + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string BuildHeader(DateTime generatedUtc, string identity, int lineCount, string hash)
|
||||
{
|
||||
return Magic + "\n"
|
||||
+ "generatedUtc=" + generatedUtc.ToUniversalTime().ToString("o", CultureInfo.InvariantCulture) + "\n"
|
||||
+ "machineBase64=" + Convert.ToBase64String(StrictUtf8.GetBytes(Environment.MachineName)) + "\n"
|
||||
+ "identityBase64=" + Convert.ToBase64String(StrictUtf8.GetBytes(identity ?? string.Empty)) + "\n"
|
||||
+ "payloadLines=" + lineCount.ToString(CultureInfo.InvariantCulture) + "\n"
|
||||
+ "payloadSha256=" + hash + "\n\n";
|
||||
}
|
||||
|
||||
private static void ValidatePayload(IEnumerable<string> lines)
|
||||
{
|
||||
if (lines == null)
|
||||
{
|
||||
throw new ArgumentNullException("lines");
|
||||
}
|
||||
|
||||
var count = 0;
|
||||
foreach (var line in lines)
|
||||
{
|
||||
count++;
|
||||
if (string.IsNullOrWhiteSpace(line)
|
||||
|| line.IndexOf('\r') >= 0
|
||||
|| line.IndexOf('\n') >= 0
|
||||
|| line.Length < 5
|
||||
|| line[1] != ' '
|
||||
|| line[2] != '"'
|
||||
|| line[0] < '0'
|
||||
|| line[0] > '3')
|
||||
{
|
||||
throw new InvalidDataException("Snapshot contains an invalid Checkmk local-check line.");
|
||||
}
|
||||
}
|
||||
|
||||
if (count < 6)
|
||||
{
|
||||
throw new InvalidDataException("Snapshot must contain all six stable services.");
|
||||
}
|
||||
}
|
||||
|
||||
private static string Hash(byte[] bytes)
|
||||
{
|
||||
using (var sha = SHA256.Create())
|
||||
{
|
||||
return string.Concat(sha.ComputeHash(bytes).Select(x => x.ToString("x2", CultureInfo.InvariantCulture)));
|
||||
}
|
||||
}
|
||||
|
||||
private static bool FixedTimeEquals(string left, string right)
|
||||
{
|
||||
if (left == null || right == null || left.Length != right.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var difference = 0;
|
||||
for (var i = 0; i < left.Length; i++)
|
||||
{
|
||||
difference |= left[i] ^ right[i];
|
||||
}
|
||||
|
||||
return difference == 0;
|
||||
}
|
||||
|
||||
private static string ReadHeaderValue(string line, string prefix)
|
||||
{
|
||||
if (line == null || !line.StartsWith(prefix, StringComparison.Ordinal))
|
||||
{
|
||||
throw new InvalidDataException("Missing snapshot header " + prefix);
|
||||
}
|
||||
|
||||
return line.Substring(prefix.Length);
|
||||
}
|
||||
|
||||
private static string DecodeHeader(string line, string prefix)
|
||||
{
|
||||
return StrictUtf8.GetString(Convert.FromBase64String(ReadHeaderValue(line, prefix)));
|
||||
}
|
||||
|
||||
private static bool TryReadDate(string line, string prefix, out DateTime result)
|
||||
{
|
||||
return DateTime.TryParseExact(
|
||||
ReadHeaderValue(line, prefix),
|
||||
"o",
|
||||
CultureInfo.InvariantCulture,
|
||||
DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal,
|
||||
out result);
|
||||
}
|
||||
|
||||
private static bool TryReadInt(string line, string prefix, out int result)
|
||||
{
|
||||
return int.TryParse(ReadHeaderValue(line, prefix), NumberStyles.Integer, CultureInfo.InvariantCulture, out result);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class SnapshotReadResult
|
||||
{
|
||||
private SnapshotReadResult()
|
||||
{
|
||||
Lines = new string[0];
|
||||
}
|
||||
|
||||
public bool IsSuccess { get; private set; }
|
||||
public string Error { get; private set; }
|
||||
public IReadOnlyList<string> Lines { get; private set; }
|
||||
public DateTime GeneratedUtc { get; private set; }
|
||||
|
||||
public static SnapshotReadResult Success(IReadOnlyList<string> lines, DateTime generatedUtc)
|
||||
{
|
||||
return new SnapshotReadResult
|
||||
{
|
||||
IsSuccess = true,
|
||||
Lines = lines,
|
||||
GeneratedUtc = generatedUtc
|
||||
};
|
||||
}
|
||||
|
||||
public static SnapshotReadResult Failed(string error)
|
||||
{
|
||||
return new SnapshotReadResult
|
||||
{
|
||||
IsSuccess = false,
|
||||
Error = error
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,8 +56,8 @@ namespace BizTalkCheckmkPulse
|
||||
? "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."
|
||||
? "Zuerst die Wmi/Permission-Diagnose sowie Provider-Konto, konfigurierte BizTalk-Read-Only-Gruppe und BTS_READONLY_USERS pruefen."
|
||||
: "Zuerst den Service 'BizTalk Platform' und MSBTS_GroupSetting im Provider-Log pruefen. Danach den Scheduled Task erneut starten."
|
||||
});
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ namespace BizTalkCheckmkPulse
|
||||
Component = "Windows identity",
|
||||
Required = false,
|
||||
Summary = "Die Windows-Ausfuehrungsidentitaet konnte nicht bestimmt werden.",
|
||||
Action = "Agent-Dienstkonto mit 'sc.exe qc CheckMKService' beziehungsweise in services.msc pruefen.",
|
||||
Action = "Konto des Scheduled Tasks 'BizTalk Checkmk Pulse Provider' pruefen.",
|
||||
TechnicalDetails = ex.GetType().Name + ": " + ex.Message
|
||||
});
|
||||
}
|
||||
@@ -268,7 +268,7 @@ namespace BizTalkCheckmkPulse
|
||||
case DiagnosticCategory.Timeout:
|
||||
return "SQL-Verbindungsaufbau oder Testabfrage hat das konfigurierte Zeitlimit ueberschritten.";
|
||||
case DiagnosticCategory.Connectivity:
|
||||
return "SQL Server oder die konfigurierte SQL-Instanz ist aus dem Checkmk-Agent-Kontext nicht erreichbar.";
|
||||
return "SQL Server oder die konfigurierte SQL-Instanz ist aus dem Provider-Kontext nicht erreichbar.";
|
||||
case DiagnosticCategory.Configuration:
|
||||
return "SQL-Verbindung scheitert an TLS-, Zertifikats-, SPN- oder SSPI-Konfiguration.";
|
||||
default:
|
||||
@@ -288,7 +288,7 @@ namespace BizTalkCheckmkPulse
|
||||
switch (category)
|
||||
{
|
||||
case DiagnosticCategory.Permission:
|
||||
return networkIdentity + " in die konfigurierte BizTalk-Operator-Gruppe aufnehmen; keine direkten BizTalk-DB-Rollen vergeben. Danach Kerberos-Tickets erneuern und den Agent-Dump wiederholen.";
|
||||
return "Provider-Konto " + networkIdentity + " und dessen Mitgliedschaft in der konfigurierten BizTalk-Read-Only-Gruppe sowie BTS_READONLY_USERS pruefen; keine direkten BizTalk-DB-Rollen vergeben. Danach den Scheduled Task mit neuem Anmeldetoken starten.";
|
||||
case DiagnosticCategory.Timeout:
|
||||
return "Netzwerkpfad, DNS, SQL-Port, Firewall und Auslastung fuer " + target.Server + " pruefen; Timeout nur nach Ursachenanalyse erhoehen.";
|
||||
case DiagnosticCategory.Connectivity:
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace BizTalkCheckmkPulse
|
||||
private const int OrchestrationStarted = 4;
|
||||
private const int EAccessDenied = unchecked((int)0x80070005);
|
||||
internal const string GroupSettingQuery =
|
||||
"SELECT Name, BizTalkOperatorGroup, MgmtDbServerName, MgmtDbName, SubscriptionDBServerName, SubscriptionDBName FROM MSBTS_GroupSetting";
|
||||
"SELECT Name, BizTalkOperatorGroup, BizTalkReadOnlyUserGroup, MgmtDbServerName, MgmtDbName, SubscriptionDBServerName, SubscriptionDBName FROM MSBTS_GroupSetting";
|
||||
private readonly MonitoringOptions _options;
|
||||
|
||||
/// <summary>
|
||||
@@ -106,6 +106,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.ReadOnlyUserGroup = WmiHelpers.GetString(item, "BizTalkReadOnlyUserGroup");
|
||||
result.Platform.ManagementDbServer = WmiHelpers.GetString(item, "MgmtDbServerName");
|
||||
result.Platform.ManagementDbName = WmiHelpers.GetString(item, "MgmtDbName");
|
||||
result.Platform.MessageBoxDbServer = WmiHelpers.GetString(item, "SubscriptionDBServerName");
|
||||
@@ -672,7 +673,7 @@ namespace BizTalkCheckmkPulse
|
||||
return "BizTalk-WMI-Provider konnte den SQL-Zugriff fuer " + sqlLoginPrincipal + " nicht anmelden.";
|
||||
}
|
||||
|
||||
return "Zugriff auf " + component + " wurde im LocalSystem-Kontext verweigert.";
|
||||
return "Zugriff des privilegierten Provider-Kontos auf " + component + " wurde verweigert.";
|
||||
case DiagnosticCategory.Configuration:
|
||||
return connectionFailure
|
||||
? "BizTalk-WMI-Namespace ist nicht vorhanden oder nicht korrekt registriert."
|
||||
@@ -704,13 +705,13 @@ namespace BizTalkCheckmkPulse
|
||||
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 "Provider-Konto " + sqlLoginPrincipal
|
||||
+ " und dessen Mitgliedschaft in der konfigurierten BizTalk-Read-Only-Gruppe sowie BTS_READONLY_USERS pruefen; 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.";
|
||||
? "Lokalen Namespace-Zugriff des Scheduled-Task-Kontos auf root\\MicrosoftBizTalkServer gezielt pruefen; keine pauschalen WMI-Rechte vergeben."
|
||||
: "Provider-Identitaet und Mitgliedschaft in der konfigurierten BizTalk-Read-Only-Gruppe pruefen; Operator-Rechte nur nach klassenspezifischer Analyse erwaegen.";
|
||||
case DiagnosticCategory.Configuration:
|
||||
return "BizTalk-WMI-Provider/Namespace auf dem BizTalk-Server pruefen und gegebenenfalls mit dem BizTalk-Setup reparieren.";
|
||||
case DiagnosticCategory.Schema:
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace BizTalkCheckmkPulse.Tests
|
||||
{
|
||||
@@ -21,6 +23,12 @@ namespace BizTalkCheckmkPulse.Tests
|
||||
Run("ProviderSqlLoginFailureIsPermission", ProviderSqlLoginFailureIsPermission);
|
||||
Run("RejectedSqlPrincipalIsExtracted", RejectedSqlPrincipalIsExtracted);
|
||||
Run("PlatformPermissionPropagatesToSqlDiscovery", PlatformPermissionPropagatesToSqlDiscovery);
|
||||
Run("SnapshotRoundTripPreservesLines", SnapshotRoundTripPreservesLines);
|
||||
Run("SnapshotRejectsTampering", SnapshotRejectsTampering);
|
||||
Run("SnapshotRejectsStaleData", SnapshotRejectsStaleData);
|
||||
Run("SnapshotRejectsDifferentMachine", SnapshotRejectsDifferentMachine);
|
||||
Run("SnapshotReportsMissingFile", SnapshotReportsMissingFile);
|
||||
Run("SnapshotFailureEmitsStableUnknownServices", SnapshotFailureEmitsStableUnknownServices);
|
||||
|
||||
if (Failures.Count == 0)
|
||||
{
|
||||
@@ -81,6 +89,7 @@ namespace BizTalkCheckmkPulse.Tests
|
||||
var query = WmiBizTalkProbe.GroupSettingQuery;
|
||||
Assert(query.Contains("FROM MSBTS_GroupSetting"), "MSBTS_GroupSetting query missing");
|
||||
Assert(query.Contains("BizTalkOperatorGroup"), "configured operator group property missing");
|
||||
Assert(query.Contains("BizTalkReadOnlyUserGroup"), "configured read-only group property missing");
|
||||
Assert(query.Contains("SubscriptionDBServerName"), "master MessageBox server property missing");
|
||||
Assert(query.Contains("SubscriptionDBName"), "master MessageBox database property missing");
|
||||
Assert(query.IndexOf("MessageBoxSetting", StringComparison.OrdinalIgnoreCase) < 0, "unsupported MessageBoxSetting class present");
|
||||
@@ -128,6 +137,114 @@ namespace BizTalkCheckmkPulse.Tests
|
||||
"SQL discovery classification");
|
||||
}
|
||||
|
||||
private static void SnapshotRoundTripPreservesLines()
|
||||
{
|
||||
WithTemporarySnapshot((path, store) =>
|
||||
{
|
||||
var lines = new CheckmkLocalFormatter(new MonitoringOptions()).FormatSelfTest().ToArray();
|
||||
var generated = DateTime.UtcNow;
|
||||
store.Write(lines, generated, "DOMAIN\\collector$");
|
||||
var result = store.Read(generated.AddSeconds(10), TimeSpan.FromMinutes(3));
|
||||
|
||||
Assert(result.IsSuccess, "snapshot should be readable: " + result.Error);
|
||||
AssertEqual(lines.Length, result.Lines.Count, "snapshot line count");
|
||||
Assert(lines.SequenceEqual(result.Lines), "snapshot payload changed");
|
||||
|
||||
var replacement = lines.Select(x => x.Replace("Self test OK", "Replacement OK")).ToArray();
|
||||
store.Write(replacement, generated.AddSeconds(30), "DOMAIN\\collector$");
|
||||
var replaced = store.Read(generated.AddSeconds(40), TimeSpan.FromMinutes(3));
|
||||
Assert(replaced.IsSuccess, "replaced snapshot should be readable");
|
||||
Assert(replacement.SequenceEqual(replaced.Lines), "atomic replacement payload changed");
|
||||
});
|
||||
}
|
||||
|
||||
private static void SnapshotRejectsTampering()
|
||||
{
|
||||
WithTemporarySnapshot((path, store) =>
|
||||
{
|
||||
var lines = new CheckmkLocalFormatter(new MonitoringOptions()).FormatSelfTest().ToArray();
|
||||
store.Write(lines, DateTime.UtcNow, "DOMAIN\\collector$");
|
||||
var content = File.ReadAllText(path, Encoding.UTF8);
|
||||
File.WriteAllText(path, content.Replace("Self test OK", "Tampered output"), new UTF8Encoding(false));
|
||||
|
||||
var result = store.Read(DateTime.UtcNow, TimeSpan.FromMinutes(3));
|
||||
Assert(!result.IsSuccess, "tampered snapshot must be rejected");
|
||||
Assert(result.Error.IndexOf("SHA-256", StringComparison.OrdinalIgnoreCase) >= 0, "tamper reason should mention SHA-256");
|
||||
});
|
||||
}
|
||||
|
||||
private static void SnapshotRejectsStaleData()
|
||||
{
|
||||
WithTemporarySnapshot((path, store) =>
|
||||
{
|
||||
var lines = new CheckmkLocalFormatter(new MonitoringOptions()).FormatSelfTest().ToArray();
|
||||
var generated = DateTime.UtcNow.AddMinutes(-10);
|
||||
store.Write(lines, generated, "DOMAIN\\collector$");
|
||||
|
||||
var result = store.Read(DateTime.UtcNow, TimeSpan.FromMinutes(3));
|
||||
Assert(!result.IsSuccess, "stale snapshot must be rejected");
|
||||
Assert(result.Error.IndexOf("stale", StringComparison.OrdinalIgnoreCase) >= 0, "stale reason missing");
|
||||
});
|
||||
}
|
||||
|
||||
private static void SnapshotRejectsDifferentMachine()
|
||||
{
|
||||
WithTemporarySnapshot((path, store) =>
|
||||
{
|
||||
var lines = new CheckmkLocalFormatter(new MonitoringOptions()).FormatSelfTest().ToArray();
|
||||
store.Write(lines, DateTime.UtcNow, "DOMAIN\\collector$");
|
||||
var content = File.ReadAllText(path, Encoding.UTF8);
|
||||
var currentMachine = Convert.ToBase64String(Encoding.UTF8.GetBytes(Environment.MachineName));
|
||||
var otherMachine = Convert.ToBase64String(Encoding.UTF8.GetBytes("OTHER-SERVER"));
|
||||
File.WriteAllText(
|
||||
path,
|
||||
content.Replace("machineBase64=" + currentMachine, "machineBase64=" + otherMachine),
|
||||
new UTF8Encoding(false));
|
||||
|
||||
var result = store.Read(DateTime.UtcNow, TimeSpan.FromMinutes(3));
|
||||
Assert(!result.IsSuccess, "snapshot for another machine must be rejected");
|
||||
Assert(result.Error.IndexOf("different machine", StringComparison.OrdinalIgnoreCase) >= 0, "machine mismatch reason missing");
|
||||
});
|
||||
}
|
||||
|
||||
private static void SnapshotReportsMissingFile()
|
||||
{
|
||||
WithTemporarySnapshot((path, store) =>
|
||||
{
|
||||
var result = store.Read(DateTime.UtcNow, TimeSpan.FromMinutes(3));
|
||||
Assert(!result.IsSuccess, "missing snapshot must be rejected");
|
||||
Assert(result.Error.IndexOf("does not exist", StringComparison.OrdinalIgnoreCase) >= 0, "missing file reason missing");
|
||||
});
|
||||
}
|
||||
|
||||
private static void SnapshotFailureEmitsStableUnknownServices()
|
||||
{
|
||||
var lines = new CheckmkLocalFormatter(new MonitoringOptions())
|
||||
.FormatSnapshotFailure("Snapshot file does not exist.")
|
||||
.ToArray();
|
||||
AssertEqual(6, lines.Length, "snapshot failure stable service count");
|
||||
Assert(lines.All(x => x.StartsWith("3 \"BizTalk ", StringComparison.Ordinal)), "snapshot failure must be UNKNOWN");
|
||||
Assert(lines.All(x => x.Contains("Scheduled Task")), "snapshot failure must contain provider action");
|
||||
}
|
||||
|
||||
private static void WithTemporarySnapshot(Action<string, SnapshotStore> test)
|
||||
{
|
||||
var directory = Path.Combine(Path.GetTempPath(), "BizTalkCheckmkPulse.Tests." + Guid.NewGuid().ToString("N"));
|
||||
Directory.CreateDirectory(directory);
|
||||
var path = Path.Combine(directory, "snapshot.txt");
|
||||
try
|
||||
{
|
||||
test(path, new SnapshotStore(path, 1048576));
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (Directory.Exists(directory))
|
||||
{
|
||||
Directory.Delete(directory, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static ProbeResult CreateSuspensionResult(string applicationName)
|
||||
{
|
||||
var result = new ProbeResult();
|
||||
|
||||
Reference in New Issue
Block a user