Stabilize BizTalk inventory collection and diagnostics
Build und Test / build (push) Has been cancelled
Build und Test / build (push) Has been cancelled
This commit is contained in:
+60
-19
@@ -38,13 +38,13 @@ Administrative cmd.exe
|
|||||||
+-- SystemCollector
|
+-- SystemCollector
|
||||||
| +-- Windows WMI
|
| +-- Windows WMI
|
||||||
| +-- BizTalk Registry
|
| +-- BizTalk Registry
|
||||||
| +-- Management SQL/DB
|
| +-- MSBTS_GroupSetting für Management SQL/DB
|
||||||
|
|
|
|
||||||
+-- BizTalkWmiCollector
|
+-- BizTalkWmiCollector
|
||||||
|
| +-- ExplorerOM: vollständige Anwendungsliste
|
||||||
| +-- root\MicrosoftBizTalkServer
|
| +-- root\MicrosoftBizTalkServer
|
||||||
| +-- Anwendungen und Artefakte
|
| +-- optionale Artefakte, Adapter, Hosts und Handler
|
||||||
| +-- Adapter, Hosts und Handler
|
| +-- optionaler SAP-Endpunkt-Fallback
|
||||||
| +-- SAP-Endpunkt-Fallback
|
|
||||||
|
|
|
|
||||||
+-- BindingExportCollector
|
+-- BindingExportCollector
|
||||||
| +-- BTSTask ExportBindings /GroupLevel
|
| +-- BTSTask ExportBindings /GroupLevel
|
||||||
@@ -62,7 +62,13 @@ Administrative cmd.exe
|
|||||||
+-- atomare Ausgabe
|
+-- atomare Ausgabe
|
||||||
```
|
```
|
||||||
|
|
||||||
Das Programm referenziert keine `Microsoft.BizTalk.*`-Assembly. Dadurch besteht das Deployment nur aus EXE, Konfiguration, Startskript und Dokumentation. Die BizTalk-Integration erfolgt über die offiziell vorhandenen Verwaltungsoberflächen WMI und `BTSTask.exe`.
|
Das Programm besitzt keine compile-time-Referenz auf eine
|
||||||
|
`Microsoft.BizTalk.*`-Assembly. Dadurch besteht das Deployment nur aus EXE,
|
||||||
|
Konfiguration, Startskript und Dokumentation. Die lokal mit den
|
||||||
|
BizTalk-Verwaltungskomponenten installierte
|
||||||
|
`Microsoft.BizTalk.ExplorerOM.dll` wird zur Laufzeit aus GAC beziehungsweise
|
||||||
|
Installationspfad geladen. Die übrige BizTalk-Integration erfolgt über WMI und
|
||||||
|
`BTSTask.exe`.
|
||||||
|
|
||||||
## 3. Datenquellen
|
## 3. Datenquellen
|
||||||
|
|
||||||
@@ -85,7 +91,30 @@ Ermittelt werden unter anderem:
|
|||||||
|
|
||||||
Kommandozeilenparameter überschreiben automatisch ermittelte Management-DB-Werte.
|
Kommandozeilenparameter überschreiben automatisch ermittelte Management-DB-Werte.
|
||||||
|
|
||||||
### 3.2 BizTalk-WMI
|
### 3.2 BizTalk-Gruppe und ExplorerOM
|
||||||
|
|
||||||
|
Die Management-Datenbank wird zunächst aus Registry und lokalem
|
||||||
|
`MSBTS_GroupSetting` ermittelt. Kommandozeilenparameter haben Vorrang. Die
|
||||||
|
verwendete WQL-Abfrage wird mit Laufzeit und Datensatzanzahl protokolliert:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT Name,MgmtDbServerName,MgmtDbName FROM MSBTS_GroupSetting
|
||||||
|
```
|
||||||
|
|
||||||
|
Die vollständige Anwendungsliste wird anschließend über
|
||||||
|
`Microsoft.BizTalk.ExplorerOM.BtsCatalogExplorer` und integrierte
|
||||||
|
Windows-Authentifizierung aus der BizTalk Management Database gelesen. Es werden
|
||||||
|
keine ändernden ExplorerOM-Methoden aufgerufen. Die Connection String wird nicht
|
||||||
|
protokolliert.
|
||||||
|
|
||||||
|
Dieser Aufbau vermeidet die nicht verlässlich vorhandene WMI-Klasse
|
||||||
|
`MSBTS_Application`, die je nach Installation mit `Invalid class` abbrechen kann.
|
||||||
|
ExplorerOM-Assemblyname, Version und Dateipfad werden protokolliert. Wenn
|
||||||
|
`MSBTS_GroupSetting` nicht gelesen werden kann, darf das automatisch oder per
|
||||||
|
`--management-server`/`--management-database` ermittelte Datenbankziel weiterhin
|
||||||
|
für ExplorerOM verwendet werden.
|
||||||
|
|
||||||
|
### 3.3 Optionale BizTalk-WMI-Daten
|
||||||
|
|
||||||
Primärer Namespace:
|
Primärer Namespace:
|
||||||
|
|
||||||
@@ -93,11 +122,6 @@ Primärer Namespace:
|
|||||||
root\MicrosoftBizTalkServer
|
root\MicrosoftBizTalkServer
|
||||||
```
|
```
|
||||||
|
|
||||||
Pflichtquelle:
|
|
||||||
|
|
||||||
- `MSBTS_Application`
|
|
||||||
- `MSBTS_GroupSetting` wird zusätzlich zur sicheren Erkennung von Management SQL Server und Datenbank verwendet
|
|
||||||
|
|
||||||
Optionale beziehungsweise versionsabhängige Artefaktklassen:
|
Optionale beziehungsweise versionsabhängige Artefaktklassen:
|
||||||
|
|
||||||
- `MSBTS_Orchestration`
|
- `MSBTS_Orchestration`
|
||||||
@@ -115,13 +139,16 @@ Optionale beziehungsweise versionsabhängige Artefaktklassen:
|
|||||||
- `MSBTS_HostSetting`
|
- `MSBTS_HostSetting`
|
||||||
- `MSBTS_HostInstance`
|
- `MSBTS_HostInstance`
|
||||||
|
|
||||||
Eine fehlende optionale Klasse erzeugt einen Hinweis, verhindert aber nicht die Anwendungsliste oder den Word-Bericht.
|
Eine fehlende optionale Klasse erzeugt einen Hinweis, verhindert aber nicht die
|
||||||
|
ExplorerOM-Anwendungsliste oder den Word-Bericht. Ist der gesamte BizTalk-WMI-
|
||||||
|
Namespace für diese Zusatzdaten nicht erreichbar, wird der WMI-Teil übersprungen;
|
||||||
|
die zuvor geladene Anwendungsliste bleibt erhalten.
|
||||||
|
|
||||||
Zum Schutz vor ungewöhnlich großen Artefaktmengen begrenzt `MaxArtifactDetailsPerType`
|
Zum Schutz vor ungewöhnlich großen Artefaktmengen begrenzt `MaxArtifactDetailsPerType`
|
||||||
die aufgenommenen Detaildatensätze pro WMI-Klasse auf standardmäßig 5000. Die
|
die aufgenommenen Detaildatensätze pro WMI-Klasse auf standardmäßig 5000. Die
|
||||||
Anwendungsliste selbst wird nicht begrenzt.
|
Anwendungsliste selbst wird nicht begrenzt.
|
||||||
|
|
||||||
### 3.3 BTSTask-Gruppenbinding
|
### 3.4 BTSTask-Gruppenbinding
|
||||||
|
|
||||||
Das Tool verwendet:
|
Das Tool verwendet:
|
||||||
|
|
||||||
@@ -144,7 +171,7 @@ Die temporäre XML-Datei wird:
|
|||||||
|
|
||||||
Microsoft dokumentiert, dass Kennwörter beim Binding-Export entfernt werden. Zusätzlich redigiert das Tool sensitive Element-, Attribut- und URI-Werte.
|
Microsoft dokumentiert, dass Kennwörter beim Binding-Export entfernt werden. Zusätzlich redigiert das Tool sensitive Element-, Attribut- und URI-Werte.
|
||||||
|
|
||||||
### 3.4 Offline-Binding
|
### 3.5 Offline-Binding
|
||||||
|
|
||||||
Für Diagnose- oder Berechtigungsfälle kann ein vorhandener Export verarbeitet werden:
|
Für Diagnose- oder Berechtigungsfälle kann ein vorhandener Export verarbeitet werden:
|
||||||
|
|
||||||
@@ -306,8 +333,14 @@ Das DR-Runbook muss mindestens enthalten:
|
|||||||
## 10. Resilienz
|
## 10. Resilienz
|
||||||
|
|
||||||
- Collector-Isolation mit eigenem Status und Zeitmessung
|
- Collector-Isolation mit eigenem Status und Zeitmessung
|
||||||
- WMI-Timeout
|
- WMI nur lokal über `\\.\root\MicrosoftBizTalkServer`
|
||||||
|
- WMI-Timeout, WQL-, Laufzeit- und Datensatzprotokollierung
|
||||||
|
- vollständige Anwendungsliste über read-only ExplorerOM statt
|
||||||
|
`MSBTS_Application`
|
||||||
|
- dynamische ExplorerOM-Auflösung aus GAC und BizTalk-Installationspfad
|
||||||
|
- WMI-Zusatzdaten dürfen nach erfolgreicher ExplorerOM-Erfassung ausfallen
|
||||||
- Prozess-Timeout für `BTSTask`
|
- Prozess-Timeout für `BTSTask`
|
||||||
|
- live gespiegelte, secret-bereinigte BTSTask-Ausgabe
|
||||||
- WMI-Fallback für SAP-Endpunkte
|
- WMI-Fallback für SAP-Endpunkte
|
||||||
- Offline-Bindingmodus
|
- Offline-Bindingmodus
|
||||||
- optionale WMI-Klassen stoppen den Lauf nicht
|
- optionale WMI-Klassen stoppen den Lauf nicht
|
||||||
@@ -317,6 +350,8 @@ Das DR-Runbook muss mindestens enthalten:
|
|||||||
- atomare DOCX-Ausgabe
|
- atomare DOCX-Ausgabe
|
||||||
- eindeutige Dateinamen mit Umgebung, Server und Zeitstempel
|
- eindeutige Dateinamen mit Umgebung, Server und Zeitstempel
|
||||||
- Exitcode `1` bei unvollständigem Pflichtabschnitt
|
- Exitcode `1` bei unvollständigem Pflichtabschnitt
|
||||||
|
- vollständige Ausnahmeketten mit Typ, HRESULT, WMI-Status, inneren Ausnahmen
|
||||||
|
und Stacktrace auf Konsole und im Log
|
||||||
|
|
||||||
## 11. DOCX-Erzeugung ohne Office
|
## 11. DOCX-Erzeugung ohne Office
|
||||||
|
|
||||||
@@ -343,10 +378,12 @@ Auf jedem der beiden BizTalk Server:
|
|||||||
3. `BizTalkSapEnvironmentInventory.exe --self-test` ausführen.
|
3. `BizTalkSapEnvironmentInventory.exe --self-test` ausführen.
|
||||||
4. Erfassung starten.
|
4. Erfassung starten.
|
||||||
5. Exitcode prüfen.
|
5. Exitcode prüfen.
|
||||||
6. DOCX und Log gemeinsam kontrollieren.
|
6. Im Log `MSBTS_GroupSetting`, Datensatzanzahl und Management-Datenbankziel prüfen.
|
||||||
7. Anwendungsliste gegen BizTalk Administration abgleichen.
|
7. ExplorerOM-Assemblyname, Version und Pfad kontrollieren.
|
||||||
8. SAP-Endpunkte und Hostzuordnung prüfen.
|
8. DOCX und Log gemeinsam kontrollieren.
|
||||||
9. WE20-/WE21-/Credential-/DR-Findings an SAP Basis geben.
|
9. Anwendungsliste gegen BizTalk Administration abgleichen.
|
||||||
|
10. SAP-Endpunkte und Hostzuordnung prüfen.
|
||||||
|
11. WE20-/WE21-/Credential-/DR-Findings an SAP Basis geben.
|
||||||
|
|
||||||
ACC:
|
ACC:
|
||||||
|
|
||||||
@@ -382,6 +419,7 @@ Tests prüfen:
|
|||||||
- Parsing eines WCF-SAP-Gruppenbindings
|
- Parsing eines WCF-SAP-Gruppenbindings
|
||||||
- SAP-Client, Host und `UseSnc`
|
- SAP-Client, Host und `UseSnc`
|
||||||
- Anwendungszuordnung
|
- Anwendungszuordnung
|
||||||
|
- Diagnose-Logging mit HRESULT und innerer Reflection-Ausnahme
|
||||||
- DOCX-Paketstruktur
|
- DOCX-Paketstruktur
|
||||||
- XML-Gültigkeit aller DOCX-Parts
|
- XML-Gültigkeit aller DOCX-Parts
|
||||||
- Ausschluss von Testkennwörtern
|
- Ausschluss von Testkennwörtern
|
||||||
@@ -395,12 +433,15 @@ Tests prüfen:
|
|||||||
- keine Garantie, dass Schemanamen alle verwendeten IDoc-Erweiterungen enthalten
|
- keine Garantie, dass Schemanamen alle verwendeten IDoc-Erweiterungen enthalten
|
||||||
- dynamisch zur Laufzeit konstruierte SAP-Adressen können im statischen Binding fehlen
|
- dynamisch zur Laufzeit konstruierte SAP-Adressen können im statischen Binding fehlen
|
||||||
- WMI-Klassen unterscheiden sich teilweise je BizTalk-Version/CU
|
- WMI-Klassen unterscheiden sich teilweise je BizTalk-Version/CU
|
||||||
|
- die lokal installierte ExplorerOM-Version muss zur lokalen BizTalk-Installation
|
||||||
|
passen
|
||||||
- lokaler Ist-Stand; ACC und PROD müssen getrennt ausgeführt werden
|
- lokaler Ist-Stand; ACC und PROD müssen getrennt ausgeführt werden
|
||||||
|
|
||||||
## 15. Referenzen
|
## 15. Referenzen
|
||||||
|
|
||||||
- Microsoft: BizTalk Server 2020 Hardware and Software Requirements
|
- Microsoft: BizTalk Server 2020 Hardware and Software Requirements
|
||||||
- Microsoft: `BTSTask ExportBindings`
|
- Microsoft: `BTSTask ExportBindings`
|
||||||
|
- Microsoft: BizTalk Explorer Object Model
|
||||||
- Microsoft: BizTalk WMI Technical Reference
|
- Microsoft: BizTalk WMI Technical Reference
|
||||||
- Microsoft: WCF-SAP Port Configuration
|
- Microsoft: WCF-SAP Port Configuration
|
||||||
- Microsoft: SAP System Connection URI
|
- Microsoft: SAP System Connection URI
|
||||||
|
|||||||
@@ -7,14 +7,18 @@ Das Tool wird einmal auf dem BizTalk Server in `ACC` und einmal auf dem BizTalk
|
|||||||
- einem BizTalk Server 2020
|
- einem BizTalk Server 2020
|
||||||
- einem separaten SQL Server für die BizTalk-Datenbanken
|
- einem separaten SQL Server für die BizTalk-Datenbanken
|
||||||
|
|
||||||
Microsoft Office, Word, PowerShell, ein .NET SDK, Internetzugriff und zusätzliche BizTalk-Programmier-DLLs werden auf dem Zielserver nicht benötigt.
|
Microsoft Office, Word, PowerShell, ein .NET SDK, Internetzugriff und separat
|
||||||
|
mitgelieferte BizTalk-DLLs werden auf dem Zielserver nicht benötigt. Die mit den
|
||||||
|
lokalen BizTalk-Verwaltungskomponenten installierte
|
||||||
|
`Microsoft.BizTalk.ExplorerOM.dll` wird zur Laufzeit aus GAC beziehungsweise
|
||||||
|
BizTalk-Installationspfad geladen.
|
||||||
|
|
||||||
## Erfasster Umfang
|
## Erfasster Umfang
|
||||||
|
|
||||||
### Vollständige BizTalk-Anwendungsliste
|
### Vollständige BizTalk-Anwendungsliste
|
||||||
|
|
||||||
- alle Anwendungen aus `MSBTS_Application`
|
- alle Anwendungen read-only aus dem BizTalk Explorer Object Model
|
||||||
- Anwendungsstatus und Beschreibung
|
- Anwendungsstatus und Beschreibung, soweit ExplorerOM sie bereitstellt
|
||||||
- Anzahl und Zuordnung von Orchestrierungen
|
- Anzahl und Zuordnung von Orchestrierungen
|
||||||
- Send Ports und Send Port Groups
|
- Send Ports und Send Port Groups
|
||||||
- Receive Ports und Receive Locations
|
- Receive Ports und Receive Locations
|
||||||
@@ -69,7 +73,7 @@ Das Tool erfindet hierfür keine Werte. Es dokumentiert nur lokal belegbare Indi
|
|||||||
|
|
||||||
## Sicherheitsprinzip
|
## Sicherheitsprinzip
|
||||||
|
|
||||||
- ausschließlich read-only WMI- und Registry-Abfragen
|
- ausschließlich read-only ExplorerOM-, WMI- und Registry-Zugriffe
|
||||||
- `BTSTask ExportBindings /GroupLevel` wird nur zum Lesen verwendet
|
- `BTSTask ExportBindings /GroupLevel` wird nur zum Lesen verwendet
|
||||||
- temporärer Binding-Export wird nach dem Parsen gelöscht
|
- temporärer Binding-Export wird nach dem Parsen gelöscht
|
||||||
- BizTalk entfernt Kennwörter bereits beim Binding-Export
|
- BizTalk entfernt Kennwörter bereits beim Binding-Export
|
||||||
@@ -86,6 +90,8 @@ DOCX und Log enthalten trotzdem interne Host-, Anwendungs-, Zertifikats- und Pfa
|
|||||||
- Konto ist Mitglied von `BizTalk Server Administrators` oder besitzt vergleichbare Leserechte
|
- Konto ist Mitglied von `BizTalk Server Administrators` oder besitzt vergleichbare Leserechte
|
||||||
- .NET Framework 4.7.2 oder höher
|
- .NET Framework 4.7.2 oder höher
|
||||||
- Zugriff auf die konfigurierte BizTalk Management Database
|
- Zugriff auf die konfigurierte BizTalk Management Database
|
||||||
|
- lokale BizTalk-Verwaltungskomponenten einschließlich
|
||||||
|
`Microsoft.BizTalk.ExplorerOM.dll`
|
||||||
- `BTSTask.exe` aus der lokalen BizTalk-Installation
|
- `BTSTask.exe` aus der lokalen BizTalk-Installation
|
||||||
|
|
||||||
## Schnellstart
|
## Schnellstart
|
||||||
@@ -127,15 +133,25 @@ BizTalkSapEnvironmentInventory.exe ^
|
|||||||
Das Tool zeigt jeden Abschnitt und die erzeugten Dateien direkt auf der Konsole an:
|
Das Tool zeigt jeden Abschnitt und die erzeugten Dateien direkt auf der Konsole an:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
[INFO] Starte Abschnitt: BizTalk-Anwendungen und WMI-Artefakte
|
[INFO] Ermittle die BizTalk-Gruppe über MSBTS_GroupSetting.
|
||||||
[INFO] 34 BizTalk-Anwendung(en) über WMI gefunden.
|
[DETAIL] WQL: SELECT Name,MgmtDbServerName,MgmtDbName FROM MSBTS_GroupSetting
|
||||||
|
[INFO] MSBTS_GroupSetting abgeschlossen: Datensätze=1; Dauer=84 ms.
|
||||||
|
[INFO] Suche die lokale Assembly Microsoft.BizTalk.ExplorerOM.dll.
|
||||||
|
[INFO] ExplorerOM-Assembly geladen: Microsoft.BizTalk.ExplorerOM, Version=...
|
||||||
|
[INFO] 34 BizTalk-Anwendung(en) geladen (412 ms).
|
||||||
|
[INFO] Verbinde lokal mit dem BizTalk-WMI-Namespace für optionale Zusatzdaten.
|
||||||
|
[INFO] WMI-Abfrage abgeschlossen: SendPort; Datensätze=91; Dauer=173 ms.
|
||||||
[INFO] Exportiere BizTalk-Gruppenbindings read-only mit BTSTask.
|
[INFO] Exportiere BizTalk-Gruppenbindings read-only mit BTSTask.
|
||||||
|
[INFO] BTSTask ExportBindings erfolgreich abgeschlossen (1865 ms).
|
||||||
[INFO] 12 SAP-Endpunkt(e) aus dem Binding-Export ausgewertet.
|
[INFO] 12 SAP-Endpunkt(e) aus dem Binding-Export ausgewertet.
|
||||||
[INFO] Erzeuge Microsoft-Word-Dokument: C:\BizTalk-Doku\ACC\...
|
[INFO] Ergebnisübersicht: Anwendungen=34; SAP-Endpunkte=12; Findings=4.
|
||||||
[INFO] Word-Dokument erfolgreich erzeugt: C:\BizTalk-Doku\ACC\...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Fehler eines Collectors stoppen die übrigen Abschnitte nicht. Sie erscheinen im Log, im Erfassungsstatus und als Finding im Word-Bericht.
|
Konsole und Log erhalten dieselben Fortschritts- und Diagnosemeldungen. Fehler
|
||||||
|
enthalten Ausnahmetyp, HRESULT, WMI-Status, innere Ausnahmen und Stacktrace.
|
||||||
|
Fehler eines Collectors stoppen die übrigen Abschnitte nicht. Fehlende optionale
|
||||||
|
WMI-Klassen verwerfen insbesondere nicht die über ExplorerOM geladene
|
||||||
|
Anwendungsliste.
|
||||||
|
|
||||||
## Ausgabedateien
|
## Ausgabedateien
|
||||||
|
|
||||||
@@ -200,7 +216,28 @@ Die Gitea-Workflowdatei `.gitea/workflows/build.yml` baut, testet und veröffent
|
|||||||
|
|
||||||
- lokal auf dem BizTalk Server ausführen
|
- lokal auf dem BizTalk Server ausführen
|
||||||
- Konto und Mitgliedschaft in den BizTalk-Administrator-/Operator-Gruppen prüfen
|
- Konto und Mitgliedschaft in den BizTalk-Administrator-/Operator-Gruppen prüfen
|
||||||
- WMI-Namespace `root\MicrosoftBizTalkServer` prüfen
|
- im Log das ermittelte Management-Datenbankziel prüfen
|
||||||
|
- geladene ExplorerOM-Assembly einschließlich Version und Pfad prüfen
|
||||||
|
- Zugriff auf die BizTalk Management Database mit integrierter
|
||||||
|
Windows-Authentifizierung prüfen
|
||||||
|
- bei fehlender Erkennung `--management-server` und
|
||||||
|
`--management-database` explizit angeben
|
||||||
|
|
||||||
|
### WMI meldet `Invalid class`
|
||||||
|
|
||||||
|
Die Anwendungsliste verwendet nicht mehr die nicht verlässlich vorhandene Klasse
|
||||||
|
`MSBTS_Application`, sondern ExplorerOM. Ein `Invalid class` für eine optionale
|
||||||
|
Artefaktklasse erscheint mit WQL, HRESULT und WMI-Status im Log, wird als Finding
|
||||||
|
übernommen und der Lauf wird fortgesetzt. Schlägt bereits
|
||||||
|
`MSBTS_GroupSetting` fehl, kann die Management-Datenbank über die beiden
|
||||||
|
`--management-*`-Optionen vorgegeben werden.
|
||||||
|
|
||||||
|
### ExplorerOM-Assembly fehlt
|
||||||
|
|
||||||
|
- prüfen, ob die BizTalk-Verwaltungskomponenten lokal installiert sind
|
||||||
|
- im Log die geprüften GAC-/Installationspfade kontrollieren
|
||||||
|
- keine DLL von einem anderen BizTalk-Server oder einer anderen CU-Version
|
||||||
|
in den Deployment-Ordner kopieren
|
||||||
|
|
||||||
### BTSTask findet die Management Database nicht
|
### BTSTask findet die Management Database nicht
|
||||||
|
|
||||||
@@ -228,4 +265,3 @@ BizTalkSapEnvironmentInventory.exe ^
|
|||||||
- DOCX-Findings und Log prüfen
|
- DOCX-Findings und Log prüfen
|
||||||
|
|
||||||
Weitere Architektur- und Sicherheitsdetails stehen in [Dokumentation.md](Dokumentation.md).
|
Weitere Architektur- und Sicherheitsdetails stehen in [Dokumentation.md](Dokumentation.md).
|
||||||
|
|
||||||
|
|||||||
@@ -530,7 +530,13 @@ namespace BizTalkSapEnvironmentInventory.Collectors
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.Info("Exportiere BizTalk-Gruppenbindings read-only mit BTSTask.");
|
logger.Info("Exportiere BizTalk-Gruppenbindings read-only mit BTSTask.");
|
||||||
|
logger.Detail("BTSTask-Pfad: " + btsTaskPath);
|
||||||
|
logger.Detail("BTSTask-Timeout: "
|
||||||
|
+ processTimeout.TotalSeconds.ToString(CultureInfo.InvariantCulture)
|
||||||
|
+ " Sekunden.");
|
||||||
var output = new StringBuilder();
|
var output = new StringBuilder();
|
||||||
|
var outputSync = new object();
|
||||||
|
var timer = Stopwatch.StartNew();
|
||||||
var startInfo = new ProcessStartInfo
|
var startInfo = new ProcessStartInfo
|
||||||
{
|
{
|
||||||
FileName = btsTaskPath,
|
FileName = btsTaskPath,
|
||||||
@@ -548,14 +554,24 @@ namespace BizTalkSapEnvironmentInventory.Collectors
|
|||||||
{
|
{
|
||||||
if (!string.IsNullOrWhiteSpace(args.Data))
|
if (!string.IsNullOrWhiteSpace(args.Data))
|
||||||
{
|
{
|
||||||
output.AppendLine(args.Data);
|
var safeLine = SensitiveDataSanitizer.SanitizeText(args.Data);
|
||||||
|
lock (outputSync)
|
||||||
|
{
|
||||||
|
output.AppendLine(safeLine);
|
||||||
|
}
|
||||||
|
logger.Detail("BTSTask: " + safeLine);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
process.ErrorDataReceived += (sender, args) =>
|
process.ErrorDataReceived += (sender, args) =>
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrWhiteSpace(args.Data))
|
if (!string.IsNullOrWhiteSpace(args.Data))
|
||||||
{
|
{
|
||||||
output.AppendLine(args.Data);
|
var safeLine = SensitiveDataSanitizer.SanitizeText(args.Data);
|
||||||
|
lock (outputSync)
|
||||||
|
{
|
||||||
|
output.AppendLine(safeLine);
|
||||||
|
}
|
||||||
|
logger.Detail("BTSTask: " + safeLine);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -575,20 +591,32 @@ namespace BizTalkSapEnvironmentInventory.Collectors
|
|||||||
"BTSTask ExportBindings überschritt " + processTimeout.TotalSeconds + " Sekunden.");
|
"BTSTask ExportBindings überschritt " + processTimeout.TotalSeconds + " Sekunden.");
|
||||||
}
|
}
|
||||||
process.WaitForExit();
|
process.WaitForExit();
|
||||||
|
timer.Stop();
|
||||||
if (process.ExitCode != 0)
|
if (process.ExitCode != 0)
|
||||||
{
|
{
|
||||||
|
string errorOutput;
|
||||||
|
lock (outputSync)
|
||||||
|
{
|
||||||
|
errorOutput = output.ToString();
|
||||||
|
}
|
||||||
throw new InvalidOperationException(
|
throw new InvalidOperationException(
|
||||||
"BTSTask ExportBindings meldete Exitcode "
|
"BTSTask ExportBindings meldete Exitcode "
|
||||||
+ process.ExitCode
|
+ process.ExitCode
|
||||||
+ ": "
|
+ ": "
|
||||||
+ LastLines(output.ToString(), 8));
|
+ LastLines(errorOutput, 8));
|
||||||
}
|
}
|
||||||
|
logger.Info("BTSTask ExportBindings erfolgreich abgeschlossen ("
|
||||||
|
+ timer.ElapsedMilliseconds + " ms).");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!File.Exists(destination) || new FileInfo(destination).Length == 0)
|
if (!File.Exists(destination) || new FileInfo(destination).Length == 0)
|
||||||
{
|
{
|
||||||
throw new InvalidDataException("BTSTask erzeugte keine Binding-Datei.");
|
throw new InvalidDataException("BTSTask erzeugte keine Binding-Datei.");
|
||||||
}
|
}
|
||||||
|
logger.Detail("Temporärer Binding-Export: "
|
||||||
|
+ new FileInfo(destination).Length
|
||||||
|
.ToString(CultureInfo.InvariantCulture)
|
||||||
|
+ " Bytes.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private string ResolveBtsTaskPath()
|
private string ResolveBtsTaskPath()
|
||||||
|
|||||||
@@ -1,19 +1,29 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Management;
|
using System.Management;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Security.Principal;
|
||||||
using BizTalkSapEnvironmentInventory.Infrastructure;
|
using BizTalkSapEnvironmentInventory.Infrastructure;
|
||||||
using BizTalkSapEnvironmentInventory.Models;
|
using BizTalkSapEnvironmentInventory.Models;
|
||||||
|
|
||||||
namespace BizTalkSapEnvironmentInventory.Collectors
|
namespace BizTalkSapEnvironmentInventory.Collectors
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Liest Anwendungen, Artefakte, Adapter und Hosts aus dem BizTalk-WMI-Provider.
|
/// Liest die vollständige Anwendungsliste über ExplorerOM und ergänzt sie um
|
||||||
|
/// optionale Artefakt-, Adapter- und Hostdaten aus dem BizTalk-WMI-Provider.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal sealed class BizTalkWmiCollector
|
internal sealed class BizTalkWmiCollector
|
||||||
{
|
{
|
||||||
private const string NamespacePath = @"root\MicrosoftBizTalkServer";
|
private const string WmiNamespace = @"\\.\root\MicrosoftBizTalkServer";
|
||||||
|
private const string ExplorerAssemblyName = "Microsoft.BizTalk.ExplorerOM";
|
||||||
|
private const string ExplorerTypeName =
|
||||||
|
"Microsoft.BizTalk.ExplorerOM.BtsCatalogExplorer";
|
||||||
|
|
||||||
private readonly InventoryDocument document;
|
private readonly InventoryDocument document;
|
||||||
private readonly ConsoleFileLogger logger;
|
private readonly ConsoleFileLogger logger;
|
||||||
@@ -31,18 +41,104 @@ namespace BizTalkSapEnvironmentInventory.Collectors
|
|||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
timeout = TimeSpan.FromSeconds(Math.Max(5, timeoutSeconds));
|
timeout = TimeSpan.FromSeconds(Math.Max(5, timeoutSeconds));
|
||||||
this.maxArtifactDetailsPerType = Math.Max(100, maxArtifactDetailsPerType);
|
this.maxArtifactDetailsPerType = Math.Max(100, maxArtifactDetailsPerType);
|
||||||
scope = new ManagementScope(@"\\" + Environment.MachineName + @"\" + NamespacePath);
|
scope = new ManagementScope(WmiNamespace);
|
||||||
scope.Options.Timeout = timeout;
|
scope.Options.Timeout = timeout;
|
||||||
|
scope.Options.EnablePrivileges = false;
|
||||||
|
scope.Options.Impersonation = ImpersonationLevel.Impersonate;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Baut die vollständige Anwendungsliste auf und ergänzt verfügbare Artefaktbeziehungen.
|
/// Baut die vollständige Anwendungsliste auf und ergänzt verfügbare WMI-Daten.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Collect()
|
public void Collect()
|
||||||
{
|
{
|
||||||
scope.Connect();
|
LogRuntimeInformation();
|
||||||
CollectApplications();
|
CollectApplications();
|
||||||
|
|
||||||
|
if (TryConnectWmi())
|
||||||
|
{
|
||||||
|
CollectOptionalWmiData();
|
||||||
|
}
|
||||||
|
|
||||||
|
document.Applications.Sort((left, right) =>
|
||||||
|
string.Compare(left.Name, right.Name, StringComparison.OrdinalIgnoreCase));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CollectApplications()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(document.System.ManagementServer))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
"Der BizTalk Management SQL Server wurde weder über "
|
||||||
|
+ "MSBTS_GroupSetting/Registry noch über --management-server ermittelt.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(document.System.ManagementDatabase))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
"Die BizTalk Management Database wurde weder automatisch noch "
|
||||||
|
+ "über --management-database ermittelt.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var explorerType = ResolveExplorerType();
|
||||||
|
object explorer = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
explorer = Activator.CreateInstance(explorerType);
|
||||||
|
logger.Info(
|
||||||
|
"Öffne das BizTalk Explorer Object Model ausschließlich lesend.");
|
||||||
|
logger.Info("ExplorerOM-Ziel: Server="
|
||||||
|
+ document.System.ManagementServer
|
||||||
|
+ "; Datenbank="
|
||||||
|
+ document.System.ManagementDatabase
|
||||||
|
+ ".");
|
||||||
|
|
||||||
|
// Die integrierte Verbindungszeichenfolge wird bewusst nicht protokolliert.
|
||||||
|
SetRequiredProperty(
|
||||||
|
explorer,
|
||||||
|
"ConnectionString",
|
||||||
|
"Data Source=" + document.System.ManagementServer
|
||||||
|
+ ";Initial Catalog=" + document.System.ManagementDatabase
|
||||||
|
+ ";Integrated Security=SSPI;Persist Security Info=False;");
|
||||||
|
|
||||||
|
var timer = Stopwatch.StartNew();
|
||||||
|
var applications = EnumerateRequiredProperty(
|
||||||
|
explorer,
|
||||||
|
"Applications",
|
||||||
|
"BizTalk-Anwendungen");
|
||||||
|
timer.Stop();
|
||||||
|
logger.Info(applications.Count + " BizTalk-Anwendung(en) geladen ("
|
||||||
|
+ timer.ElapsedMilliseconds + " ms).");
|
||||||
|
|
||||||
|
foreach (var item in applications)
|
||||||
|
{
|
||||||
|
var name = RequiredText(item, "Name");
|
||||||
|
var application = GetOrCreateApplication(name);
|
||||||
|
application.Description = OptionalText(item, "Description");
|
||||||
|
application.Status = FormatStatus(OptionalText(item, "Status"));
|
||||||
|
logger.Detail("ExplorerOM-Anwendung: " + name
|
||||||
|
+ "; Status=" + application.Status + ".");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.Applications.Count == 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
"Das BizTalk Explorer Object Model lieferte keine Anwendungen.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
var disposable = explorer as IDisposable;
|
||||||
|
if (disposable != null)
|
||||||
|
{
|
||||||
|
disposable.Dispose();
|
||||||
|
logger.Detail("BizTalk Explorer Object Model wurde geschlossen.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CollectOptionalWmiData()
|
||||||
|
{
|
||||||
var artifactClasses = new[]
|
var artifactClasses = new[]
|
||||||
{
|
{
|
||||||
new ClassDescriptor("MSBTS_Orchestration", "Orchestration"),
|
new ClassDescriptor("MSBTS_Orchestration", "Orchestration"),
|
||||||
@@ -72,41 +168,210 @@ namespace BizTalkSapEnvironmentInventory.Collectors
|
|||||||
|
|
||||||
TryCollectWmiSapEndpoints("MSBTS_SendPort", "Senden");
|
TryCollectWmiSapEndpoints("MSBTS_SendPort", "Senden");
|
||||||
TryCollectWmiSapEndpoints("MSBTS_ReceiveLocation", "Empfangen");
|
TryCollectWmiSapEndpoints("MSBTS_ReceiveLocation", "Empfangen");
|
||||||
|
|
||||||
document.Applications.Sort((left, right) =>
|
|
||||||
string.Compare(left.Name, right.Name, StringComparison.OrdinalIgnoreCase));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CollectApplications()
|
private void LogRuntimeInformation()
|
||||||
{
|
{
|
||||||
var rows = Query("SELECT * FROM MSBTS_Application");
|
logger.Info("Diagnose: Computer=" + Environment.MachineName
|
||||||
foreach (var row in rows)
|
+ "; Betriebssystem=" + Environment.OSVersion.VersionString
|
||||||
|
+ "; CLR=" + Environment.Version
|
||||||
|
+ "; Prozess=" + (Environment.Is64BitProcess ? "64-Bit" : "32-Bit")
|
||||||
|
+ "; Betriebssystem="
|
||||||
|
+ (Environment.Is64BitOperatingSystem ? "64-Bit" : "32-Bit")
|
||||||
|
+ ".");
|
||||||
|
logger.Detail("WMI-Namespace: " + WmiNamespace);
|
||||||
|
logger.Detail("WMI-Timeout: "
|
||||||
|
+ timeout.TotalSeconds.ToString(CultureInfo.InvariantCulture)
|
||||||
|
+ " Sekunden.");
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
var name = First(row, "Name", "ApplicationName");
|
using (var identity = WindowsIdentity.GetCurrent())
|
||||||
if (string.IsNullOrWhiteSpace(name))
|
{
|
||||||
|
var principal = new WindowsPrincipal(identity);
|
||||||
|
logger.Info("Sicherheitskontext: Benutzer=" + identity.Name
|
||||||
|
+ "; administrativ="
|
||||||
|
+ (principal.IsInRole(WindowsBuiltInRole.Administrator)
|
||||||
|
? "Ja"
|
||||||
|
: "Nein")
|
||||||
|
+ ".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
// Diagnosefehler dürfen die eigentliche Inventarisierung nicht verhindern.
|
||||||
|
logger.Warning("Sicherheitskontext konnte nicht ermittelt werden: "
|
||||||
|
+ exception.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryConnectWmi()
|
||||||
|
{
|
||||||
|
var timer = Stopwatch.StartNew();
|
||||||
|
logger.Info(
|
||||||
|
"Verbinde lokal mit dem BizTalk-WMI-Namespace für optionale Zusatzdaten.");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
scope.Connect();
|
||||||
|
timer.Stop();
|
||||||
|
logger.Info("BizTalk-WMI-Verbindung hergestellt ("
|
||||||
|
+ timer.ElapsedMilliseconds + " ms).");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception exception) when (IsRecoverableWmiException(exception))
|
||||||
|
{
|
||||||
|
timer.Stop();
|
||||||
|
document.Findings.Add(new Finding
|
||||||
|
{
|
||||||
|
Severity = "Warnung",
|
||||||
|
Area = "BizTalk WMI",
|
||||||
|
Message =
|
||||||
|
"Der BizTalk-WMI-Namespace ist für optionale Artefakt-, "
|
||||||
|
+ "Adapter- und Hostdaten nicht erreichbar: " + exception.Message,
|
||||||
|
RecommendedAction =
|
||||||
|
"WMI-Provider und Berechtigungen anhand des Logs prüfen. "
|
||||||
|
+ "Die über ExplorerOM geladene Anwendungsliste bleibt verwendbar.",
|
||||||
|
Owner = "BizTalk-Betrieb"
|
||||||
|
});
|
||||||
|
logger.Exception("BizTalk-WMI-Verbindung fehlgeschlagen", exception);
|
||||||
|
logger.Warning(
|
||||||
|
"Optionale WMI-Zusatzdaten werden übersprungen; "
|
||||||
|
+ "die ExplorerOM-Anwendungsliste bleibt erhalten.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Type ResolveExplorerType()
|
||||||
|
{
|
||||||
|
logger.Info("Suche die lokale Assembly " + ExplorerAssemblyName + ".dll.");
|
||||||
|
var qualifiedTypeName = ExplorerTypeName + ", " + ExplorerAssemblyName;
|
||||||
|
var explorerType = Type.GetType(qualifiedTypeName, false);
|
||||||
|
if (explorerType != null)
|
||||||
|
{
|
||||||
|
LogExplorerAssembly(explorerType.Assembly);
|
||||||
|
return explorerType;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assembly assembly = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
assembly = Assembly.Load(ExplorerAssemblyName);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
logger.Detail("Assembly.Load fehlgeschlagen: "
|
||||||
|
+ exception.GetType().Name + ": " + exception.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (assembly == null)
|
||||||
|
{
|
||||||
|
#pragma warning disable 618
|
||||||
|
// Kompatibilitäts-Fallback für unterschiedliche BizTalk-GAC-Versionen.
|
||||||
|
assembly = Assembly.LoadWithPartialName(ExplorerAssemblyName);
|
||||||
|
#pragma warning restore 618
|
||||||
|
}
|
||||||
|
|
||||||
|
if (assembly == null)
|
||||||
|
{
|
||||||
|
assembly = LoadExplorerAssemblyFromInstallDirectory();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (assembly == null)
|
||||||
|
{
|
||||||
|
throw new FileNotFoundException(
|
||||||
|
"Microsoft.BizTalk.ExplorerOM.dll wurde weder im GAC noch "
|
||||||
|
+ "im BizTalk-Installationsverzeichnis gefunden. "
|
||||||
|
+ "Die BizTalk-Verwaltungskomponenten müssen lokal installiert sein.");
|
||||||
|
}
|
||||||
|
|
||||||
|
explorerType = assembly.GetType(ExplorerTypeName, false);
|
||||||
|
if (explorerType == null)
|
||||||
|
{
|
||||||
|
throw new TypeLoadException(
|
||||||
|
"Typ " + ExplorerTypeName + " fehlt in " + assembly.FullName + ".");
|
||||||
|
}
|
||||||
|
|
||||||
|
LogExplorerAssembly(assembly);
|
||||||
|
return explorerType;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Assembly LoadExplorerAssemblyFromInstallDirectory()
|
||||||
|
{
|
||||||
|
var candidates = new List<string>();
|
||||||
|
if (!string.IsNullOrWhiteSpace(document.System.BizTalkInstallPath))
|
||||||
|
{
|
||||||
|
candidates.Add(Path.Combine(
|
||||||
|
document.System.BizTalkInstallPath,
|
||||||
|
ExplorerAssemblyName + ".dll"));
|
||||||
|
}
|
||||||
|
|
||||||
|
var roots = new[]
|
||||||
|
{
|
||||||
|
Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86),
|
||||||
|
Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles)
|
||||||
|
};
|
||||||
|
var folders = new[]
|
||||||
|
{
|
||||||
|
"Microsoft BizTalk Server",
|
||||||
|
"Microsoft BizTalk Server 2020"
|
||||||
|
};
|
||||||
|
foreach (var root in roots.Where(item => !string.IsNullOrWhiteSpace(item)))
|
||||||
|
{
|
||||||
|
foreach (var folder in folders)
|
||||||
|
{
|
||||||
|
candidates.Add(Path.Combine(
|
||||||
|
root,
|
||||||
|
folder,
|
||||||
|
ExplorerAssemblyName + ".dll"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var path in candidates.Distinct(StringComparer.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
logger.Detail("Prüfe ExplorerOM-Assemblypfad: " + path);
|
||||||
|
if (!File.Exists(path))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
GetOrCreateApplication(name).Description = First(row, "Description");
|
try
|
||||||
GetOrCreateApplication(name).Status = FormatStatus(First(row, "Status"));
|
{
|
||||||
|
return Assembly.LoadFrom(path);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
logger.Detail("Assembly.LoadFrom fehlgeschlagen: "
|
||||||
|
+ exception.GetType().Name + ": " + exception.Message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (document.Applications.Count == 0)
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LogExplorerAssembly(Assembly assembly)
|
||||||
|
{
|
||||||
|
logger.Info("ExplorerOM-Assembly geladen: " + assembly.FullName + ".");
|
||||||
|
try
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException(
|
logger.Detail("ExplorerOM-Assemblypfad: " + assembly.Location);
|
||||||
"WMI lieferte keine BizTalk-Anwendungen. Berechtigungen und BizTalk-Gruppe prüfen.");
|
}
|
||||||
|
catch (NotSupportedException)
|
||||||
|
{
|
||||||
|
logger.Detail(
|
||||||
|
"ExplorerOM-Assemblypfad ist für diese Assembly nicht verfügbar.");
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Info(document.Applications.Count + " BizTalk-Anwendung(en) über WMI gefunden.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TryCollectArtifacts(ClassDescriptor descriptor)
|
private void TryCollectArtifacts(ClassDescriptor descriptor)
|
||||||
{
|
{
|
||||||
|
List<ManagementObject> rows = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var count = 0;
|
var count = 0;
|
||||||
foreach (var row in Query("SELECT * FROM " + descriptor.ClassName))
|
rows = Query(
|
||||||
|
descriptor.DisplayName,
|
||||||
|
"SELECT * FROM " + descriptor.ClassName);
|
||||||
|
foreach (var row in rows)
|
||||||
{
|
{
|
||||||
if (count >= maxArtifactDetailsPerType)
|
if (count >= maxArtifactDetailsPerType)
|
||||||
{
|
{
|
||||||
@@ -164,10 +429,14 @@ namespace BizTalkSapEnvironmentInventory.Collectors
|
|||||||
|
|
||||||
logger.Info(descriptor.DisplayName + ": " + count + " Artefakt(e).");
|
logger.Info(descriptor.DisplayName + ": " + count + " Artefakt(e).");
|
||||||
}
|
}
|
||||||
catch (ManagementException exception)
|
catch (Exception exception) when (IsRecoverableWmiException(exception))
|
||||||
{
|
{
|
||||||
AddOptionalWmiFinding(descriptor.ClassName, exception);
|
AddOptionalWmiFinding(descriptor.ClassName, exception);
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
DisposeRows(rows);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryCollectComponents(
|
private bool TryCollectComponents(
|
||||||
@@ -176,9 +445,11 @@ namespace BizTalkSapEnvironmentInventory.Collectors
|
|||||||
List<ComponentRecord> target,
|
List<ComponentRecord> target,
|
||||||
bool onlySap)
|
bool onlySap)
|
||||||
{
|
{
|
||||||
|
List<ManagementObject> rows = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
foreach (var row in Query("SELECT * FROM " + className))
|
rows = Query(category, "SELECT * FROM " + className);
|
||||||
|
foreach (var row in rows)
|
||||||
{
|
{
|
||||||
if (onlySap && !LooksLikeSap(row))
|
if (onlySap && !LooksLikeSap(row))
|
||||||
{
|
{
|
||||||
@@ -199,18 +470,26 @@ namespace BizTalkSapEnvironmentInventory.Collectors
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (ManagementException exception)
|
catch (Exception exception) when (IsRecoverableWmiException(exception))
|
||||||
{
|
{
|
||||||
AddOptionalWmiFinding(className, exception);
|
AddOptionalWmiFinding(className, exception);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
DisposeRows(rows);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TryCollectWmiSapEndpoints(string className, string direction)
|
private void TryCollectWmiSapEndpoints(string className, string direction)
|
||||||
{
|
{
|
||||||
|
List<ManagementObject> rows = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
foreach (var row in Query("SELECT * FROM " + className))
|
rows = Query(
|
||||||
|
className + " SAP-Fallback",
|
||||||
|
"SELECT * FROM " + className);
|
||||||
|
foreach (var row in rows)
|
||||||
{
|
{
|
||||||
if (!LooksLikeSap(row))
|
if (!LooksLikeSap(row))
|
||||||
{
|
{
|
||||||
@@ -249,13 +528,17 @@ namespace BizTalkSapEnvironmentInventory.Collectors
|
|||||||
document.SapEndpoints.Add(endpoint);
|
document.SapEndpoints.Add(endpoint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (ManagementException exception)
|
catch (Exception exception) when (IsRecoverableWmiException(exception))
|
||||||
{
|
{
|
||||||
AddOptionalWmiFinding(className + " SAP-Fallback", exception);
|
AddOptionalWmiFinding(className + " SAP-Fallback", exception);
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
DisposeRows(rows);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<ManagementObject> Query(string query)
|
private List<ManagementObject> Query(string name, string query)
|
||||||
{
|
{
|
||||||
var options = new EnumerationOptions
|
var options = new EnumerationOptions
|
||||||
{
|
{
|
||||||
@@ -263,15 +546,125 @@ namespace BizTalkSapEnvironmentInventory.Collectors
|
|||||||
Rewindable = false,
|
Rewindable = false,
|
||||||
Timeout = timeout
|
Timeout = timeout
|
||||||
};
|
};
|
||||||
|
var timer = Stopwatch.StartNew();
|
||||||
|
logger.Info("WMI-Abfrage startet: " + name + ".");
|
||||||
|
logger.Detail("WQL: " + query);
|
||||||
using (var searcher = new ManagementObjectSearcher(
|
using (var searcher = new ManagementObjectSearcher(
|
||||||
scope,
|
scope,
|
||||||
new ObjectQuery(query),
|
new ObjectQuery(query),
|
||||||
options))
|
options))
|
||||||
{
|
{
|
||||||
return searcher.Get().Cast<ManagementObject>().ToList();
|
using (var rows = searcher.Get())
|
||||||
|
{
|
||||||
|
var result = rows.Cast<ManagementObject>().ToList();
|
||||||
|
timer.Stop();
|
||||||
|
logger.Info("WMI-Abfrage abgeschlossen: " + name
|
||||||
|
+ "; Datensätze=" + result.Count
|
||||||
|
+ "; Dauer=" + timer.ElapsedMilliseconds + " ms.");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static IList<object> EnumerateRequiredProperty(
|
||||||
|
object instance,
|
||||||
|
string propertyName,
|
||||||
|
string description)
|
||||||
|
{
|
||||||
|
var value = RequiredProperty(instance, propertyName);
|
||||||
|
var enumerable = value as IEnumerable;
|
||||||
|
if (enumerable == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
description + " ist keine aufzählbare Sammlung.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = new List<object>();
|
||||||
|
foreach (var item in enumerable)
|
||||||
|
{
|
||||||
|
if (item != null)
|
||||||
|
{
|
||||||
|
result.Add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string RequiredText(object instance, string propertyName)
|
||||||
|
{
|
||||||
|
var value = Convert.ToString(
|
||||||
|
RequiredProperty(instance, propertyName),
|
||||||
|
CultureInfo.InvariantCulture);
|
||||||
|
if (string.IsNullOrWhiteSpace(value))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
instance.GetType().FullName + "." + propertyName + " ist leer.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return value.Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
private string OptionalText(object instance, string propertyName)
|
||||||
|
{
|
||||||
|
var property = instance.GetType().GetProperty(
|
||||||
|
propertyName,
|
||||||
|
BindingFlags.Instance | BindingFlags.Public);
|
||||||
|
if (property == null)
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return Convert.ToString(
|
||||||
|
property.GetValue(instance, null),
|
||||||
|
CultureInfo.InvariantCulture) ?? string.Empty;
|
||||||
|
}
|
||||||
|
catch (Exception exception) when (
|
||||||
|
exception is TargetInvocationException
|
||||||
|
|| exception is COMException
|
||||||
|
|| exception is NotSupportedException)
|
||||||
|
{
|
||||||
|
logger.Detail("Optionale ExplorerOM-Eigenschaft "
|
||||||
|
+ instance.GetType().FullName
|
||||||
|
+ "."
|
||||||
|
+ propertyName
|
||||||
|
+ " konnte nicht gelesen werden: "
|
||||||
|
+ exception.Message);
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object RequiredProperty(object instance, string propertyName)
|
||||||
|
{
|
||||||
|
var property = instance.GetType().GetProperty(
|
||||||
|
propertyName,
|
||||||
|
BindingFlags.Instance | BindingFlags.Public);
|
||||||
|
if (property == null)
|
||||||
|
{
|
||||||
|
throw new MissingMemberException(instance.GetType().FullName, propertyName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return property.GetValue(instance, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SetRequiredProperty(
|
||||||
|
object instance,
|
||||||
|
string propertyName,
|
||||||
|
object value)
|
||||||
|
{
|
||||||
|
var property = instance.GetType().GetProperty(
|
||||||
|
propertyName,
|
||||||
|
BindingFlags.Instance | BindingFlags.Public);
|
||||||
|
if (property == null || !property.CanWrite)
|
||||||
|
{
|
||||||
|
throw new MissingMemberException(instance.GetType().FullName, propertyName);
|
||||||
|
}
|
||||||
|
|
||||||
|
property.SetValue(instance, value, null);
|
||||||
|
}
|
||||||
|
|
||||||
private ApplicationRecord GetOrCreateApplication(string name)
|
private ApplicationRecord GetOrCreateApplication(string name)
|
||||||
{
|
{
|
||||||
var application = document.Applications.FirstOrDefault(item =>
|
var application = document.Applications.FirstOrDefault(item =>
|
||||||
@@ -405,7 +798,7 @@ namespace BizTalkSapEnvironmentInventory.Collectors
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddOptionalWmiFinding(string className, ManagementException exception)
|
private void AddOptionalWmiFinding(string className, Exception exception)
|
||||||
{
|
{
|
||||||
document.Findings.Add(new Finding
|
document.Findings.Add(new Finding
|
||||||
{
|
{
|
||||||
@@ -415,7 +808,32 @@ namespace BizTalkSapEnvironmentInventory.Collectors
|
|||||||
RecommendedAction = "Nur relevant, wenn die zugehörige Artefaktspalte vollständig benötigt wird.",
|
RecommendedAction = "Nur relevant, wenn die zugehörige Artefaktspalte vollständig benötigt wird.",
|
||||||
Owner = "BizTalk-Betrieb"
|
Owner = "BizTalk-Betrieb"
|
||||||
});
|
});
|
||||||
logger.Warning("Optionale WMI-Abfrage " + className + " fehlgeschlagen: " + exception.Message);
|
logger.Exception(
|
||||||
|
"Optionale WMI-Abfrage " + className + " fehlgeschlagen",
|
||||||
|
exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsRecoverableWmiException(Exception exception)
|
||||||
|
{
|
||||||
|
return exception is ManagementException
|
||||||
|
|| exception is COMException
|
||||||
|
|| exception is UnauthorizedAccessException;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DisposeRows(IEnumerable<ManagementObject> rows)
|
||||||
|
{
|
||||||
|
if (rows == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var row in rows)
|
||||||
|
{
|
||||||
|
if (row != null)
|
||||||
|
{
|
||||||
|
row.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed class ClassDescriptor
|
private sealed class ClassDescriptor
|
||||||
|
|||||||
@@ -46,15 +46,32 @@ namespace BizTalkSapEnvironmentInventory.Collectors
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void Collect()
|
public void Collect()
|
||||||
{
|
{
|
||||||
|
logger.Info("Prüfe installierte SAP-/BizTalk-Adapter-Komponenten.");
|
||||||
CollectInstalledProducts();
|
CollectInstalledProducts();
|
||||||
|
logger.Info("Lese konfigurierte SNC- und SAP-Runtime-Pfade.");
|
||||||
var configuredPaths = CollectConfiguredSecurityPaths();
|
var configuredPaths = CollectConfiguredSecurityPaths();
|
||||||
|
logger.Info("Lese SAP-relevante Umgebungsvariablen.");
|
||||||
CollectEnvironment(document.RuntimeComponents);
|
CollectEnvironment(document.RuntimeComponents);
|
||||||
|
logger.Info("Suche SAP-Runtime-Dateien in begrenzten Installationspfaden.");
|
||||||
CollectRuntimeFiles(configuredPaths);
|
CollectRuntimeFiles(configuredPaths);
|
||||||
|
logger.Info("Prüfe sichere Metadaten lokaler PSE-/Security-Dateien.");
|
||||||
CollectSecurityFiles(configuredPaths);
|
CollectSecurityFiles(configuredPaths);
|
||||||
if (includeCertificates)
|
if (includeCertificates)
|
||||||
{
|
{
|
||||||
|
logger.Info("Lese SAP-relevante Zertifikatsmetadaten.");
|
||||||
CollectCertificates();
|
CollectCertificates();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.Detail(
|
||||||
|
"Zertifikatserfassung ist per Konfiguration deaktiviert.");
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Info("SAP-Runtime-Erfassung abgeschlossen: Komponenten="
|
||||||
|
+ document.RuntimeComponents.Count
|
||||||
|
+ "; Security-Materialien="
|
||||||
|
+ document.SecurityMaterials.Count
|
||||||
|
+ ".");
|
||||||
|
|
||||||
if (!document.RuntimeComponents.Any(item =>
|
if (!document.RuntimeComponents.Any(item =>
|
||||||
string.Equals(
|
string.Equals(
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
using System.Management;
|
using System.Management;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Security;
|
||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
using BizTalkSapEnvironmentInventory.Configuration;
|
using BizTalkSapEnvironmentInventory.Configuration;
|
||||||
|
using BizTalkSapEnvironmentInventory.Infrastructure;
|
||||||
using BizTalkSapEnvironmentInventory.Models;
|
using BizTalkSapEnvironmentInventory.Models;
|
||||||
|
|
||||||
namespace BizTalkSapEnvironmentInventory.Collectors
|
namespace BizTalkSapEnvironmentInventory.Collectors
|
||||||
@@ -17,10 +22,17 @@ namespace BizTalkSapEnvironmentInventory.Collectors
|
|||||||
private const string AdministrationKey = BizTalkKey + @"\Administration";
|
private const string AdministrationKey = BizTalkKey + @"\Administration";
|
||||||
|
|
||||||
private readonly CommandLineOptions options;
|
private readonly CommandLineOptions options;
|
||||||
|
private readonly ConsoleFileLogger logger;
|
||||||
|
private readonly TimeSpan timeout;
|
||||||
|
|
||||||
public SystemCollector(CommandLineOptions options)
|
public SystemCollector(
|
||||||
|
CommandLineOptions options,
|
||||||
|
ConsoleFileLogger logger,
|
||||||
|
int timeoutSeconds)
|
||||||
{
|
{
|
||||||
this.options = options;
|
this.options = options;
|
||||||
|
this.logger = logger;
|
||||||
|
timeout = TimeSpan.FromSeconds(Math.Max(5, timeoutSeconds));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -43,8 +55,11 @@ namespace BizTalkSapEnvironmentInventory.Collectors
|
|||||||
".NET Runtime",
|
".NET Runtime",
|
||||||
Environment.Version.ToString()));
|
Environment.Version.ToString()));
|
||||||
|
|
||||||
|
logger.Info("Lese lokale Windows-Betriebssysteminformationen.");
|
||||||
CollectOperatingSystem(document);
|
CollectOperatingSystem(document);
|
||||||
|
logger.Info("Lese lokale BizTalk-Installation aus der Registry.");
|
||||||
CollectBizTalkRegistry(document);
|
CollectBizTalkRegistry(document);
|
||||||
|
logger.Info("Ermittle die BizTalk-Gruppe über MSBTS_GroupSetting.");
|
||||||
CollectBizTalkGroupSetting(document);
|
CollectBizTalkGroupSetting(document);
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(options.ManagementServer))
|
if (!string.IsNullOrWhiteSpace(options.ManagementServer))
|
||||||
@@ -67,6 +82,11 @@ namespace BizTalkSapEnvironmentInventory.Collectors
|
|||||||
document.System.Properties.Add(new NameValueRecord(
|
document.System.Properties.Add(new NameValueRecord(
|
||||||
"BizTalk Management Database",
|
"BizTalk Management Database",
|
||||||
EmptyAsUnknown(document.System.ManagementDatabase)));
|
EmptyAsUnknown(document.System.ManagementDatabase)));
|
||||||
|
logger.Info("Management-Datenbankziel: Server="
|
||||||
|
+ EmptyAsUnknown(document.System.ManagementServer)
|
||||||
|
+ "; Datenbank="
|
||||||
|
+ EmptyAsUnknown(document.System.ManagementDatabase)
|
||||||
|
+ ".");
|
||||||
|
|
||||||
if (!string.Equals(document.EnvironmentName, "ACC", StringComparison.OrdinalIgnoreCase)
|
if (!string.Equals(document.EnvironmentName, "ACC", StringComparison.OrdinalIgnoreCase)
|
||||||
&& !string.Equals(document.EnvironmentName, "PROD", StringComparison.OrdinalIgnoreCase))
|
&& !string.Equals(document.EnvironmentName, "PROD", StringComparison.OrdinalIgnoreCase))
|
||||||
@@ -82,78 +102,146 @@ namespace BizTalkSapEnvironmentInventory.Collectors
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void CollectOperatingSystem(InventoryDocument document)
|
private void CollectOperatingSystem(InventoryDocument document)
|
||||||
{
|
{
|
||||||
using (var searcher = new ManagementObjectSearcher(
|
const string query =
|
||||||
"root\\cimv2",
|
"SELECT Caption,Version,BuildNumber,OSArchitecture,LastBootUpTime "
|
||||||
"SELECT Caption,Version,BuildNumber,OSArchitecture,LastBootUpTime FROM Win32_OperatingSystem"))
|
+ "FROM Win32_OperatingSystem";
|
||||||
|
var timer = Stopwatch.StartNew();
|
||||||
|
logger.Detail("WMI-Namespace: root\\cimv2");
|
||||||
|
logger.Detail("WQL: " + query);
|
||||||
|
try
|
||||||
{
|
{
|
||||||
foreach (ManagementObject item in searcher.Get())
|
using (var searcher = new ManagementObjectSearcher("root\\cimv2", query))
|
||||||
{
|
{
|
||||||
document.System.Properties.Add(new NameValueRecord("Betriebssystem", Value(item, "Caption")));
|
ConfigureSearcher(searcher);
|
||||||
document.System.Properties.Add(new NameValueRecord("Windows-Version", Value(item, "Version")));
|
using (var rows = searcher.Get())
|
||||||
document.System.Properties.Add(new NameValueRecord("Windows-Build", Value(item, "BuildNumber")));
|
{
|
||||||
document.System.Properties.Add(new NameValueRecord("Architektur", Value(item, "OSArchitecture")));
|
var count = 0;
|
||||||
document.System.Properties.Add(new NameValueRecord(
|
foreach (ManagementObject item in rows)
|
||||||
"Letzter Systemstart",
|
{
|
||||||
ConvertWmiDate(Value(item, "LastBootUpTime"))));
|
using (item)
|
||||||
break;
|
{
|
||||||
|
count++;
|
||||||
|
if (count != 1)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.System.Properties.Add(new NameValueRecord(
|
||||||
|
"Betriebssystem",
|
||||||
|
Value(item, "Caption")));
|
||||||
|
document.System.Properties.Add(new NameValueRecord(
|
||||||
|
"Windows-Version",
|
||||||
|
Value(item, "Version")));
|
||||||
|
document.System.Properties.Add(new NameValueRecord(
|
||||||
|
"Windows-Build",
|
||||||
|
Value(item, "BuildNumber")));
|
||||||
|
document.System.Properties.Add(new NameValueRecord(
|
||||||
|
"Architektur",
|
||||||
|
Value(item, "OSArchitecture")));
|
||||||
|
document.System.Properties.Add(new NameValueRecord(
|
||||||
|
"Letzter Systemstart",
|
||||||
|
ConvertWmiDate(Value(item, "LastBootUpTime"))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
timer.Stop();
|
||||||
|
logger.Info("Windows-WMI-Abfrage abgeschlossen: Datensätze="
|
||||||
|
+ count + "; Dauer=" + timer.ElapsedMilliseconds + " ms.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (Exception exception) when (IsRecoverableWmiException(exception))
|
||||||
|
{
|
||||||
|
timer.Stop();
|
||||||
|
document.Findings.Add(new Finding
|
||||||
|
{
|
||||||
|
Severity = "Warnung",
|
||||||
|
Area = "Windows WMI",
|
||||||
|
Message = "Win32_OperatingSystem konnte nicht gelesen werden: "
|
||||||
|
+ exception.Message,
|
||||||
|
RecommendedAction =
|
||||||
|
"Lokalen WMI-Dienst und Leseberechtigungen prüfen; "
|
||||||
|
+ "die BizTalk-Erfassung wird fortgesetzt.",
|
||||||
|
Owner = "Windows-/BizTalk-Betrieb"
|
||||||
|
});
|
||||||
|
logger.Exception("Windows-WMI-Abfrage fehlgeschlagen", exception);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void CollectBizTalkRegistry(InventoryDocument document)
|
private void CollectBizTalkRegistry(InventoryDocument document)
|
||||||
{
|
{
|
||||||
foreach (var view in new[] { RegistryView.Registry64, RegistryView.Registry32 })
|
foreach (var view in new[] { RegistryView.Registry64, RegistryView.Registry32 })
|
||||||
{
|
{
|
||||||
using (var baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, view))
|
logger.Detail("Prüfe BizTalk-Registryansicht: " + view + ".");
|
||||||
using (var product = baseKey.OpenSubKey(BizTalkKey, false))
|
try
|
||||||
{
|
{
|
||||||
if (product != null)
|
using (var baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, view))
|
||||||
|
using (var product = baseKey.OpenSubKey(BizTalkKey, false))
|
||||||
{
|
{
|
||||||
AddRegistryValue(document, product, "ProductVersion", "BizTalk Produktversion", view);
|
if (product != null)
|
||||||
AddRegistryValue(document, product, "ProductName", "BizTalk Produktname", view);
|
|
||||||
AddRegistryValue(document, product, "Edition", "BizTalk Edition", view);
|
|
||||||
var installPath = FirstRegistryValue(
|
|
||||||
product,
|
|
||||||
"InstallPath",
|
|
||||||
"BizTalkServerInstallPath",
|
|
||||||
"Path");
|
|
||||||
if (!string.IsNullOrWhiteSpace(installPath)
|
|
||||||
&& string.IsNullOrWhiteSpace(document.System.BizTalkInstallPath))
|
|
||||||
{
|
{
|
||||||
document.System.BizTalkInstallPath = installPath;
|
AddRegistryValue(document, product, "ProductVersion", "BizTalk Produktversion", view);
|
||||||
document.System.Properties.Add(new NameValueRecord(
|
AddRegistryValue(document, product, "ProductName", "BizTalk Produktname", view);
|
||||||
"BizTalk Installationspfad",
|
AddRegistryValue(document, product, "Edition", "BizTalk Edition", view);
|
||||||
installPath,
|
var installPath = FirstRegistryValue(
|
||||||
"Registry " + view));
|
product,
|
||||||
|
"InstallPath",
|
||||||
|
"BizTalkServerInstallPath",
|
||||||
|
"Path");
|
||||||
|
if (!string.IsNullOrWhiteSpace(installPath)
|
||||||
|
&& string.IsNullOrWhiteSpace(document.System.BizTalkInstallPath))
|
||||||
|
{
|
||||||
|
document.System.BizTalkInstallPath = installPath;
|
||||||
|
document.System.Properties.Add(new NameValueRecord(
|
||||||
|
"BizTalk Installationspfad",
|
||||||
|
installPath,
|
||||||
|
"Registry " + view));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, view))
|
||||||
|
using (var administration = baseKey.OpenSubKey(AdministrationKey, false))
|
||||||
|
{
|
||||||
|
if (administration == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(document.System.ManagementServer))
|
||||||
|
{
|
||||||
|
document.System.ManagementServer = FirstRegistryValue(
|
||||||
|
administration,
|
||||||
|
"MgmtDBServer",
|
||||||
|
"ManagementDBServer");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(document.System.ManagementDatabase))
|
||||||
|
{
|
||||||
|
document.System.ManagementDatabase = FirstRegistryValue(
|
||||||
|
administration,
|
||||||
|
"MgmtDBName",
|
||||||
|
"ManagementDBName");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (Exception exception) when (IsRecoverableRegistryException(exception))
|
||||||
using (var baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, view))
|
|
||||||
using (var administration = baseKey.OpenSubKey(AdministrationKey, false))
|
|
||||||
{
|
{
|
||||||
if (administration == null)
|
document.Findings.Add(new Finding
|
||||||
{
|
{
|
||||||
continue;
|
Severity = "Hinweis",
|
||||||
}
|
Area = "BizTalk Registry " + view,
|
||||||
|
Message = "BizTalk-Registryansicht konnte nicht gelesen werden: "
|
||||||
if (string.IsNullOrWhiteSpace(document.System.ManagementServer))
|
+ exception.Message,
|
||||||
{
|
RecommendedAction =
|
||||||
document.System.ManagementServer = FirstRegistryValue(
|
"Berechtigungen prüfen; WMI oder explizite "
|
||||||
administration,
|
+ "--management-Parameter können die Ermittlung übernehmen.",
|
||||||
"MgmtDBServer",
|
Owner = "Windows-/BizTalk-Betrieb"
|
||||||
"ManagementDBServer");
|
});
|
||||||
}
|
logger.Exception(
|
||||||
|
"BizTalk-Registryansicht " + view + " fehlgeschlagen",
|
||||||
if (string.IsNullOrWhiteSpace(document.System.ManagementDatabase))
|
exception);
|
||||||
{
|
|
||||||
document.System.ManagementDatabase = FirstRegistryValue(
|
|
||||||
administration,
|
|
||||||
"MgmtDBName",
|
|
||||||
"ManagementDBName");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,64 +258,138 @@ namespace BizTalkSapEnvironmentInventory.Collectors
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void CollectBizTalkGroupSetting(InventoryDocument document)
|
private void CollectBizTalkGroupSetting(InventoryDocument document)
|
||||||
{
|
{
|
||||||
|
const string query =
|
||||||
|
"SELECT Name,MgmtDbServerName,MgmtDbName FROM MSBTS_GroupSetting";
|
||||||
|
var timer = Stopwatch.StartNew();
|
||||||
|
logger.Detail(@"WMI-Namespace: \\.\root\MicrosoftBizTalkServer");
|
||||||
|
logger.Detail("WQL: " + query);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
var scope = CreateBizTalkScope();
|
||||||
|
logger.Info("Verbinde lokal mit dem BizTalk-WMI-Namespace.");
|
||||||
|
scope.Connect();
|
||||||
using (var searcher = new ManagementObjectSearcher(
|
using (var searcher = new ManagementObjectSearcher(
|
||||||
@"root\MicrosoftBizTalkServer",
|
scope,
|
||||||
"SELECT Name,MgmtDbServerName,MgmtDbName,BizTalkAdministratorGroup,"
|
new ObjectQuery(query),
|
||||||
+ "BizTalkOperatorGroup,BizTalkReadOnlyUserGroup,SSOServerName "
|
CreateEnumerationOptions()))
|
||||||
+ "FROM MSBTS_GroupSetting"))
|
|
||||||
{
|
{
|
||||||
foreach (ManagementObject item in searcher.Get())
|
using (var rows = searcher.Get())
|
||||||
{
|
{
|
||||||
var managementServer = Value(item, "MgmtDbServerName");
|
var count = 0;
|
||||||
var managementDatabase = Value(item, "MgmtDbName");
|
foreach (ManagementObject item in rows)
|
||||||
if (!string.IsNullOrWhiteSpace(managementServer))
|
|
||||||
{
|
{
|
||||||
document.System.ManagementServer = managementServer;
|
using (item)
|
||||||
}
|
{
|
||||||
if (!string.IsNullOrWhiteSpace(managementDatabase))
|
count++;
|
||||||
{
|
if (count != 1)
|
||||||
document.System.ManagementDatabase = managementDatabase;
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var managementServer = Value(item, "MgmtDbServerName");
|
||||||
|
var managementDatabase = Value(item, "MgmtDbName");
|
||||||
|
if (!string.IsNullOrWhiteSpace(managementServer))
|
||||||
|
{
|
||||||
|
document.System.ManagementServer = managementServer;
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrWhiteSpace(managementDatabase))
|
||||||
|
{
|
||||||
|
document.System.ManagementDatabase = managementDatabase;
|
||||||
|
}
|
||||||
|
|
||||||
|
AddWmiValue(document, item, "Name", "BizTalk Gruppenname");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AddWmiValue(document, item, "Name", "BizTalk Gruppenname");
|
timer.Stop();
|
||||||
AddWmiValue(
|
logger.Info("MSBTS_GroupSetting abgeschlossen: Datensätze="
|
||||||
document,
|
+ count + "; Dauer=" + timer.ElapsedMilliseconds + " ms.");
|
||||||
item,
|
if (count == 0)
|
||||||
"BizTalkAdministratorGroup",
|
{
|
||||||
"BizTalk Administratorengruppe");
|
AddGroupSettingFinding(
|
||||||
AddWmiValue(
|
document,
|
||||||
document,
|
"MSBTS_GroupSetting lieferte keinen Datensatz.");
|
||||||
item,
|
}
|
||||||
"BizTalkOperatorGroup",
|
else if (count > 1)
|
||||||
"BizTalk Operatorengruppe");
|
{
|
||||||
AddWmiValue(
|
AddGroupSettingFinding(
|
||||||
document,
|
document,
|
||||||
item,
|
"MSBTS_GroupSetting lieferte "
|
||||||
"BizTalkReadOnlyUserGroup",
|
+ count
|
||||||
"BizTalk ReadOnly-Gruppe");
|
+ " Datensätze; der erste Datensatz wurde verwendet.");
|
||||||
AddWmiValue(document, item, "SSOServerName", "Enterprise SSO Server");
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (ManagementException exception)
|
catch (Exception exception) when (IsRecoverableWmiException(exception))
|
||||||
{
|
{
|
||||||
document.Findings.Add(new Finding
|
timer.Stop();
|
||||||
{
|
AddGroupSettingFinding(
|
||||||
Severity = "Hinweis",
|
document,
|
||||||
Area = "BizTalk GroupSetting",
|
"MSBTS_GroupSetting konnte für die Management-DB-Erkennung "
|
||||||
Message = "MSBTS_GroupSetting konnte für die Management-DB-Erkennung nicht gelesen werden: "
|
+ "nicht gelesen werden: " + exception.Message);
|
||||||
+ exception.Message,
|
logger.Exception("MSBTS_GroupSetting fehlgeschlagen", exception);
|
||||||
RecommendedAction = "Registry-Ergebnis prüfen oder --management-server explizit angeben.",
|
|
||||||
Owner = "BizTalk-Betrieb"
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ManagementScope CreateBizTalkScope()
|
||||||
|
{
|
||||||
|
var scope = new ManagementScope(@"\\.\root\MicrosoftBizTalkServer");
|
||||||
|
scope.Options.Timeout = timeout;
|
||||||
|
scope.Options.EnablePrivileges = false;
|
||||||
|
scope.Options.Impersonation = ImpersonationLevel.Impersonate;
|
||||||
|
return scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
private EnumerationOptions CreateEnumerationOptions()
|
||||||
|
{
|
||||||
|
return new EnumerationOptions
|
||||||
|
{
|
||||||
|
ReturnImmediately = false,
|
||||||
|
Rewindable = false,
|
||||||
|
Timeout = timeout
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ConfigureSearcher(ManagementObjectSearcher searcher)
|
||||||
|
{
|
||||||
|
searcher.Options.ReturnImmediately = false;
|
||||||
|
searcher.Options.Rewindable = false;
|
||||||
|
searcher.Options.Timeout = timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AddGroupSettingFinding(
|
||||||
|
InventoryDocument document,
|
||||||
|
string message)
|
||||||
|
{
|
||||||
|
document.Findings.Add(new Finding
|
||||||
|
{
|
||||||
|
Severity = "Hinweis",
|
||||||
|
Area = "BizTalk GroupSetting",
|
||||||
|
Message = message,
|
||||||
|
RecommendedAction =
|
||||||
|
"Registry-Ergebnis prüfen oder --management-server und "
|
||||||
|
+ "--management-database explizit angeben.",
|
||||||
|
Owner = "BizTalk-Betrieb"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsRecoverableWmiException(Exception exception)
|
||||||
|
{
|
||||||
|
return exception is ManagementException
|
||||||
|
|| exception is COMException
|
||||||
|
|| exception is UnauthorizedAccessException;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsRecoverableRegistryException(Exception exception)
|
||||||
|
{
|
||||||
|
return exception is IOException
|
||||||
|
|| exception is SecurityException
|
||||||
|
|| exception is UnauthorizedAccessException;
|
||||||
|
}
|
||||||
|
|
||||||
private static void AddWmiValue(
|
private static void AddWmiValue(
|
||||||
InventoryDocument document,
|
InventoryDocument document,
|
||||||
ManagementBaseObject item,
|
ManagementBaseObject item,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace BizTalkSapEnvironmentInventory.Infrastructure
|
namespace BizTalkSapEnvironmentInventory.Infrastructure
|
||||||
@@ -26,6 +27,11 @@ namespace BizTalkSapEnvironmentInventory.Infrastructure
|
|||||||
Write("INFO", message);
|
Write("INFO", message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Detail(string message)
|
||||||
|
{
|
||||||
|
Write("DETAIL", message);
|
||||||
|
}
|
||||||
|
|
||||||
public void Warning(string message)
|
public void Warning(string message)
|
||||||
{
|
{
|
||||||
Write("WARNUNG", message);
|
Write("WARNUNG", message);
|
||||||
@@ -36,6 +42,62 @@ namespace BizTalkSapEnvironmentInventory.Infrastructure
|
|||||||
Write("FEHLER", message);
|
Write("FEHLER", message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Protokolliert die vollständige Ausnahmekette einschließlich HRESULT,
|
||||||
|
/// WMI-Status und Stacktrace, damit Providerfehler vor Ort analysierbar bleiben.
|
||||||
|
/// </summary>
|
||||||
|
public void Exception(string context, Exception exception)
|
||||||
|
{
|
||||||
|
if (exception == null)
|
||||||
|
{
|
||||||
|
Error(context + ": Unbekannter Fehler ohne Ausnahmeobjekt.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Error(context + ": " + exception.Message);
|
||||||
|
var current = exception;
|
||||||
|
var depth = 0;
|
||||||
|
while (current != null)
|
||||||
|
{
|
||||||
|
Detail(string.Format(
|
||||||
|
CultureInfo.InvariantCulture,
|
||||||
|
"Ausnahme[{0}]: Typ={1}; HRESULT=0x{2:X8}; Meldung={3}",
|
||||||
|
depth,
|
||||||
|
current.GetType().FullName,
|
||||||
|
current.HResult,
|
||||||
|
current.Message));
|
||||||
|
|
||||||
|
if (string.Equals(
|
||||||
|
current.GetType().FullName,
|
||||||
|
"System.Management.ManagementException",
|
||||||
|
StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
var errorCodeProperty = current.GetType().GetProperty(
|
||||||
|
"ErrorCode",
|
||||||
|
BindingFlags.Instance | BindingFlags.Public);
|
||||||
|
if (errorCodeProperty != null)
|
||||||
|
{
|
||||||
|
Detail("WMI-Status: " + Convert.ToString(
|
||||||
|
errorCodeProperty.GetValue(current, null),
|
||||||
|
CultureInfo.InvariantCulture));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(current.StackTrace))
|
||||||
|
{
|
||||||
|
Detail("Stacktrace[" + depth + "]:" + Environment.NewLine
|
||||||
|
+ current.StackTrace);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reflection kapselt ExplorerOM-Fehler häufig in TargetInvocationException.
|
||||||
|
var targetInvocation = current as TargetInvocationException;
|
||||||
|
current = targetInvocation != null && targetInvocation.InnerException != null
|
||||||
|
? targetInvocation.InnerException
|
||||||
|
: current.InnerException;
|
||||||
|
depth++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
writer.Dispose();
|
writer.Dispose();
|
||||||
|
|||||||
@@ -58,10 +58,12 @@ namespace BizTalkSapEnvironmentInventory.Infrastructure
|
|||||||
Severity = required ? "Fehler" : "Warnung",
|
Severity = required ? "Fehler" : "Warnung",
|
||||||
Area = name,
|
Area = name,
|
||||||
Message = "Datenerfassung fehlgeschlagen: " + exception.Message,
|
Message = "Datenerfassung fehlgeschlagen: " + exception.Message,
|
||||||
RecommendedAction = "Logdatei, Berechtigungen und lokale BizTalk-Konfiguration prüfen.",
|
RecommendedAction =
|
||||||
|
"Logdatei, WMI-Provider, ExplorerOM-Installation, Berechtigungen "
|
||||||
|
+ "und lokale BizTalk-Konfiguration prüfen.",
|
||||||
Owner = "BizTalk-Betrieb"
|
Owner = "BizTalk-Betrieb"
|
||||||
});
|
});
|
||||||
logger.Error(name + ": " + exception.Message);
|
logger.Exception(name, exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using System;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
using BizTalkSapEnvironmentInventory.Collectors;
|
using BizTalkSapEnvironmentInventory.Collectors;
|
||||||
@@ -23,6 +24,7 @@ namespace BizTalkSapEnvironmentInventory.Infrastructure
|
|||||||
{
|
{
|
||||||
TestSanitizer();
|
TestSanitizer();
|
||||||
TestBindingParser();
|
TestBindingParser();
|
||||||
|
TestLoggerDiagnostics();
|
||||||
TestDocxPackage();
|
TestDocxPackage();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,6 +238,47 @@ namespace BizTalkSapEnvironmentInventory.Infrastructure
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void TestLoggerDiagnostics()
|
||||||
|
{
|
||||||
|
var directory = CreateTemporaryDirectory();
|
||||||
|
var path = Path.Combine(directory, "diagnostics.log");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var exception = new TargetInvocationException(
|
||||||
|
new InvalidOperationException("Innerer Diagnosefehler"));
|
||||||
|
using (var logger = new ConsoleFileLogger(path))
|
||||||
|
{
|
||||||
|
var originalOutput = Console.Out;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Die absichtlich erzeugte Testausnahme soll den Self-Test
|
||||||
|
// auf der Konsole nicht wie einen echten Laufzeitfehler aussehen lassen.
|
||||||
|
Console.SetOut(TextWriter.Null);
|
||||||
|
logger.Exception("Self-Test-Diagnose", exception);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Console.SetOut(originalOutput);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var log = File.ReadAllText(path);
|
||||||
|
Assert(
|
||||||
|
log.Contains(typeof(TargetInvocationException).FullName),
|
||||||
|
"Logger dokumentierte die Reflection-Ausnahme nicht.");
|
||||||
|
Assert(
|
||||||
|
log.Contains(typeof(InvalidOperationException).FullName),
|
||||||
|
"Logger dokumentierte die innere Ausnahme nicht.");
|
||||||
|
Assert(
|
||||||
|
log.Contains("HRESULT=0x"),
|
||||||
|
"Logger dokumentierte den HRESULT nicht.");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
DeleteDirectory(directory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void AssertPackageNamespaces(ZipArchive archive)
|
private static void AssertPackageNamespaces(ZipArchive archive)
|
||||||
{
|
{
|
||||||
var contentTypes = LoadEntryXml(archive, "[Content_Types].xml");
|
var contentTypes = LoadEntryXml(archive, "[Content_Types].xml");
|
||||||
|
|||||||
@@ -82,21 +82,36 @@ namespace BizTalkSapEnvironmentInventory
|
|||||||
logger.Info("Umgebung: " + options.EnvironmentName);
|
logger.Info("Umgebung: " + options.EnvironmentName);
|
||||||
logger.Info("Server: " + Environment.MachineName);
|
logger.Info("Server: " + Environment.MachineName);
|
||||||
logger.Info("Ausgabeordner: " + options.OutputDirectory);
|
logger.Info("Ausgabeordner: " + options.OutputDirectory);
|
||||||
logger.Info("Modus: read-only");
|
logger.Info(
|
||||||
|
"Modus: read-only; ExplorerOM für Anwendungen, WMI für "
|
||||||
|
+ "Gruppenermittlung und optionale Artefakte.");
|
||||||
|
logger.Detail("Logdatei: " + logPath);
|
||||||
|
logger.Detail("DOCX-Zieldatei: " + reportPath);
|
||||||
|
|
||||||
|
var wmiTimeoutSeconds = ReadIntSetting("WmiTimeoutSeconds", 30);
|
||||||
|
var processTimeoutSeconds = ReadIntSetting("ProcessTimeoutSeconds", 120);
|
||||||
|
var maxArtifactDetails = ReadIntSetting("MaxArtifactDetailsPerType", 5000);
|
||||||
|
logger.Detail("Konfiguration WmiTimeoutSeconds=" + wmiTimeoutSeconds + ".");
|
||||||
|
logger.Detail("Konfiguration ProcessTimeoutSeconds=" + processTimeoutSeconds + ".");
|
||||||
|
logger.Detail("Konfiguration MaxArtifactDetailsPerType="
|
||||||
|
+ maxArtifactDetails + ".");
|
||||||
|
|
||||||
var safeCollector = new SafeCollector(document, logger);
|
var safeCollector = new SafeCollector(document, logger);
|
||||||
safeCollector.Execute(
|
safeCollector.Execute(
|
||||||
"System und BizTalk-Installation",
|
"System und BizTalk-Installation",
|
||||||
true,
|
true,
|
||||||
() => new SystemCollector(options).Collect(document));
|
() => new SystemCollector(
|
||||||
|
options,
|
||||||
|
logger,
|
||||||
|
wmiTimeoutSeconds).Collect(document));
|
||||||
safeCollector.Execute(
|
safeCollector.Execute(
|
||||||
"BizTalk-Anwendungen und WMI-Artefakte",
|
"BizTalk-Anwendungen und optionale WMI-Artefakte",
|
||||||
true,
|
true,
|
||||||
() => new BizTalkWmiCollector(
|
() => new BizTalkWmiCollector(
|
||||||
document,
|
document,
|
||||||
logger,
|
logger,
|
||||||
ReadIntSetting("WmiTimeoutSeconds", 30),
|
wmiTimeoutSeconds,
|
||||||
ReadIntSetting("MaxArtifactDetailsPerType", 5000)).Collect());
|
maxArtifactDetails).Collect());
|
||||||
safeCollector.Execute(
|
safeCollector.Execute(
|
||||||
"SAP-Bindingparameter",
|
"SAP-Bindingparameter",
|
||||||
true,
|
true,
|
||||||
@@ -104,7 +119,7 @@ namespace BizTalkSapEnvironmentInventory
|
|||||||
options,
|
options,
|
||||||
document,
|
document,
|
||||||
logger,
|
logger,
|
||||||
ReadIntSetting("ProcessTimeoutSeconds", 120)).Collect());
|
processTimeoutSeconds).Collect());
|
||||||
safeCollector.Execute(
|
safeCollector.Execute(
|
||||||
"SAP NCo, SNC und Zertifikatsmetadaten",
|
"SAP NCo, SNC und Zertifikatsmetadaten",
|
||||||
false,
|
false,
|
||||||
@@ -125,11 +140,22 @@ namespace BizTalkSapEnvironmentInventory
|
|||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
logger.Error("Word-Dokument konnte nicht erzeugt werden: " + exception);
|
logger.Exception(
|
||||||
|
"Word-Dokument konnte nicht erzeugt werden",
|
||||||
|
exception);
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
var exitCode = document.HasRequiredSectionFailure ? 1 : 0;
|
var exitCode = document.HasRequiredSectionFailure ? 1 : 0;
|
||||||
|
logger.Info("Ergebnisübersicht: Anwendungen=" + document.Applications.Count
|
||||||
|
+ "; SAP-Endpunkte=" + document.SapEndpoints.Count
|
||||||
|
+ "; Findings=" + document.Findings.Count + ".");
|
||||||
|
if (exitCode != 0)
|
||||||
|
{
|
||||||
|
logger.Warning(
|
||||||
|
"Der Bericht wurde erzeugt, ist wegen mindestens eines "
|
||||||
|
+ "Pflichtfehlers aber unvollständig.");
|
||||||
|
}
|
||||||
logger.Info("Erfassung beendet. Exitcode: " + exitCode);
|
logger.Info("Erfassung beendet. Exitcode: " + exitCode);
|
||||||
return exitCode;
|
return exitCode;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -167,9 +167,10 @@ namespace BizTalkSapEnvironmentInventory.Reporting
|
|||||||
WriteHeading(writer, "5. Vollständige BizTalk-Anwendungsliste", 1);
|
WriteHeading(writer, "5. Vollständige BizTalk-Anwendungsliste", 1);
|
||||||
WriteParagraph(
|
WriteParagraph(
|
||||||
writer,
|
writer,
|
||||||
"Die Liste stammt primär aus MSBTS_Application. Artefaktzahlen werden aus den "
|
"Die vollständige Anwendungsliste stammt aus dem read-only BizTalk Explorer "
|
||||||
+ "lokal verfügbaren BizTalk-WMI-Klassen gebildet; optionale WMI-Klassen können "
|
+ "Object Model. Artefaktzahlen werden aus lokal verfügbaren, optionalen "
|
||||||
+ "versionsabhängig fehlen und werden dann im Erfassungsstatus bzw. als Finding ausgewiesen.",
|
+ "BizTalk-WMI-Klassen gebildet; fehlende Klassen werden im Log und als "
|
||||||
|
+ "Finding ausgewiesen, ohne die Anwendungsliste zu verwerfen.",
|
||||||
"Normal",
|
"Normal",
|
||||||
false,
|
false,
|
||||||
null);
|
null);
|
||||||
|
|||||||
Reference in New Issue
Block a user