Fix BizTalk catalog collection and diagnostics
Build und Test / build (push) Has been cancelled
Build und Test / build (push) Has been cancelled
This commit is contained in:
+101
-30
@@ -21,7 +21,7 @@ ACC und PROD besitzen jeweils einen lokalen BizTalk Server 2020. Ein Prozess auf
|
||||
```text
|
||||
ACC BizTalk Server PROD BizTalk Server
|
||||
| |
|
||||
| lokales WMI | lokales WMI
|
||||
| WMI + ExplorerOM | WMI + ExplorerOM
|
||||
v v
|
||||
ACC JSON + lokale XLSX PROD JSON + lokale XLSX
|
||||
\ /
|
||||
@@ -42,10 +42,9 @@ CommandLineOptions
|
||||
+-- validiert lokalen Modus oder Merge-Modus
|
||||
|
||||
BizTalkWmiCollector
|
||||
+-- MSBTS_Application
|
||||
+-- MSBTS_ReceivePort
|
||||
+-- MSBTS_SendPort
|
||||
+-- MSBTS_ReceiveLocation
|
||||
+-- lokales MSBTS_GroupSetting per WMI
|
||||
+-- lädt Microsoft.BizTalk.ExplorerOM aus GAC/Installationspfad
|
||||
+-- Anwendungen und Ports per BtsCatalogExplorer
|
||||
|
||||
SnapshotStore
|
||||
+-- normalisiert je Anwendung Adapterzählungen
|
||||
@@ -58,37 +57,83 @@ XlsxReportWriter
|
||||
+-- Titel, Filter, Freeze Pane und Summenzeile
|
||||
```
|
||||
|
||||
## 4. WMI-Datenmodell
|
||||
## 4. Datenzugriff
|
||||
|
||||
### 4.1 Anwendungen
|
||||
### 4.1 Ursache des Fehlers `Invalid class`
|
||||
|
||||
`MSBTS_Application` ist die Pflichtquelle der Anwendungsliste. Verwendet wird nur `Name` beziehungsweise `ApplicationName`.
|
||||
Die ursprüngliche Implementierung führte als erste Abfrage
|
||||
`SELECT * FROM MSBTS_Application` im Namespace
|
||||
`root\MicrosoftBizTalkServer` aus. Das von BizTalk Server dokumentierte WMI-Schema
|
||||
enthält keine Klasse `MSBTS_Application`. WMI antwortet deshalb unabhängig von
|
||||
einer administrativ gestarteten Konsole mit `Invalid class`.
|
||||
|
||||
Die Abfrage muss mindestens eine Anwendung liefern. Andernfalls ist der lokale Lauf unvollständig.
|
||||
Auch die WMI-Portklassen bilden die fachliche Anwendungshierarchie nicht so
|
||||
vollständig ab, dass damit Anwendungen ohne Ports zuverlässig erfasst werden
|
||||
könnten. Der Fehler wird daher nicht nur abgefangen; der ungeeignete Zugriffsweg
|
||||
wurde ersetzt.
|
||||
|
||||
### 4.2 Receive-Endpunkte
|
||||
### 4.2 Lokale Gruppenermittlung per WMI
|
||||
|
||||
Eine `MSBTS_ReceiveLocation` zählt als ein Endpunkt. Der Adapter wird defensiv aus `AdapterName`, `TransportType` oder `PTTransportType` gelesen.
|
||||
Verwendeter Namespace:
|
||||
|
||||
Da eine Receive Location abhängig vom WMI-Provider keine direkte `ApplicationName` liefern kann, wird vorher `MSBTS_ReceivePort` gelesen. Über `ReceivePortName` wird die Anwendung aufgelöst. Ein Receive Port zählt selbst nicht als Endpunkt.
|
||||
```text
|
||||
\\.\root\MicrosoftBizTalkServer
|
||||
```
|
||||
|
||||
### 4.3 Send-Endpunkte
|
||||
Verwendete Abfrage:
|
||||
|
||||
Bei `MSBTS_SendPort` zählt die primäre Transportkonfiguration als ein Endpunkt. Ist eine sekundäre Transportkonfiguration vorhanden, zählt sie als zusätzlicher Endpunkt.
|
||||
```sql
|
||||
SELECT Name, MgmtDbServerName, MgmtDbName FROM MSBTS_GroupSetting
|
||||
```
|
||||
|
||||
Primärer Adapter:
|
||||
Der Punkt im Namespace erzwingt den lokalen Computer. Die Abfrage liefert den
|
||||
BizTalk-Gruppennamen und das Ziel der Management-Datenbank. Der konfigurierte
|
||||
WMI-Timeout wird angewendet. Kennwörter werden weder benötigt noch protokolliert.
|
||||
|
||||
- `PTTransportType`
|
||||
- Fallback `PrimaryTransportType`
|
||||
- Fallback `AdapterName`
|
||||
- Fallback `TransportType`
|
||||
### 4.3 Kataloglesung per Explorer Object Model
|
||||
|
||||
Sekundärer Adapter:
|
||||
Die Assembly `Microsoft.BizTalk.ExplorerOM.dll` wird zur Laufzeit geladen. Dadurch
|
||||
bleibt das Quellprojekt auf einem Build-Rechner ohne BizTalk-Assembly kompilierbar;
|
||||
für den echten Inventarlauf müssen die BizTalk-Verwaltungskomponenten lokal
|
||||
installiert sein.
|
||||
|
||||
- `STTransportType`
|
||||
- Fallback `SecondaryTransportType`
|
||||
Ladestrategie:
|
||||
|
||||
Fehlt ein gelieferter Adaptername, wird `Unbekannt` verwendet. Ein Endpunkt ohne Anwendungszuordnung macht den Lauf unvollständig, weil er sonst in der Anwendungssicht verloren ginge.
|
||||
1. normale Typauflösung der bereits verfügbaren Assembly
|
||||
2. Laden aus dem Global Assembly Cache
|
||||
3. kompatible GAC-Auflösung ohne fest verdrahtete Assemblyversion
|
||||
4. bekannte BizTalk-Installationsverzeichnisse unter `Program Files`
|
||||
|
||||
`BtsCatalogExplorer.ConnectionString` verwendet ausschließlich integrierte
|
||||
Windows-Authentifizierung. Die Anwendung ruft weder `SaveChanges` noch eine andere
|
||||
ändernde ExplorerOM-Methode auf.
|
||||
|
||||
Die Eigenschaft `Applications` liefert auch Anwendungen ohne Ports. Pro Anwendung
|
||||
werden nur folgende Eigenschaften gelesen:
|
||||
|
||||
- `Name`
|
||||
- `SendPorts`
|
||||
- `SendPort.PrimaryTransport.TransportType.Name`
|
||||
- `SendPort.SecondaryTransport.TransportType.Name`
|
||||
- `ReceivePorts`
|
||||
- `ReceivePort.ReceiveLocations`
|
||||
- `ReceiveLocation.TransportType.Name`
|
||||
|
||||
### 4.4 Receive-Endpunkte
|
||||
|
||||
Eine Receive Location zählt als ein Endpunkt. Der Adaptername stammt aus
|
||||
`ReceiveLocation.TransportType.Name`. Ein Receive Port ist nur ein Container und
|
||||
zählt selbst nicht als Endpunkt.
|
||||
|
||||
### 4.5 Send-Endpunkte
|
||||
|
||||
Bei einem Send Port zählt die primäre Transportkonfiguration als ein Endpunkt. Ist
|
||||
eine sekundäre Transportkonfiguration vorhanden, zählt sie als zusätzlicher
|
||||
Endpunkt.
|
||||
|
||||
Fehlt ein Adaptername, wird `Unbekannt` verwendet. Die Anwendung ist durch die
|
||||
Iteration innerhalb von `Application.SendPorts` beziehungsweise
|
||||
`Application.ReceivePorts` eindeutig vorgegeben.
|
||||
|
||||
## 5. Snapshot-Format
|
||||
|
||||
@@ -170,7 +215,8 @@ Der Filter umfasst nur Kopf- und Datenzeilen, nicht die Summenzeile. Die ersten
|
||||
|
||||
## 8. Fehlerverhalten
|
||||
|
||||
Alle vier WMI-Abfragen bilden einen Pflichtabschnitt. Ein Fehler führt zu:
|
||||
Die WMI-Gruppenermittlung und die vollständige ExplorerOM-Kataloglesung bilden
|
||||
gemeinsam einen Pflichtabschnitt. Ein Fehler führt zu:
|
||||
|
||||
- Exitcode `1`
|
||||
- Logeintrag
|
||||
@@ -181,11 +227,33 @@ Der Merge lehnt einen solchen JSON-Snapshot ab. Dadurch werden technische Lücke
|
||||
|
||||
Ungültige Parameter, fehlende Dateien, fehlerhaftes JSON und Schreibfehler liefern Exitcode `2`.
|
||||
|
||||
### 8.1 Diagnosedaten
|
||||
|
||||
Fortschrittsmeldungen werden gleichzeitig auf die Konsole und in eine
|
||||
UTF-8-Logdatei geschrieben. Die Detailprotokollierung umfasst:
|
||||
|
||||
- Laufzeit-, Betriebssystem- und Prozessarchitektur
|
||||
- Windows-Identität und Administratorstatus
|
||||
- WMI-Namespace und Timeout
|
||||
- vollständige WQL-Anweisung, Laufzeit und Datensatzanzahl
|
||||
- BizTalk-Gruppe sowie Management-DB-Server und -Name
|
||||
- ExplorerOM-Assemblyname, Version und Dateipfad
|
||||
- jede gelesene Anwendung und jeden gezählten Endpunkt mit Adaptertyp
|
||||
- Gesamtzahlen für Anwendungen, Send Ports, sekundäre Transporte, Receive Ports
|
||||
und Receive Locations
|
||||
|
||||
Eine Ausnahme wird mit Typ, Meldung, HRESULT in Hexadezimalschreibweise,
|
||||
`ManagementException.ErrorCode`, inneren Ausnahmen und Stacktrace ausgegeben.
|
||||
`TargetInvocationException` wird dabei bis zur eigentlichen Provider- oder
|
||||
Datenbankausnahme aufgelöst.
|
||||
|
||||
## 9. Sicherheit
|
||||
|
||||
- ausschließlich lesende lokale WMI-Abfragen
|
||||
- keine Änderungen an BizTalk-Konfiguration oder Runtime
|
||||
- keine SQL-Abfragen
|
||||
- ausschließlich lesende Katalogabfragen über das lokale BizTalk Explorer Object Model
|
||||
- keine Aufrufe von `SaveChanges` oder ändernden BizTalk-Methoden
|
||||
- keine selbst formulierten SQL-Abfragen; ExplorerOM liest die Management-Datenbank
|
||||
über integrierte Windows-Authentifizierung
|
||||
- keine Endpunktadressen
|
||||
- keine Binding-Exporte
|
||||
- keine Credentials, Secrets oder Tokens
|
||||
@@ -203,6 +271,7 @@ Baseline:
|
||||
- C# 7.3
|
||||
- keine NuGet-Pakete
|
||||
- kein .NET SDK erforderlich
|
||||
- keine compile-time-Referenz auf `Microsoft.BizTalk.ExplorerOM.dll`
|
||||
|
||||
```cmd
|
||||
scripts\build-release.cmd
|
||||
@@ -227,10 +296,12 @@ Je Umgebung:
|
||||
|
||||
1. `--self-test` erfolgreich ausführen.
|
||||
2. Lokalen Lauf in administrativer `cmd.exe` starten.
|
||||
3. Exitcode `0` und Log prüfen.
|
||||
4. Anwendungsgesamtzahl mit der BizTalk Administration Console vergleichen.
|
||||
5. Send Ports, sekundäre Transporte und Receive Locations stichprobenartig zählen.
|
||||
6. Nur die vollständige JSON-Datei für den Merge verwenden.
|
||||
3. Im Log die erfolgreiche WMI-Abfrage `MSBTS_GroupSetting` prüfen.
|
||||
4. Im Log Assemblyname, Version und Pfad von ExplorerOM prüfen.
|
||||
5. Exitcode `0` und `IsComplete: true` prüfen.
|
||||
6. Anwendungsgesamtzahl mit der BizTalk Administration Console vergleichen.
|
||||
7. Send Ports, sekundäre Transporte und Receive Locations stichprobenartig zählen.
|
||||
8. Nur die vollständige JSON-Datei für den Merge verwenden.
|
||||
|
||||
Nach dem Merge:
|
||||
|
||||
|
||||
@@ -26,12 +26,18 @@ Jeder lokale Lauf erzeugt zusätzlich eine einblättrige Excel-Sicht für die je
|
||||
|
||||
## Erfasste Daten
|
||||
|
||||
Das Tool liest ausschließlich:
|
||||
Das Tool verwendet zwei ausschließlich lesende, lokale BizTalk-Schnittstellen:
|
||||
|
||||
- `MSBTS_Application` für die vollständige Anwendungsliste
|
||||
- `MSBTS_SendPort` für primäre und gegebenenfalls sekundäre Send-Endpunkte
|
||||
- `MSBTS_ReceivePort` nur zur Zuordnung einer Receive Location zur Anwendung
|
||||
- `MSBTS_ReceiveLocation` für Receive-Endpunkte
|
||||
- WMI-Klasse `MSBTS_GroupSetting` im Namespace
|
||||
`root\MicrosoftBizTalkServer`, um Server und Name der BizTalk-Management-Datenbank
|
||||
der lokalen BizTalk-Gruppe zu ermitteln;
|
||||
- das lokal installierte `Microsoft.BizTalk.ExplorerOM`, um Anwendungen, Send Ports,
|
||||
sekundäre Send-Transporte, Receive Ports und Receive Locations vollständig zu lesen.
|
||||
|
||||
Das Explorer Object Model ist notwendig, weil es im BizTalk-WMI-Schema keine Klasse
|
||||
`MSBTS_Application` gibt. Die frühere Abfrage dieser nicht vorhandenen Klasse führte
|
||||
unmittelbar zu `Invalid class` beziehungsweise WMI-Status `InvalidClass`. Adminrechte
|
||||
ändern nicht, welche Klassen ein WMI-Provider bereitstellt.
|
||||
|
||||
Ein Endpunkt ist:
|
||||
|
||||
@@ -48,8 +54,13 @@ Nicht erfasst werden insbesondere Adressen, Credentials, Beschreibungen, Status,
|
||||
- BizTalk Server 2020 auf Windows Server 2019
|
||||
- lokale Ausführung auf dem jeweiligen BizTalk-Server
|
||||
- .NET Framework 4.7.2 oder höher
|
||||
- lokal installierte BizTalk-Verwaltungskomponenten einschließlich
|
||||
`Microsoft.BizTalk.ExplorerOM.dll`
|
||||
- Leserechte auf `root\MicrosoftBizTalkServer`
|
||||
- administrative `cmd.exe` empfohlen
|
||||
- Leserechte auf den BizTalk-Katalog, vorzugsweise über die konfigurierte Gruppe
|
||||
`BizTalk Server Read Only Users` oder eine höher berechtigte BizTalk-Rolle
|
||||
- administrative `cmd.exe` ist für die Diagnose hilfreich, ersetzt aber keine
|
||||
BizTalk- beziehungsweise SQL-Leseberechtigung
|
||||
|
||||
Microsoft Excel, Microsoft Office, PowerShell, Internetzugriff, NuGet und ein .NET SDK werden auf dem Zielserver nicht benötigt.
|
||||
|
||||
@@ -83,6 +94,52 @@ Phase1-BizTalk-Anwendungskatalog-ACC-SERVER-20260727-150000.log
|
||||
|
||||
Nur JSON-Dateien eines Laufs mit Exitcode `0` dürfen zusammengeführt werden. Ein unvollständiger Snapshot trägt `IsComplete: false` und wird vom Merge abgelehnt.
|
||||
|
||||
## Konsolen- und Dateilogging
|
||||
|
||||
Jede Logmeldung wird unmittelbar gleichzeitig auf der Konsole und in der `.log`-Datei
|
||||
ausgegeben. Der lokale Inventarlauf protokolliert unter anderem:
|
||||
|
||||
- Computer, Betriebssystem, CLR- und Prozessarchitektur
|
||||
- Windows-Sicherheitskontext und erkannte Administratorrolle
|
||||
- WMI-Namespace, Timeout, WQL-Abfrage, Dauer und Datensatzanzahl
|
||||
- erkannte BizTalk-Gruppe sowie Management-Datenbankziel ohne Kennwort
|
||||
- geladene ExplorerOM-Assembly einschließlich Version und Pfad
|
||||
- jede Anwendung sowie Send Ports, sekundäre Transporte und Receive Locations
|
||||
- Adaptertyp und Summen je Erfassungsart
|
||||
- bei Fehlern: Ausnahmetyp, Meldung, HRESULT, WMI-Status, innere Ausnahmen und Stacktrace
|
||||
|
||||
Die Logdatei enthält interne Namen und ist wie JSON und XLSX geschützt abzulegen.
|
||||
|
||||
## Fehleranalyse
|
||||
|
||||
### `Invalid class` direkt nach dem Start
|
||||
|
||||
Dieser Fehler wurde in der früheren Version durch
|
||||
`SELECT * FROM MSBTS_Application` verursacht. `MSBTS_Application` ist keine
|
||||
BizTalk-WMI-Klasse. Die aktuelle Version verwendet diese Abfrage nicht mehr.
|
||||
|
||||
Im neuen Log muss stattdessen zunächst diese Abfrage erscheinen:
|
||||
|
||||
```text
|
||||
SELECT Name, MgmtDbServerName, MgmtDbName FROM MSBTS_GroupSetting
|
||||
```
|
||||
|
||||
Tritt weiterhin `InvalidClass` auf, ist damit nicht mehr die Anwendungsklasse
|
||||
gemeint. Dann ist die lokale BizTalk-WMI-Registrierung zu prüfen. Entscheidend sind
|
||||
die protokollierten Felder `WQL`, `WMI-Status` und `HRESULT`.
|
||||
|
||||
### ExplorerOM-Assembly fehlt
|
||||
|
||||
Die BizTalk-Verwaltungskomponenten müssen auf dem ausführenden BizTalk-Server
|
||||
installiert sein. Das Log zeigt alle geprüften Ladewege und die gefundene
|
||||
Assemblyversion.
|
||||
|
||||
### Zugriff auf den BizTalk-Katalog verweigert
|
||||
|
||||
Eine administrativ gestartete `cmd.exe` allein garantiert keine Berechtigung auf den
|
||||
BizTalk-Katalog. Das ausführende Konto benötigt mindestens die passenden
|
||||
BizTalk-Leserechte. Nach einer Gruppenänderung ist eine neue Anmeldung erforderlich.
|
||||
|
||||
## ACC und PROD zusammenführen
|
||||
|
||||
Beide JSON-Dateien in einen gemeinsamen, geschützten Ordner kopieren und ausführen:
|
||||
@@ -164,4 +221,7 @@ Dekodieren:
|
||||
certutil -decode BizTalkApplicationCatalog-source.zip.txt BizTalkApplicationCatalog-source.zip
|
||||
```
|
||||
|
||||
Die `.txt`-Datei ist Base64 und enthält das vollständige Quellpaket einschließlich
|
||||
aktualisierter `Readme.md` und `Dokumentation.md`.
|
||||
|
||||
Technische Details stehen in [Dokumentation.md](Dokumentation.md).
|
||||
|
||||
@@ -1,24 +1,33 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Management;
|
||||
using System.Reflection;
|
||||
using System.Security.Principal;
|
||||
using BizTalkApplicationCatalog.Infrastructure;
|
||||
using BizTalkApplicationCatalog.Models;
|
||||
|
||||
namespace BizTalkApplicationCatalog.Collectors
|
||||
{
|
||||
/// <summary>
|
||||
/// Liest nur die Daten, die für die Phase-1-Ansicht erforderlich sind.
|
||||
/// Ermittelt die lokale BizTalk-Gruppe lesend über WMI und liest den
|
||||
/// Anwendungskatalog anschließend über das BizTalk Explorer Object Model.
|
||||
/// </summary>
|
||||
internal sealed class BizTalkWmiCollector
|
||||
{
|
||||
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 ConsoleFileLogger logger;
|
||||
private readonly ManagementScope scope;
|
||||
private readonly TimeSpan timeout;
|
||||
private readonly Dictionary<string, string> receivePortApplications =
|
||||
new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
public BizTalkWmiCollector(
|
||||
InventoryDocument document,
|
||||
@@ -28,18 +37,18 @@ namespace BizTalkApplicationCatalog.Collectors
|
||||
this.document = document;
|
||||
this.logger = logger;
|
||||
timeout = TimeSpan.FromSeconds(Math.Max(5, timeoutSeconds));
|
||||
scope = new ManagementScope(
|
||||
@"\\" + Environment.MachineName + @"\root\MicrosoftBizTalkServer");
|
||||
scope = new ManagementScope(WmiNamespace);
|
||||
scope.Options.Timeout = timeout;
|
||||
scope.Options.EnablePrivileges = false;
|
||||
scope.Options.Impersonation = ImpersonationLevel.Impersonate;
|
||||
}
|
||||
|
||||
public void Collect()
|
||||
{
|
||||
scope.Connect();
|
||||
CollectApplications();
|
||||
CollectReceivePortMappings();
|
||||
CollectSendPortEndpoints();
|
||||
CollectReceiveLocationEndpoints();
|
||||
LogRuntimeInformation();
|
||||
ConnectWmi();
|
||||
var group = ReadBizTalkGroup();
|
||||
CollectWithExplorerObjectModel(group);
|
||||
|
||||
document.Applications.Sort((left, right) =>
|
||||
string.Compare(left.Name, right.Name, StringComparison.OrdinalIgnoreCase));
|
||||
@@ -58,108 +67,345 @@ namespace BizTalkApplicationCatalog.Collectors
|
||||
});
|
||||
}
|
||||
|
||||
private void CollectApplications()
|
||||
private void LogRuntimeInformation()
|
||||
{
|
||||
foreach (var row in Query("SELECT * FROM MSBTS_Application"))
|
||||
logger.Info("Diagnose: Computer=" + Environment.MachineName
|
||||
+ "; 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
|
||||
{
|
||||
using (row)
|
||||
using (var identity = WindowsIdentity.GetCurrent())
|
||||
{
|
||||
var name = First(row, "Name", "ApplicationName");
|
||||
if (!string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
document.Applications.Add(new ApplicationRecord { Name = name.Trim() });
|
||||
var principal = new WindowsPrincipal(identity);
|
||||
logger.Info("Sicherheitskontext: Benutzer=" + identity.Name
|
||||
+ "; administrativ="
|
||||
+ (principal.IsInRole(WindowsBuiltInRole.Administrator)
|
||||
? "Ja"
|
||||
: "Nein")
|
||||
+ ".");
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
// Ein Diagnosefehler darf die eigentliche Inventarisierung nicht verhindern.
|
||||
logger.Warning("Sicherheitskontext konnte nicht ermittelt werden: "
|
||||
+ exception.Message);
|
||||
}
|
||||
}
|
||||
|
||||
if (document.Applications.Count == 0)
|
||||
private void ConnectWmi()
|
||||
{
|
||||
var timer = Stopwatch.StartNew();
|
||||
logger.Info("Verbinde lokal mit dem BizTalk-WMI-Namespace.");
|
||||
scope.Connect();
|
||||
timer.Stop();
|
||||
logger.Info("BizTalk-WMI-Verbindung hergestellt ("
|
||||
+ timer.ElapsedMilliseconds + " ms).");
|
||||
}
|
||||
|
||||
private BizTalkGroupLocation ReadBizTalkGroup()
|
||||
{
|
||||
const string query =
|
||||
"SELECT Name, MgmtDbServerName, MgmtDbName FROM MSBTS_GroupSetting";
|
||||
var rows = Query("BizTalk-Gruppenkonfiguration", query);
|
||||
if (rows.Count != 1)
|
||||
{
|
||||
DisposeRows(rows);
|
||||
throw new InvalidOperationException(
|
||||
"MSBTS_GroupSetting lieferte "
|
||||
+ rows.Count
|
||||
+ " Datensätze; exakt ein Datensatz wurde erwartet.");
|
||||
}
|
||||
|
||||
using (var row = rows[0])
|
||||
{
|
||||
var groupName = RequiredWmiText(row, "Name");
|
||||
var serverName = RequiredWmiText(row, "MgmtDbServerName");
|
||||
var databaseName = RequiredWmiText(row, "MgmtDbName");
|
||||
logger.Info("BizTalk-Gruppe gefunden: " + groupName + ".");
|
||||
logger.Info("Management-Datenbankziel: Server="
|
||||
+ serverName + "; Datenbank=" + databaseName + ".");
|
||||
return new BizTalkGroupLocation(
|
||||
groupName,
|
||||
serverName,
|
||||
databaseName);
|
||||
}
|
||||
}
|
||||
|
||||
private void CollectWithExplorerObjectModel(BizTalkGroupLocation group)
|
||||
{
|
||||
var explorerType = ResolveExplorerType();
|
||||
object explorer = null;
|
||||
try
|
||||
{
|
||||
explorer = Activator.CreateInstance(explorerType);
|
||||
logger.Info("BizTalk Explorer Object Model wird ausschließlich lesend geöffnet.");
|
||||
|
||||
// Die Verbindungszeichenfolge enthält nur Zielserver, Datenbankname
|
||||
// und integrierte Windows-Authentifizierung. Sie wird nicht protokolliert.
|
||||
SetRequiredProperty(
|
||||
explorer,
|
||||
"ConnectionString",
|
||||
"Data Source=" + group.DatabaseServer
|
||||
+ ";Initial Catalog=" + group.DatabaseName
|
||||
+ ";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).");
|
||||
|
||||
if (applications.Count == 0)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"MSBTS_Application lieferte keine Anwendungen.");
|
||||
"Das BizTalk Explorer Object Model lieferte keine Anwendungen.");
|
||||
}
|
||||
|
||||
logger.Info(document.Applications.Count
|
||||
+ " BizTalk-Anwendung(en) gefunden.");
|
||||
CollectApplicationsAndEndpoints(applications);
|
||||
}
|
||||
finally
|
||||
{
|
||||
var disposable = explorer as IDisposable;
|
||||
if (disposable != null)
|
||||
{
|
||||
disposable.Dispose();
|
||||
logger.Detail("BizTalk Explorer Object Model wurde geschlossen.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CollectReceivePortMappings()
|
||||
private void CollectApplicationsAndEndpoints(IList<object> applications)
|
||||
{
|
||||
var count = 0;
|
||||
foreach (var row in Query("SELECT * FROM MSBTS_ReceivePort"))
|
||||
{
|
||||
using (row)
|
||||
{
|
||||
var name = First(row, "Name", "ReceivePortName");
|
||||
var application = First(row, "ApplicationName", "Application");
|
||||
if (string.IsNullOrWhiteSpace(name)) continue;
|
||||
receivePortApplications[name] = application;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
logger.Info(count + " Receive-Port-Zuordnung(en) gelesen.");
|
||||
}
|
||||
var sendPortCount = 0;
|
||||
var secondaryTransportCount = 0;
|
||||
var receivePortCount = 0;
|
||||
var receiveLocationCount = 0;
|
||||
|
||||
private void CollectSendPortEndpoints()
|
||||
foreach (var application in applications)
|
||||
{
|
||||
var count = 0;
|
||||
foreach (var row in Query("SELECT * FROM MSBTS_SendPort"))
|
||||
var applicationName = RequiredText(application, "Name");
|
||||
document.Applications.Add(new ApplicationRecord
|
||||
{
|
||||
using (row)
|
||||
{
|
||||
var application = First(row, "ApplicationName", "Application");
|
||||
AddEndpoint(application, First(
|
||||
row,
|
||||
"PTTransportType",
|
||||
"PrimaryTransportType",
|
||||
"AdapterName",
|
||||
"TransportType"));
|
||||
count++;
|
||||
Name = applicationName
|
||||
});
|
||||
logger.Detail("Lese Anwendung: " + applicationName + ".");
|
||||
|
||||
var secondaryAdapter = First(
|
||||
row,
|
||||
"STTransportType",
|
||||
"SecondaryTransportType");
|
||||
var sendPorts = EnumerateRequiredProperty(
|
||||
application,
|
||||
"SendPorts",
|
||||
"Send Ports der Anwendung " + applicationName);
|
||||
foreach (var sendPort in sendPorts)
|
||||
{
|
||||
sendPortCount++;
|
||||
var sendPortName = RequiredText(sendPort, "Name");
|
||||
var primaryTransport = OptionalProperty(sendPort, "PrimaryTransport");
|
||||
var primaryAdapter = AdapterName(primaryTransport);
|
||||
AddEndpoint(applicationName, primaryAdapter);
|
||||
logger.Detail("Send Port: Anwendung=" + applicationName
|
||||
+ "; Name=" + sendPortName
|
||||
+ "; Primäradapter=" + DisplayAdapter(primaryAdapter) + ".");
|
||||
|
||||
var secondaryTransport = OptionalProperty(sendPort, "SecondaryTransport");
|
||||
if (secondaryTransport != null)
|
||||
{
|
||||
var secondaryAdapter = AdapterName(secondaryTransport);
|
||||
// ExplorerOM kann ein leeres TransportInfo-Objekt liefern,
|
||||
// obwohl kein Backup-Transport konfiguriert ist.
|
||||
if (!string.IsNullOrWhiteSpace(secondaryAdapter))
|
||||
{
|
||||
AddEndpoint(application, secondaryAdapter);
|
||||
count++;
|
||||
AddEndpoint(applicationName, secondaryAdapter);
|
||||
secondaryTransportCount++;
|
||||
logger.Detail("Send Port: Anwendung=" + applicationName
|
||||
+ "; Name=" + sendPortName
|
||||
+ "; Sekundäradapter=" + secondaryAdapter + ".");
|
||||
}
|
||||
}
|
||||
}
|
||||
logger.Info(count + " Send-Endpunkt(e) gefunden.");
|
||||
}
|
||||
|
||||
private void CollectReceiveLocationEndpoints()
|
||||
{
|
||||
var count = 0;
|
||||
foreach (var row in Query("SELECT * FROM MSBTS_ReceiveLocation"))
|
||||
{
|
||||
using (row)
|
||||
{
|
||||
var application = First(row, "ApplicationName", "Application");
|
||||
if (string.IsNullOrWhiteSpace(application))
|
||||
{
|
||||
var receivePort = First(row, "ReceivePortName");
|
||||
receivePortApplications.TryGetValue(receivePort, out application);
|
||||
}
|
||||
|
||||
AddEndpoint(
|
||||
var receivePorts = EnumerateRequiredProperty(
|
||||
application,
|
||||
First(row, "AdapterName", "TransportType", "PTTransportType"));
|
||||
count++;
|
||||
"ReceivePorts",
|
||||
"Receive Ports der Anwendung " + applicationName);
|
||||
foreach (var receivePort in receivePorts)
|
||||
{
|
||||
receivePortCount++;
|
||||
var receivePortName = RequiredText(receivePort, "Name");
|
||||
var receiveLocations = EnumerateRequiredProperty(
|
||||
receivePort,
|
||||
"ReceiveLocations",
|
||||
"Receive Locations des Receive Ports " + receivePortName);
|
||||
foreach (var receiveLocation in receiveLocations)
|
||||
{
|
||||
receiveLocationCount++;
|
||||
var receiveLocationName = RequiredText(receiveLocation, "Name");
|
||||
var adapter = AdapterName(
|
||||
OptionalProperty(receiveLocation, "TransportType"));
|
||||
AddEndpoint(applicationName, adapter);
|
||||
logger.Detail("Receive Location: Anwendung=" + applicationName
|
||||
+ "; Receive Port=" + receivePortName
|
||||
+ "; Name=" + receiveLocationName
|
||||
+ "; Adapter=" + DisplayAdapter(adapter) + ".");
|
||||
}
|
||||
}
|
||||
logger.Info(count + " Receive-Endpunkt(e) gefunden.");
|
||||
|
||||
logger.Info("Anwendung abgeschlossen: " + applicationName
|
||||
+ "; Send Ports=" + sendPorts.Count
|
||||
+ "; Receive Ports=" + receivePorts.Count + ".");
|
||||
}
|
||||
|
||||
logger.Info("Katalogerfassung abgeschlossen: Anwendungen="
|
||||
+ document.Applications.Count
|
||||
+ "; Send Ports=" + sendPortCount
|
||||
+ "; sekundäre Send-Transporte=" + secondaryTransportCount
|
||||
+ "; Receive Ports=" + receivePortCount
|
||||
+ "; Receive Locations=" + receiveLocationCount
|
||||
+ "; Endpunkte=" + document.Endpoints.Count + ".");
|
||||
}
|
||||
|
||||
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
|
||||
// LoadWithPartialName ist hier bewusst nur ein Kompatibilitäts-Fallback,
|
||||
// damit unterschiedliche BizTalk-GAC-Versionen gefunden werden.
|
||||
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 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)
|
||||
{
|
||||
var path = Path.Combine(
|
||||
root,
|
||||
folder,
|
||||
ExplorerAssemblyName + ".dll");
|
||||
logger.Detail("Prüfe Assembly-Pfad: " + path);
|
||||
if (!File.Exists(path)) continue;
|
||||
try
|
||||
{
|
||||
return Assembly.LoadFrom(path);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
logger.Detail("Assembly.LoadFrom fehlgeschlagen: "
|
||||
+ exception.GetType().Name + ": " + exception.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void LogExplorerAssembly(Assembly assembly)
|
||||
{
|
||||
logger.Info("ExplorerOM-Assembly geladen: " + assembly.FullName + ".");
|
||||
try
|
||||
{
|
||||
logger.Detail("ExplorerOM-Assemblypfad: " + assembly.Location);
|
||||
}
|
||||
catch (NotSupportedException)
|
||||
{
|
||||
logger.Detail("ExplorerOM-Assemblypfad ist für diese Assembly nicht verfügbar.");
|
||||
}
|
||||
}
|
||||
|
||||
private List<ManagementObject> Query(string name, string query)
|
||||
{
|
||||
var options = new System.Management.EnumerationOptions
|
||||
{
|
||||
ReturnImmediately = false,
|
||||
Rewindable = false,
|
||||
Timeout = timeout
|
||||
};
|
||||
var timer = Stopwatch.StartNew();
|
||||
logger.Info("WMI-Abfrage startet: " + name + ".");
|
||||
logger.Detail("WQL: " + query);
|
||||
using (var searcher = new ManagementObjectSearcher(
|
||||
scope,
|
||||
new ObjectQuery(query),
|
||||
options))
|
||||
{
|
||||
var rows = searcher.Get().Cast<ManagementObject>().ToList();
|
||||
timer.Stop();
|
||||
logger.Info("WMI-Abfrage abgeschlossen: " + name
|
||||
+ "; Datensätze=" + rows.Count
|
||||
+ "; Dauer=" + timer.ElapsedMilliseconds + " ms.");
|
||||
return rows;
|
||||
}
|
||||
}
|
||||
|
||||
private void AddEndpoint(string applicationName, string adapterType)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(applicationName))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Ein Endpunkt konnte keiner BizTalk-Anwendung zugeordnet werden.");
|
||||
}
|
||||
|
||||
document.Endpoints.Add(new EndpointRecord
|
||||
{
|
||||
ApplicationName = applicationName.Trim(),
|
||||
@@ -169,40 +415,135 @@ namespace BizTalkApplicationCatalog.Collectors
|
||||
});
|
||||
}
|
||||
|
||||
private List<ManagementObject> Query(string query)
|
||||
private static string AdapterName(object transportTypeOrInfo)
|
||||
{
|
||||
var options = new EnumerationOptions
|
||||
{
|
||||
ReturnImmediately = false,
|
||||
Rewindable = false,
|
||||
Timeout = timeout
|
||||
};
|
||||
using (var searcher = new ManagementObjectSearcher(
|
||||
scope,
|
||||
new ObjectQuery(query),
|
||||
options))
|
||||
{
|
||||
return searcher.Get().Cast<ManagementObject>().ToList();
|
||||
}
|
||||
if (transportTypeOrInfo == null) return string.Empty;
|
||||
|
||||
// Send-Transporte liefern zuerst TransportInfo und darin TransportType.
|
||||
// Receive Locations liefern direkt den ProtocolType.
|
||||
var protocolType = OptionalProperty(
|
||||
transportTypeOrInfo,
|
||||
"TransportType") ?? transportTypeOrInfo;
|
||||
return Convert.ToString(
|
||||
OptionalProperty(protocolType, "Name"),
|
||||
CultureInfo.InvariantCulture) ?? string.Empty;
|
||||
}
|
||||
|
||||
private static string First(ManagementBaseObject row, params string[] names)
|
||||
private static string DisplayAdapter(string adapter)
|
||||
{
|
||||
foreach (var name in names)
|
||||
return string.IsNullOrWhiteSpace(adapter) ? "Unbekannt" : adapter;
|
||||
}
|
||||
|
||||
private static IList<object> EnumerateRequiredProperty(
|
||||
object instance,
|
||||
string propertyName,
|
||||
string description)
|
||||
{
|
||||
try
|
||||
var value = RequiredProperty(instance, propertyName);
|
||||
var enumerable = value as IEnumerable;
|
||||
if (enumerable == null)
|
||||
{
|
||||
var value = row[name];
|
||||
if (value == null) continue;
|
||||
var text = Convert.ToString(value, CultureInfo.InvariantCulture);
|
||||
if (!string.IsNullOrWhiteSpace(text)) return text;
|
||||
throw new InvalidOperationException(
|
||||
description + " ist keine aufzählbare Sammlung.");
|
||||
}
|
||||
catch (ManagementException)
|
||||
|
||||
var result = new List<object>();
|
||||
foreach (var item in enumerable)
|
||||
{
|
||||
// WMI-Properties unterscheiden sich zwischen Providerständen.
|
||||
if (item != null) result.Add(item);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return string.Empty;
|
||||
|
||||
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 static string RequiredWmiText(
|
||||
ManagementBaseObject instance,
|
||||
string propertyName)
|
||||
{
|
||||
var value = Convert.ToString(
|
||||
instance[propertyName],
|
||||
CultureInfo.InvariantCulture);
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"WMI-Eigenschaft " + propertyName + " ist leer.");
|
||||
}
|
||||
return value.Trim();
|
||||
}
|
||||
|
||||
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 object OptionalProperty(object instance, string propertyName)
|
||||
{
|
||||
if (instance == null) return null;
|
||||
var property = instance.GetType().GetProperty(
|
||||
propertyName,
|
||||
BindingFlags.Instance | BindingFlags.Public);
|
||||
return property == null ? null : 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 static void DisposeRows(IEnumerable<ManagementObject> rows)
|
||||
{
|
||||
foreach (var row in rows) row.Dispose();
|
||||
}
|
||||
|
||||
private sealed class BizTalkGroupLocation
|
||||
{
|
||||
public BizTalkGroupLocation(
|
||||
string groupName,
|
||||
string databaseServer,
|
||||
string databaseName)
|
||||
{
|
||||
GroupName = groupName;
|
||||
DatabaseServer = databaseServer;
|
||||
DatabaseName = databaseName;
|
||||
}
|
||||
|
||||
public string GroupName { get; private set; }
|
||||
public string DatabaseServer { get; private set; }
|
||||
public string DatabaseName { get; private set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Management;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace BizTalkApplicationCatalog.Infrastructure
|
||||
@@ -19,8 +21,57 @@ namespace BizTalkApplicationCatalog.Infrastructure
|
||||
}
|
||||
|
||||
public void Info(string message) { Write("INFO", message); }
|
||||
public void Detail(string message) { Write("DETAIL", message); }
|
||||
public void Warning(string message) { Write("WARNUNG", message); }
|
||||
public void Error(string message) { Write("FEHLER", message); }
|
||||
|
||||
/// <summary>
|
||||
/// Schreibt die vollständige Ausnahmekette einschließlich HRESULT,
|
||||
/// WMI-Status und Stacktrace auf Konsole und in die Logdatei.
|
||||
/// </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));
|
||||
|
||||
var managementException = current as ManagementException;
|
||||
if (managementException != null)
|
||||
{
|
||||
Detail("WMI-Status: " + managementException.ErrorCode);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(current.StackTrace))
|
||||
{
|
||||
Detail("Stacktrace[" + depth + "]:" + Environment.NewLine
|
||||
+ current.StackTrace);
|
||||
}
|
||||
|
||||
// Reflection kapselt Providerfehler häufig in TargetInvocationException.
|
||||
// Die innere Ausnahme wird deshalb ausdrücklich mit ausgegeben.
|
||||
var targetInvocation = current as TargetInvocationException;
|
||||
current = targetInvocation != null && targetInvocation.InnerException != null
|
||||
? targetInvocation.InnerException
|
||||
: current.InnerException;
|
||||
depth++;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose() { writer.Dispose(); }
|
||||
|
||||
private void Write(string level, string message)
|
||||
|
||||
@@ -52,9 +52,11 @@ namespace BizTalkApplicationCatalog.Infrastructure
|
||||
Severity = required ? "Fehler" : "Warnung",
|
||||
Area = name,
|
||||
Message = "Datenerfassung fehlgeschlagen: " + exception.Message,
|
||||
RecommendedAction = "Logdatei, WMI-Provider und Leseberechtigungen prüfen."
|
||||
RecommendedAction =
|
||||
"Logdatei, WMI-Provider, ExplorerOM-Installation "
|
||||
+ "und BizTalk-Leseberechtigungen prüfen."
|
||||
});
|
||||
logger.Error(name + ": " + exception.Message);
|
||||
logger.Exception(name, exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,16 @@ namespace BizTalkApplicationCatalog
|
||||
logger.Info("BEW BizTalk Application Catalog – Phase 1 startet.");
|
||||
logger.Info("Umgebung: " + options.EnvironmentName);
|
||||
logger.Info("Erfasst werden nur Anwendungen, Adaptertypen und Endpunkte.");
|
||||
logger.Info("Modus: ausschließlich lesender lokaler BizTalk-WMI-Zugriff.");
|
||||
logger.Info(
|
||||
"Modus: ausschließlich lesender Zugriff; lokales BizTalk-WMI "
|
||||
+ "für die Gruppenermittlung und ExplorerOM für den Katalog.");
|
||||
logger.Info("Ausgabeordner: " + options.OutputDirectory);
|
||||
logger.Detail("Logdatei: " + logPath);
|
||||
logger.Detail("JSON-Zieldatei: " + snapshotPath);
|
||||
logger.Detail("XLSX-Zieldatei: " + reportPath);
|
||||
var wmiTimeoutSeconds = ReadInt("WmiTimeoutSeconds", 30);
|
||||
logger.Detail("Konfiguration WmiTimeoutSeconds="
|
||||
+ wmiTimeoutSeconds + ".");
|
||||
|
||||
new SafeCollector(document, logger).Execute(
|
||||
"Phase-1-Anwendungen und Endpunkte",
|
||||
@@ -81,7 +90,7 @@ namespace BizTalkApplicationCatalog
|
||||
() => new BizTalkWmiCollector(
|
||||
document,
|
||||
logger,
|
||||
ReadInt("WmiTimeoutSeconds", 30)).Collect());
|
||||
wmiTimeoutSeconds).Collect());
|
||||
|
||||
document.CompletedUtc = DateTime.UtcNow;
|
||||
var snapshot = SnapshotStore.FromInventory(
|
||||
|
||||
@@ -76,7 +76,8 @@ namespace BizTalkApplicationCatalog.Reporting
|
||||
{
|
||||
snapshots.Count > 1
|
||||
? "Phase 1 – BizTalk Anwendungen & Adapter (Quelle: ACC/PROD JSON-Snapshots)"
|
||||
: "Phase 1 – BizTalk Anwendungen & Adapter (Quelle: lokaler BizTalk-WMI-Provider)",
|
||||
: "Phase 1 – BizTalk Anwendungen & Adapter "
|
||||
+ "(Quelle: lokale BizTalk-Verwaltungsschnittstellen)",
|
||||
"", "", "", "", "", ""
|
||||
},
|
||||
new object[]
|
||||
|
||||
Reference in New Issue
Block a user