#! 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 #> $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 = @"
Vergleich: $Before → $After
| Application | Artifact | Name | Before | After |
|---|---|---|---|---|
| $($d.Application) | $($d.ArtifactType) | $($d.Name) | $($d.BeforeText) | $($d.AfterText) |