<# 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) #> # ====================================================== # PARAMETERS # ====================================================== param( [switch]$Before, [switch]$After, [switch]$Compare, [switch]$Diag, [string]$Server, [string]$MgmtDbServer, [string]$MgmtDbName, [string]$OutDir ) if (-not $Server) { $Server = $env:COMPUTERNAME } if (-not $OutDir) { $OutDir = "." } $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 += "

Host Instances

" $html += "" $html += "" foreach ($hi in $HostInstances) { $html += "" } $html += "
InstanceHostServerStatus
$($hi.InstanceName) $($hi.HostName) $($hi.Server) $($hi.StateText)
" 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+="

BizTalk Snapshot

" foreach ($app in $snapshot) { $html+="

Application: $($app.Application)

" if ($app.ReceiveLocations.Count -gt 0) { $html+="

Receive Locations

" foreach ($rl in $app.ReceiveLocations) { $st = Format-Status -ArtifactType "ReceiveLocation" -Value $rl.Enabled $html+="" } $html+="
NameStatus
$($rl.Name)$($st.Text)
" } if ($app.SendPorts.Count -gt 0) { $html+="

Send Ports

" foreach ($sp in $app.SendPorts) { $st = Format-Status -ArtifactType "SendPort" -Value $sp.Status $html+="" } $html+="
NameStatus
$($sp.Name)$($st.Text)
" } if ($app.Orchestrations.Count -gt 0) { $html+="

Orchestrations

" foreach ($o in $app.Orchestrations) { $st = Format-Status -ArtifactType "Orchestration" -Value $o.OrchestrationStatus $html+="" } $html+="
NameStatus
$($o.Name)$($st.Text)
" } } # Add Host Instances $html += Export-HostInstancesHtml -HostInstances $hostInstances $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+="

BizTalk Differences

" # Artifacts $html+="

Artifact Differences

" $html+="" foreach ($d in $diff) { $html+="" } $html+="
ApplicationTypeNameBeforeAfter
$($d.Application) $($d.ArtifactType) $($d.Name) $($d.BeforeText) $($d.AfterText)
" # HostInstance DIFF $html+="

Host Instance Changes

" # NEW $html+="

New

" foreach ($hi in $hiDiff_New) { $html+="" } $html+="
InstanceHostServerStatus
$($hi.InstanceName) $($hi.HostName) $($hi.Server) $($hi.StateText)
" # REMOVED $html+="

Removed

" foreach ($hi in $hiDiff_Removed) { $html+="" } $html+="
InstanceHostServerStatus
$($hi.InstanceName) $($hi.HostName) $($hi.Server) $($hi.StateText)
" # CHANGED $html+="

Changed

" foreach ($hi in $hiDiff_Changed) { $before = Format-Status -ArtifactType "HostInstance" -Value $hi.Before $after = Format-Status -ArtifactType "HostInstance" -Value $hi.After $html+="" } $html+="
InstanceBeforeAfter
$($hi.Instance) $($hi.Before) $($hi.After)
" $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"