Updated sources of Status Tool, so that it works. Tested with first customer.

This commit is contained in:
2026-02-04 08:38:00 +01:00
parent cff972a0bf
commit 016ce19229

View File

@@ -1,278 +1,676 @@
<#!
BizTalk Status Tool (PowerShell only, Remote Server Support)
Compatible with:
- Windows Server 2019
- PowerShell 5.1
- BizTalk Server 2020 (WMI under root\MicrosoftBizTalkServer)
Author: Copilot
Version: 1.3.0
<#
BizTalk Status Tool Extended Version
Includes:
- Applications Snapshot (RL/SP/ORCH)
- Host Instances Snapshot
- JSON / CSV / HTML / Console
- Compare (Before / After)
- HostInstance Diff (New / Removed / Changed)
#>
$Global:LogFile = Join-Path (Get-Location) "BizTalkStatusTool.log"
function Write-Log {
param(
[string]$Message,
[ValidateSet('INFO','WARN','ERROR','DEBUG')]
[string]$Level = 'INFO'
)
$timestamp = (Get-Date).ToString('yyyy-MM-dd HH:mm:ss')
$line = "[$timestamp][$Level] $Message"
Write-Host $line
Add-Content -Path $Global:LogFile -Value $line
}
# ----------------- Helpers: Status mapping & coloring -----------------
function Format-Status {
param(
[Parameter(Mandatory=$true)][string]$ArtifactType,
[Parameter(Mandatory=$false)]$Value
)
$result = [ordered]@{ Text=''; Class='neutral'; ConsoleColor='Yellow' }
switch ($ArtifactType) {
'ReceiveLocation' {
# Value is boolean (Enabled)
$enabled = [bool]$Value
if ($enabled) { $result.Text='Enabled'; $result.Class='ok'; $result.ConsoleColor='Green' }
else { $result.Text='Disabled'; $result.Class='bad'; $result.ConsoleColor='Red' }
}
'SendPort' {
# MSBTS_SendPort.Status: 1=Bound, 2=Stopped, 3=Started
$code = [int]$Value
switch ($code) {
3 { $result.Text='Started'; $result.Class='ok'; $result.ConsoleColor='Green' }
2 { $result.Text='Stopped'; $result.Class='bad'; $result.ConsoleColor='Red' }
1 { $result.Text='Bound'; $result.Class='bad'; $result.ConsoleColor='Red' }
default { $result.Text="Unknown($code)"; $result.Class='bad'; $result.ConsoleColor='Red' }
}
}
'Orchestration' {
# MSBTS_Orchestration.OrchestrationStatus: 1=Unbound, 2=Bound, 3=Stopped, 4=Started
$code = [int]$Value
switch ($code) {
4 { $result.Text='Started'; $result.Class='ok'; $result.ConsoleColor='Green' }
3 { $result.Text='Stopped'; $result.Class='bad'; $result.ConsoleColor='Red' }
2 { $result.Text='Bound'; $result.Class='bad'; $result.ConsoleColor='Red' }
1 { $result.Text='Unbound'; $result.Class='bad'; $result.ConsoleColor='Red' }
default { $result.Text="Unknown($code)"; $result.Class='bad'; $result.ConsoleColor='Red' }
}
}
default {
$result.Text = [string]$Value
}
}
return [pscustomobject]$result
}
function Write-ColoredKV {
param(
[string]$Key,
[psobject]$Formatted
)
Write-Host ("{0,-15}" -f ($Key + ':')) -NoNewline
Write-Host $Formatted.Text -ForegroundColor $Formatted.ConsoleColor
}
# ----------------- Snapshot -----------------
function Get-BizTalkSnapshot {
param(
[string]$OutputFile = 'snapshot.json',
[string]$Server = $env:COMPUTERNAME
)
Write-Log "Erzeuge Snapshot von Server '$Server' → Datei: $OutputFile"
try {
$apps = Get-WmiObject MSBTS_Application -Namespace "root\MicrosoftBizTalkServer" -ComputerName $Server -ErrorAction Stop
} catch {
Write-Log "FEHLER: Verbindung zu BizTalk-WMI auf '$Server' fehlgeschlagen: $($_.Exception.Message)" 'ERROR'
throw
}
$snapshot = @()
foreach ($app in $apps) {
$appName = $app.Name
Write-Log "Lese Anwendung '$appName'"
$rl = Get-WmiObject MSBTS_ReceiveLocation -Namespace root\MicrosoftBizTalkServer -ComputerName $Server |
Where-Object { $_.ApplicationName -eq $appName } |
Select-Object Name,
@{n='Enabled'; e={ -not $_.IsDisabled }},
AdapterName,
@{n='Address'; e={$_.InboundTransportURL}}
$sp = Get-WmiObject MSBTS_SendPort -Namespace root\MicrosoftBizTalkServer -ComputerName $Server |
Where-Object { $_.ApplicationName -eq $appName } |
Select-Object Name,
@{n='Status';e={$_.Status}},
@{n='PrimaryTransportType';e={$_.PTTransportType}},
@{n='PrimaryTransportAddress';e={$_.PTAddress}}
$orch = Get-WmiObject MSBTS_Orchestration -Namespace root\MicrosoftBizTalkServer -ComputerName $Server |
Where-Object { $_.ApplicationName -eq $appName } |
Select-Object Name, @{n='OrchestrationStatus';e={$_.OrchestrationStatus}}
$snapshot += [PSCustomObject]@{
Application = $appName
ReceiveLocations = $rl
SendPorts = $sp
Orchestrations = $orch
}
}
$snapshot | ConvertTo-Json -Depth 8 | Out-File $OutputFile -Encoding UTF8
Write-Log "Snapshot gespeichert: $OutputFile"
}
# ----------------- Diff + HTML + Console Output -----------------
function Compare-BizTalkSnapshots {
param(
[Parameter(Mandatory=$true)][string]$Before,
[Parameter(Mandatory=$true)][string]$After,
[string]$HtmlReport = 'BizTalkDiff.html'
)
Write-Log "Vergleiche Snapshots: $Before$After"
$snapBefore = Get-Content $Before | ConvertFrom-Json
$snapAfter = Get-Content $After | ConvertFrom-Json
$differences = @()
# Helper: add diff row
function Add-Diff {
param($appName,$type,$name,$beforeVal,$afterVal)
$b = Format-Status -ArtifactType $type -Value $beforeVal
$a = Format-Status -ArtifactType $type -Value $afterVal
$differences += [PSCustomObject]@{
Application = $appName
ArtifactType = $type
Name = $name
Before = $beforeVal
After = $afterVal
BeforeText = $b.Text
BeforeClass = $b.Class
AfterText = $a.Text
AfterClass = $a.Class
}
}
foreach ($appAfter in $snapAfter) {
$appBefore = $snapBefore | Where-Object { $_.Application -eq $appAfter.Application }
# Receive Locations
foreach ($rlAfter in $appAfter.ReceiveLocations) {
$rlBefore = $appBefore.ReceiveLocations | Where-Object { $_.Name -eq $rlAfter.Name }
if ($null -eq $rlBefore) { Add-Diff $appAfter.Application 'ReceiveLocation' $rlAfter.Name $null $rlAfter.Enabled; continue }
if ($rlBefore.Enabled -ne $rlAfter.Enabled) { Add-Diff $appAfter.Application 'ReceiveLocation' $rlAfter.Name $rlBefore.Enabled $rlAfter.Enabled }
}
# Detect removed RL
foreach ($rlBefore in $appBefore.ReceiveLocations) {
$rlAfter = $appAfter.ReceiveLocations | Where-Object { $_.Name -eq $rlBefore.Name }
if ($null -eq $rlAfter) { Add-Diff $appAfter.Application 'ReceiveLocation' $rlBefore.Name $rlBefore.Enabled $null }
}
# Send Ports
foreach ($spAfter in $appAfter.SendPorts) {
$spBefore = $appBefore.SendPorts | Where-Object { $_.Name -eq $spAfter.Name }
if ($null -eq $spBefore) { Add-Diff $appAfter.Application 'SendPort' $spAfter.Name $null $spAfter.Status; continue }
if ($spBefore.Status -ne $spAfter.Status) { Add-Diff $appAfter.Application 'SendPort' $spAfter.Name $spBefore.Status $spAfter.Status }
}
foreach ($spBefore in $appBefore.SendPorts) {
$spAfter = $appAfter.SendPorts | Where-Object { $_.Name -eq $spBefore.Name }
if ($null -eq $spAfter) { Add-Diff $appAfter.Application 'SendPort' $spBefore.Name $spBefore.Status $null }
}
# Orchestrations
foreach ($oAfter in $appAfter.Orchestrations) {
$oBefore = $appBefore.Orchestrations | Where-Object { $_.Name -eq $oAfter.Name }
if ($null -eq $oBefore) { Add-Diff $appAfter.Application 'Orchestration' $oAfter.Name $null $oAfter.OrchestrationStatus; continue }
if ($oBefore.OrchestrationStatus -ne $oAfter.OrchestrationStatus) { Add-Diff $appAfter.Application 'Orchestration' $oAfter.Name $oBefore.OrchestrationStatus $oAfter.OrchestrationStatus }
}
foreach ($oBefore in $appBefore.Orchestrations) {
$oAfter = $appAfter.Orchestrations | Where-Object { $_.Name -eq $oBefore.Name }
if ($null -eq $oAfter) { Add-Diff $appAfter.Application 'Orchestration' $oBefore.Name $oBefore.OrchestrationStatus $null }
}
}
# ---------------- Console text summary with colors ----------------
Write-Host ""; Write-Host "------------------------------------------------------------" -ForegroundColor Yellow
Write-Host " TEXTUELLE ÄNDERUNGSÜBERSICHT (StdOut)" -ForegroundColor Yellow
Write-Host "------------------------------------------------------------" -ForegroundColor Yellow
if ($differences.Count -eq 0) {
Write-Host "Keine Unterschiede gefunden." -ForegroundColor Green
} else {
foreach ($d in $differences) {
Write-Host ""; Write-Host ("Application: {0}" -f $d.Application) -ForegroundColor Cyan
Write-Host ("Artifact: {0}" -f $d.ArtifactType)
Write-Host ("Name: {0}" -f $d.Name)
Write-ColoredKV -Key 'Before' -Formatted ([pscustomobject]@{ Text=$d.BeforeText; ConsoleColor=($(if($d.BeforeClass -eq 'ok'){'Green'}elseif($d.BeforeClass -eq 'bad'){'Red'}else{'Yellow'})) })
Write-ColoredKV -Key 'After' -Formatted ([pscustomobject]@{ Text=$d.AfterText; ConsoleColor=($(if($d.AfterClass -eq 'ok'){'Green'}elseif($d.AfterClass -eq 'bad'){'Red'}else{'Yellow'})) })
}
}
# ---------------- HTML report ----------------
Write-Log "Erzeuge HTML-Report: $HtmlReport"
$htmlHeader = @"
<html>
<head>
<title>BizTalk Differences Report</title>
<style>
body { font-family: Arial, Helvetica, sans-serif; }
table { border-collapse: collapse; width: 100%; }
th, td { border: 1px solid #999; padding: 8px; text-align: left; }
th { background-color: #2d6cdf; color: white; }
tr:nth-child(even) { background-color: #f8f8f8; }
.ok { color: #0a7d00; font-weight: 600; }
.bad { color: #b00020; font-weight: 600; }
.neutral { color: #b58900; font-weight: 600; }
.small { color:#666; font-size:12px; }
</style>
</head>
<body>
<h2>BizTalk Differences Report</h2>
<p class="small">Vergleich: <b>$Before</b> <b>$After</b></p>
<table>
<tr>
<th>Application</th>
<th>Artifact</th>
<th>Name</th>
<th>Before</th>
<th>After</th>
</tr>
"@
$rows = foreach ($d in $differences) {
"<tr><td>$($d.Application)</td><td>$($d.ArtifactType)</td><td>$($d.Name)</td><td class='${($d.BeforeClass)}'>$($d.BeforeText)</td><td class='${($d.AfterClass)}'>$($d.AfterText)</td></tr>"
}
$htmlFooter = "</table></body></html>"
($htmlHeader + ($rows -join "`n") + $htmlFooter) | Out-File $HtmlReport -Encoding UTF8
Write-Host ""; Write-Host ("HTML-Report erstellt: {0}" -f $HtmlReport) -ForegroundColor Green
Write-Host "→ Im selben Verzeichnis öffnen für die farbige Übersicht." -ForegroundColor DarkYellow
return $differences
}
# ---------------- Parameters (CLI) ----------------
# ======================================================
# PARAMETERS
# ======================================================
param(
[switch]$Before,
[switch]$After,
[switch]$Compare,
[string]$Server = $env:COMPUTERNAME,
[string]$OutDir = '.'
[switch]$Diag,
[string]$Server,
[string]$MgmtDbServer,
[string]$MgmtDbName,
[string]$OutDir
)
if ($PSBoundParameters.ContainsKey('OutDir')) { New-Item -ItemType Directory -Force -Path $OutDir | Out-Null; Set-Location $OutDir }
if (-not $Server) { $Server = $env:COMPUTERNAME }
if (-not $OutDir) { $OutDir = "." }
if ($Before) { Get-BizTalkSnapshot -OutputFile 'before.json' -Server $Server; exit }
elif ($After) { Get-BizTalkSnapshot -OutputFile 'after.json' -Server $Server; exit }
elif ($Compare) { Compare-BizTalkSnapshots -Before 'before.json' -After 'after.json' -HtmlReport 'BizTalkDiff.html'; exit }
else {
Write-Host "Verwendung:" -ForegroundColor Yellow
Write-Host " Snapshot vor Downtime: .\BizTalkStatusTool.ps1 -Before -Server <BizTalkServer> -OutDir <Pfad>"
Write-Host " Snapshot nach Downtime: .\BizTalkStatusTool.ps1 -After -Server <BizTalkServer> -OutDir <Pfad>"
Write-Host " Diff + HTML Report: .\BizTalkStatusTool.ps1 -Compare"
$ErrorActionPreference = "Stop"
$Global:LogFile = Join-Path (Get-Location) "BizTalkStatusTool.log"
# ======================================================
# LOG
# ======================================================
function Write-Log {
param([string]$Message,[ValidateSet('INFO','WARN','ERROR','DEBUG')]$Level='INFO')
$ts = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss")
$line = "[$ts][$Level] $Message"
$fg = switch ($Level) {
INFO {"Gray"} WARN {"Yellow"} ERROR {"Red"} DEBUG {"DarkCyan"}
}
Write-Host $line -ForegroundColor $fg
Add-Content -Path $Global:LogFile -Value $line
}
# ======================================================
# STATUS MAPPING
# ======================================================
function Format-Status {
param([string]$ArtifactType, $Value)
$r = [ordered]@{
Text = ""
Class = "neutral"
ConsoleColor = "Yellow"
}
switch ($ArtifactType) {
"ReceiveLocation" {
if ($Value -eq $true) {
$r.Text="Enabled"; $r.Class="ok"; $r.ConsoleColor="Green"
}
elseif ($Value -eq $false) {
$r.Text="Disabled"; $r.Class="bad"; $r.ConsoleColor="Red"
}
}
"SendPort" {
switch ([int]$Value) {
3 { $r.Text="Started"; $r.Class="ok"; $r.ConsoleColor="Green" }
2 { $r.Text="Stopped"; $r.Class="bad"; $r.ConsoleColor="Red" }
1 { $r.Text="Bound"; $r.Class="bad"; $r.ConsoleColor="Red" }
default { $r.Text="Unknown"; $r.Class="bad"; $r.ConsoleColor="Red" }
}
}
"Orchestration" {
switch ([int]$Value) {
4 { $r.Text="Started"; $r.Class="ok"; $r.ConsoleColor="Green" }
3 { $r.Text="Stopped"; $r.Class="bad"; $r.ConsoleColor="Red" }
2 { $r.Text="Bound"; $r.Class="bad"; $r.ConsoleColor="Red" }
default { $r.Text="Unknown"; $r.Class="bad"; $r.ConsoleColor="Red" }
}
}
"HostInstance" {
switch ([int]$Value) {
4 { $r.Text="Started"; $r.Class="ok"; $r.ConsoleColor="Green" }
2 { $r.Text="StartPending"; $r.Class="warn"; $r.ConsoleColor="Yellow" }
3 { $r.Text="StopPending"; $r.Class="warn"; $r.ConsoleColor="Yellow" }
1 { $r.Text="Stopped"; $r.Class="bad"; $r.ConsoleColor="Red" }
default { $r.Text="Unknown"; $r.Class="bad"; $r.ConsoleColor="Red" }
}
}
}
return [pscustomobject]$r
}
# ======================================================
# MGMT DB
# ======================================================
function Get-BizTalkMgmtDbInfo {
param([string]$MgmtDbServer,[string]$MgmtDbName)
$reg = "HKLM:\SOFTWARE\Microsoft\BizTalk Server\3.0\Administration"
try {
$p = Get-ItemProperty $reg
if (-not $MgmtDbServer) { $MgmtDbServer = $p.MgmtDBServer }
if (-not $MgmtDbName) { $MgmtDbName = $p.MgmtDBName }
}
catch {
if (-not $MgmtDbServer) { $MgmtDbServer = "localhost" }
if (-not $MgmtDbName) { $MgmtDbName = "BizTalkMgmtDb" }
}
return @{ Server=$MgmtDbServer; Name=$MgmtDbName }
}
# ======================================================
# HOST INSTANCES
# ======================================================
function Get-BizTalkHostInstances {
param([string]$Server)
$wmi = @{
Namespace = "root\MicrosoftBizTalkServer"
ComputerName = $Server
ErrorAction = 'Stop'
}
$list = Get-WmiObject MSBTS_HostInstance @wmi
$result = foreach ($hi in $list) {
$st = Format-Status -ArtifactType "HostInstance" -Value $hi.ServiceState
[pscustomobject]@{
InstanceName = $hi.InstanceName
HostName = $hi.HostName
Server = $hi.RunningServer
RawState = $hi.ServiceState
StateText = $st.Text
StateClass = $st.Class
ConsoleColor = $st.ConsoleColor
}
}
return $result
}
function Show-HostInstancesConsole {
param($HostInstances)
Write-Host "`n=== Host Instances ===" -ForegroundColor Yellow
foreach ($hi in $HostInstances) {
Write-Host ("{0,-35} {1,-20} {2,-10}" -f $hi.InstanceName,$hi.HostName,$hi.StateText) `
-ForegroundColor $hi.ConsoleColor
}
}
function Export-HostInstancesHtml {
param($HostInstances)
$html = @()
$html += "<h3>Host Instances</h3>"
$html += "<table>"
$html += "<tr><th>Instance</th><th>Host</th><th>Server</th><th>Status</th></tr>"
foreach ($hi in $HostInstances) {
$html += "<tr>
<td>$($hi.InstanceName)</td>
<td>$($hi.HostName)</td>
<td>$($hi.Server)</td>
<td class='$($hi.StateClass)'>$($hi.StateText)</td>
</tr>"
}
$html += "</table>"
return ($html -join "`n")
}
function Export-HostInstancesCsv {
param($HostInstances, [string]$CsvFile)
$HostInstances |
Select InstanceName,HostName,Server,StateText |
Export-Csv -NoTypeInformation -Encoding UTF8 -Path $CsvFile
}
# ======================================================
# SNAPSHOT
# ======================================================
function Get-BizTalkSnapshot {
param(
[string]$OutputFile,
[string]$Server,
[string]$MgmtDbServer,
[string]$MgmtDbName
)
Write-Log "Snapshot: Server=$Server File=$OutputFile"
[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.BizTalk.ExplorerOM")
$db = Get-BizTalkMgmtDbInfo -MgmtDbServer $MgmtDbServer -MgmtDbName $MgmtDbName
$cat = New-Object Microsoft.BizTalk.ExplorerOM.BtsCatalogExplorer
$cat.ConnectionString = "Server={0};Database={1};Integrated Security=SSPI" -f $db.Server,$db.Name
$apps = $cat.Applications
# WMI lookup tables
$wmi = @{ Namespace='root\MicrosoftBizTalkServer'; ComputerName=$Server; ErrorAction='Stop' }
$rlW = Get-WmiObject MSBTS_ReceiveLocation @wmi |
Select Name, @{n='Enabled';e={-not $_.IsDisabled}}, AdapterName, @{n='Address';e={$_.InboundTransportURL}}
$spW = Get-WmiObject MSBTS_SendPort @wmi |
Select Name, @{n='Status';e={$_.Status}}, @{n='PrimaryTransportType';e={$_.PTTransportType}},
@{n='PrimaryTransportAddress';e={$_.PTAddress}}
$orW = Get-WmiObject MSBTS_Orchestration @wmi |
Select Name, @{n='OrchestrationStatus';e={$_.OrchestrationStatus}}
$mapRL=@{}; foreach($i in $rlW){$mapRL[$i.Name]=$i}
$mapSP=@{}; foreach($i in $spW){$mapSP[$i.Name]=$i}
$mapOR=@{}; foreach($i in $orW){$mapOR[$i.Name]=$i}
$snapshot=@()
foreach ($app in $apps) {
$rlItems=@()
foreach ($rp in $app.ReceivePorts) {
foreach ($rl in $rp.ReceiveLocations) {
if ($mapRL.ContainsKey($rl.Name)) {
$w = $mapRL[$rl.Name]
$rlItems += [pscustomobject]@{
Name=$rl.Name
Enabled=$w.Enabled
AdapterName=$w.AdapterName
Address=$w.Address
}
}
}
}
$spItems=@()
foreach ($sp in $app.SendPorts) {
if ($mapSP.ContainsKey($sp.Name)) {
$w = $mapSP[$sp.Name]
$spItems += [pscustomobject]@{
Name=$sp.Name
Status=$w.Status
PrimaryTransportType=$w.PrimaryTransportType
PrimaryTransportAddress=$w.PrimaryTransportAddress
}
}
}
$orItems=@()
foreach ($o in $app.Orchestrations) {
$key = if ($mapOR.ContainsKey($o.FullName)) { $o.FullName } else { $o.Name }
if ($mapOR.ContainsKey($key)) {
$w = $mapOR[$key]
$orItems += [pscustomobject]@{
Name=$o.FullName
OrchestrationStatus=$w.OrchestrationStatus
}
}
}
$snapshot += [pscustomobject]@{
Application=$app.Name
ReceiveLocations=$rlItems
SendPorts=$spItems
Orchestrations=$orItems
}
}
# Host Instances
$hostInstances = Get-BizTalkHostInstances -Server $Server
# Export JSON (Applications + HostInstances)
$full = [pscustomobject]@{
Applications = $snapshot
HostInstances = $hostInstances
}
$full | ConvertTo-Json -Depth 8 | Out-File $OutputFile -Encoding UTF8
# Export CSV
$csvArtifact = foreach ($app in $snapshot) {
foreach ($rl in $app.ReceiveLocations) {
$st = Format-Status -ArtifactType "ReceiveLocation" -Value $rl.Enabled
[pscustomobject]@{
Application=$app.Application
Type="ReceiveLocation"
Name=$rl.Name
Status=$st.Text
}
}
foreach ($sp in $app.SendPorts) {
$st = Format-Status -ArtifactType "SendPort" -Value $sp.Status
[pscustomobject]@{
Application=$app.Application
Type="SendPort"
Name=$sp.Name
Status=$st.Text
}
}
foreach ($o in $app.Orchestrations) {
$st = Format-Status -ArtifactType "Orchestration" -Value $o.OrchestrationStatus
[pscustomobject]@{
Application=$app.Application
Type="Orchestration"
Name=$o.Name
Status=$st.Text
}
}
}
$csvArtifact |
Export-Csv -Encoding UTF8 -NoTypeInformation -Path ($OutputFile + ".csv")
Export-HostInstancesCsv -HostInstances $hostInstances -CsvFile ($OutputFile + ".hosts.csv")
# HTML
$html=@()
$html+="<html><head><style>
table{border-collapse:collapse;margin-bottom:20px;}
td,th{border:1px solid #999;padding:4px 8px;}
.ok{color:green;font-weight:bold;}
.bad{color:red;font-weight:bold;}
.warn{color:orange;font-weight:bold;}
</style></head><body>"
$html+="<h2>BizTalk Snapshot</h2>"
foreach ($app in $snapshot) {
$html+="<h3>Application: $($app.Application)</h3>"
if ($app.ReceiveLocations.Count -gt 0) {
$html+="<h4>Receive Locations</h4><table><tr><th>Name</th><th>Status</th></tr>"
foreach ($rl in $app.ReceiveLocations) {
$st = Format-Status -ArtifactType "ReceiveLocation" -Value $rl.Enabled
$html+="<tr><td>$($rl.Name)</td><td class='$($st.Class)'>$($st.Text)</td></tr>"
}
$html+="</table>"
}
if ($app.SendPorts.Count -gt 0) {
$html+="<h4>Send Ports</h4><table><tr><th>Name</th><th>Status</th></tr>"
foreach ($sp in $app.SendPorts) {
$st = Format-Status -ArtifactType "SendPort" -Value $sp.Status
$html+="<tr><td>$($sp.Name)</td><td class='$($st.Class)'>$($st.Text)</td></tr>"
}
$html+="</table>"
}
if ($app.Orchestrations.Count -gt 0) {
$html+="<h4>Orchestrations</h4><table><tr><th>Name</th><th>Status</th></tr>"
foreach ($o in $app.Orchestrations) {
$st = Format-Status -ArtifactType "Orchestration" -Value $o.OrchestrationStatus
$html+="<tr><td>$($o.Name)</td><td class='$($st.Class)'>$($st.Text)</td></tr>"
}
$html+="</table>"
}
}
# Add Host Instances
$html += Export-HostInstancesHtml -HostInstances $hostInstances
$html+="</body></html>"
$html -join "`n" | Out-File ($OutputFile + ".html") -Encoding UTF8
# Console Output
Write-Host "`n===== Snapshot =====" -ForegroundColor Yellow
foreach ($app in $snapshot) {
Write-Host "`nApplication: $($app.Application)" -ForegroundColor Cyan
foreach ($rl in $app.ReceiveLocations) {
$st = Format-Status -ArtifactType "ReceiveLocation" -Value $rl.Enabled
Write-Host (" RL {0,-60} {1}" -f $rl.Name,$st.Text) -ForegroundColor $st.ConsoleColor
}
foreach ($sp in $app.SendPorts) {
$st = Format-Status -ArtifactType "SendPort" -Value $sp.Status
Write-Host (" SP {0,-60} {1}" -f $sp.Name,$st.Text) -ForegroundColor $st.ConsoleColor
}
foreach ($o in $app.Orchestrations) {
$st = Format-Status -ArtifactType "Orchestration" -Value $o.OrchestrationStatus
Write-Host (" OR {0,-60} {1}" -f $o.Name,$st.Text) -ForegroundColor $st.ConsoleColor
}
}
Show-HostInstancesConsole -HostInstances $hostInstances
Write-Host "Snapshot erstellt: $(Resolve-Path $OutputFile)" -ForegroundColor Green
}
# ======================================================
# COMPARE (Before/After)
# ======================================================
function Compare-BizTalkSnapshots {
param(
[string]$Before,
[string]$After,
[string]$HtmlReport = "BizTalkDiff.html"
)
$b = Get-Content $Before | ConvertFrom-Json
$a = Get-Content $After | ConvertFrom-Json
$diff = @()
function AddDiff {
param($app,$type,$name,$before,$after)
$bf = Format-Status -ArtifactType $type -Value $before
$af = Format-Status -ArtifactType $type -Value $after
$script:diff += [pscustomobject]@{
Application = $app
ArtifactType= $type
Name = $name
BeforeText = $bf.Text
BeforeClass = $bf.Class
AfterText = $af.Text
AfterClass = $af.Class
}
}
foreach ($appAfter in $a.Applications) {
$appBefore = $b.Applications | Where-Object {$_.Application -eq $appAfter.Application}
# RECEIVE LOCATIONS
foreach ($x in $appAfter.ReceiveLocations) {
$old = $appBefore.ReceiveLocations | Where-Object {$_.Name -eq $x.Name}
if (-not $old) { AddDiff $appAfter.Application "ReceiveLocation" $x.Name $null $x.Enabled; continue }
if ($old.Enabled -ne $x.Enabled) { AddDiff $appAfter.Application "ReceiveLocation" $x.Name $old.Enabled $x.Enabled }
}
foreach ($x in $appBefore.ReceiveLocations) {
if (-not ($appAfter.ReceiveLocations | Where-Object {$_.Name -eq $x.Name})) {
AddDiff $appBefore.Application "ReceiveLocation" $x.Name $x.Enabled $null
}
}
# SEND PORTS
foreach ($x in $appAfter.SendPorts) {
$old = $appBefore.SendPorts | Where-Object {$_.Name -eq $x.Name}
if (-not $old) { AddDiff $appAfter.Application "SendPort" $x.Name $null $x.Status; continue }
if ($old.Status -ne $x.Status) { AddDiff $appAfter.Application "SendPort" $x.Name $old.Status $x.Status }
}
foreach ($x in $appBefore.SendPorts) {
if (-not ($appAfter.SendPorts | Where-Object {$_.Name -eq $x.Name})) {
AddDiff $appBefore.Application "SendPort" $x.Name $x.Status $null
}
}
# ORCHESTRATIONS
foreach ($x in $appAfter.Orchestrations) {
$old = $appBefore.Orchestrations | Where-Object {$_.Name -eq $x.Name}
if (-not $old) { AddDiff $appAfter.Application "Orchestration" $x.Name $null $x.OrchestrationStatus; continue }
if ($old.OrchestrationStatus -ne $x.OrchestrationStatus) {
AddDiff $appAfter.Application "Orchestration" $x.Name $old.OrchestrationStatus $x.OrchestrationStatus
}
}
foreach ($x in $appBefore.Orchestrations) {
if (-not ($appAfter.Orchestrations | Where-Object {$_.Name -eq $x.Name})) {
AddDiff $appBefore.Application "Orchestration" $x.Name $x.OrchestrationStatus $null
}
}
}
# ======================================================
# HOST INSTANCE DIFF (Option H-DIFF2)
# ======================================================
$beforeHI = $b.HostInstances
$afterHI = $a.HostInstances
$hiDiff_New=@()
$hiDiff_Removed=@()
$hiDiff_Changed=@()
# New
foreach ($hi in $afterHI) {
if (-not ($beforeHI | Where-Object {$_.InstanceName -eq $hi.InstanceName})) {
$hiDiff_New += $hi
}
}
# Removed
foreach ($hi in $beforeHI) {
if (-not ($afterHI | Where-Object {$_.InstanceName -eq $hi.InstanceName})) {
$hiDiff_Removed += $hi
}
}
# Changed
foreach ($hiAfter in $afterHI) {
$hiBefore = $beforeHI | Where-Object {$_.InstanceName -eq $hiAfter.InstanceName}
if ($hiBefore) {
if ($hiBefore.StateText -ne $hiAfter.StateText) {
$hiDiff_Changed += [pscustomobject]@{
Instance = $hiAfter.InstanceName
Before = $hiBefore.StateText
After = $hiAfter.StateText
}
}
}
}
# ======================================================
# JSON DIFF EXPORT
# ======================================================
$jsonDiff = [pscustomobject]@{
ArtifactsDiff = $diff
HostInstances = [pscustomobject]@{
New = $hiDiff_New
Removed = $hiDiff_Removed
Changed = $hiDiff_Changed
}
}
$jsonDiff | ConvertTo-Json -Depth 8 | Out-File "diff.json" -Encoding UTF8
# ======================================================
# CSV DIFF EXPORT
# ======================================================
$diff | Export-Csv -Path "diff.csv" -NoTypeInformation -Encoding UTF8
# ======================================================
# HTML DIFF EXPORT
# ======================================================
$html=@()
$html+="<html><head><style>
table{border-collapse:collapse;margin-bottom:20px;}
td,th{border:1px solid #999;padding:4px 8px;}
.ok{color:green;font-weight:bold;}
.bad{color:red;font-weight:bold;}
.warn{color:orange;font-weight:bold;}
</style></head><body>"
$html+="<h2>BizTalk Differences</h2>"
# Artifacts
$html+="<h3>Artifact Differences</h3>"
$html+="<table><tr><th>Application</th><th>Type</th><th>Name</th><th>Before</th><th>After</th></tr>"
foreach ($d in $diff) {
$html+="<tr>
<td>$($d.Application)</td>
<td>$($d.ArtifactType)</td>
<td>$($d.Name)</td>
<td class='$($d.BeforeClass)'>$($d.BeforeText)</td>
<td class='$($d.AfterClass)'>$($d.AfterText)</td>
</tr>"
}
$html+="</table>"
# HostInstance DIFF
$html+="<h3>Host Instance Changes</h3>"
# NEW
$html+="<h4>New</h4><table><tr><th>Instance</th><th>Host</th><th>Server</th><th>Status</th></tr>"
foreach ($hi in $hiDiff_New) {
$html+="<tr>
<td>$($hi.InstanceName)</td>
<td>$($hi.HostName)</td>
<td>$($hi.Server)</td>
<td class='$($hi.StateClass)'>$($hi.StateText)</td>
</tr>"
}
$html+="</table>"
# REMOVED
$html+="<h4>Removed</h4><table><tr><th>Instance</th><th>Host</th><th>Server</th><th>Status</th></tr>"
foreach ($hi in $hiDiff_Removed) {
$html+="<tr>
<td>$($hi.InstanceName)</td>
<td>$($hi.HostName)</td>
<td>$($hi.Server)</td>
<td class='bad'>$($hi.StateText)</td>
</tr>"
}
$html+="</table>"
# CHANGED
$html+="<h4>Changed</h4><table><tr><th>Instance</th><th>Before</th><th>After</th></tr>"
foreach ($hi in $hiDiff_Changed) {
$before = Format-Status -ArtifactType "HostInstance" -Value $hi.Before
$after = Format-Status -ArtifactType "HostInstance" -Value $hi.After
$html+="<tr>
<td>$($hi.Instance)</td>
<td class='$($before.Class)'>$($hi.Before)</td>
<td class='$($after.Class)'>$($hi.After)</td>
</tr>"
}
$html+="</table>"
$html+="</body></html>"
$html -join "`n" | Out-File $HtmlReport -Encoding UTF8
# ======================================================
# CONSOLE OUTPUT
# ======================================================
Write-Host "`n===== BizTalk DIFF =====" -ForegroundColor Yellow
if ($diff.Count -eq 0 -and
$hiDiff_New.Count -eq 0 -and
$hiDiff_Removed.Count -eq 0 -and
$hiDiff_Changed.Count -eq 0)
{
Write-Host "Keine Unterschiede." -ForegroundColor Green
}
else {
Write-Host "`n--- Artifacts ---" -ForegroundColor Cyan
foreach ($d in $diff) {
Write-Host "`nApplication: $($d.Application)" -ForegroundColor Cyan
Write-Host "Artifact: $($d.ArtifactType)"
Write-Host "Name: $($d.Name)"
Write-Host ("Before: {0}" -f $d.BeforeText) -ForegroundColor $(
if ($d.BeforeClass -eq "ok") {"Green"} elseif ($d.BeforeClass -eq "warn") {"Yellow"} else {"Red"})
Write-Host ("After: {0}" -f $d.AfterText) -ForegroundColor $(
if ($d.AfterClass -eq "ok") {"Green"} elseif ($d.AfterClass -eq "warn") {"Yellow"} else {"Red"})
}
Write-Host "`n--- Host Instances (New) ---" -ForegroundColor Cyan
foreach ($hi in $hiDiff_New) {
Write-Host ("NEW: {0} {1}" -f $hi.InstanceName,$hi.StateText) -ForegroundColor $hi.ConsoleColor
}
Write-Host "`n--- Host Instances (Removed) ---" -ForegroundColor Cyan
foreach ($hi in $hiDiff_Removed) {
Write-Host ("REMOVED: {0} {1}" -f $hi.InstanceName,$hi.StateText) -ForegroundColor Red
}
Write-Host "`n--- Host Instances (Changed) ---" -ForegroundColor Cyan
foreach ($hi in $hiDiff_Changed) {
Write-Host ("CHANGED: {0} {1} -> {2}" -f $hi.Instance,$hi.Before,$hi.After) -ForegroundColor Yellow
}
}
Write-Host "HTML Diff exportiert: $HtmlReport" -ForegroundColor Green
}
# ======================================================
# DISPATCH
# ======================================================
if (-not (Test-Path $OutDir)) {
New-Item -ItemType Directory -Path $OutDir | Out-Null
}
Set-Location $OutDir
Write-Log "Working dir: $(Get-Location)"
if ($Diag) {
Write-Host "DIAG → Server: $Server" -ForegroundColor Yellow
try {
$sp = Get-WmiObject MSBTS_SendPort -Namespace root\MicrosoftBizTalkServer -ComputerName $Server |
Select -First 3 Name,Status
Write-Host "WMI OK:" -ForegroundColor Green
$sp | Format-Table
}
catch {
Write-Host "ERROR: $($_.Exception.Message)" -ForegroundColor Red
}
exit
}
if ($Before) { Get-BizTalkSnapshot -OutputFile "before.json" -Server $Server -MgmtDbServer $MgmtDbServer -MgmtDbName $MgmtDbName; exit }
if ($After) { Get-BizTalkSnapshot -OutputFile "after.json" -Server $Server -MgmtDbServer $MgmtDbServer -MgmtDbName $MgmtDbName; exit }
if ($Compare){ Compare-BizTalkSnapshots -Before "before.json" -After "after.json"; exit }
Write-Host "Usage:"
Write-Host " .\BizTalkStatusTool.ps1 -Before"
Write-Host " .\BizTalkStatusTool.ps1 -After"
Write-Host " .\BizTalkStatusTool.ps1 -Compare"
Write-Host " .\BizTalkStatusTool.ps1 -Diag"