Sometimes you ask yourself what version of BizTalk is running on a certain server. Maybe you’re a consultant or employee stepping into a new job/contract and you are trying to get the lay of the land. Or maybe you have some old servers that are not documented.

The following script will answer that question. Might save keystrokes of opeining Control Panel or RegEdit to find out.

See this page to get a list of all the BizTalk versions. Hope you don’t find anything older than BizTalk 2010!

cls
#read the registry 
$item = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\BizTalk Server\3.0"
Write-Host "Biztalk Internal Version: $($item.ProductVersion)"
$major,$minor, $x, $y = $item.ProductVersion.split('.') 
#Write-Host "$major $minor" 

# see https://social.technet.microsoft.com/wiki/contents/articles/7915.biztalk-server-versions.aspx 

$version = switch ($minor)
{
    13 {"2019"}
    12 {"2016"}
    11 {"2013/R2"}
    10 {"2013 (not R2)"}
    9 {"2010"} 
    8 {"2009"} 
    6 {"2006/R2"} 
    5 {"2006 (not R2)"} 
    0 {"2000-2004 Past Time to Upgrade! "} 
}

Write-Host "BizTalk Common Name: $version" 
Write-Host "Product Edition: $($item.ProductEdition)" 

Example Output:

Biztalk Internal Version: 3.10.229.0
BizTalk Common Name: 2013 (not R2)
Product Edition: Enterprise

BizTalk has a schedule job that does incremental backups, usually every 15 minutes. But what if you want to do a full backup immediate?

Run this command in SQL Server Management Studio (SSMS):

USE BizTalkMgmtDb
EXEC sp_ForceFullBackup

Then manually start the SQL agent job “Backup BizTalk Server (BizTalkMgmtDb)” (or wait until the next scheduled 15 minute interval).

The above is required if you get in the situation where you see this error in the 3rd step of the above backup agent job:

Error: BACKUP LOG cannot be performed because there is no current database backup.

test

This script can be scheduled to create a nightly backup of all the bindings and MSIs for all the applications in BizTalk. It uses the Powershell Extensions for BizTalk to enumerate the application names, then calls BTSTask once for the bindings, and once for the MSI.

It gives you the ability to customize your backup folder names and files names. For example, if your application name is “ABC.Orders”, the backups are:
ABC.Orders_2019_05_17__09_10_21.xml
ABC.Orders_2019_05_17__09_10_21.msi

You could make changes to create a new folder for each set of backups, example: Backups_2019_05_17__09_10_21.

Another enhancement could be to zip the archives, or to purge archives over x days.

There is one trick. You may also want to backup the Parties. I’m currently on BizTalk 2013, and I know there are some improvements for supporting importing/exporting parties individually in 2016. Given that I’m on BT2013, I decided to backup the parties just once. So if the application name = “BizTalk EDI Application”, I add the parameter for the party backup, and they will be in that file. You could also create a dummy empty application and use it.

<#

       Author: Neal Walters 
  Description: Backup BizTalk Applications (both Bindings and Application/MSIs) via Script 
         Date: May 2019 

#>

Add-PSSnapIn -Name BiztalkFactory.PowerShell.Extensions
#This next line only needed when SQL server is not on same machine as BizTalk
New-PSDrive -Name BizTalk -Root BizTalk:\ -PsProvider BizTalk -Instance "YourSQLServer" -Database BizTalkMgmtDb

$exportFolder = "c:\Backup\BizTalk" 
$server = "YourSQLServer"
$database = "BizTalkMgmtDB"

cls

$apps = Get-ChildItem -Path "Biztalk:\Applications"

foreach ($app in $apps) 
    {
        Write-Host "===== Application: $($app.Name) ==== " 

        if ($app.Name -ne "BizTalk.System")   # avoid error trying to export this app 
          {
            $fmtDateTime = $(get-date -f yyyy_MM_dd__HH_mm_ss)
            $bindingsDestFilename = "${exportFolder}\$($app.Name)_$fmtDateTime.xml" 
            $msiAppDestFilename = "${exportFolder}\$($app.Name)_$fmtDateTime.msi" 
           
            write-host $bindingsDestFilename 
            $allArgs = @("ExportBindings", "/Destination:$bindingsDestFilename",  "/ApplicationName:$($app.Name)",
                "/Server:$server", "/Database:$database") 
            if ($app.Name -eq "BizTalk EDI Application") 
            {
               #just backup the parties one time with this one app 
               $allArgs += "/GlobalParties"
            }

            write-host "Args=$allArgs"
            & "c:\Program Files (x86)\Microsoft BizTalk Server 2013\BTSTask.exe" $allArgs

            $allArgs = @("ExportApp", "/Package:$msiAppDestFilename",  "/ApplicationName:$($app.Name)",
                "/Server:$server", "/Database:$database") 
            write-host "Args=$allArgs"
            & "c:\Program Files (x86)\Microsoft BizTalk Server 2013\BTSTask.exe" $allArgs
          }
    }

 

I’m writing a custom deploy solution for BizTalk. If the application doesn’t exist, I need to call “BTSTask AddApp” to add it.

Sample:


Add-PSSnapIn -Name BiztalkFactory.PowerShell.Extensions

function checkApplicationExists ($appName) 
{
    cd "Biztalk:\Applications"
    $apps = Get-ChildItem
    $boolFoundMatch = $false 
    foreach ($app in $apps) 
      {
         Write-Host "  Testing $($app.Name) " 
         if ($app.Name -eq $appname) 
         {
            $boolFoundMatch = $true 
         }
      }
    return $boolFoundMatch 
  
}

cls
$app1Exists = checkApplicationExists "Test.Vendors"
Write-Host "REI.Vendors $app1Exists" 

$app2Exists = checkApplicationExists "Test.Nonexistent"
Write-Host "REI.Vendor.TestDeploy $app2Exists" 


PowerShell extensions allow you to interact with BizTalk Artificacts. For example, you can like list applications, sendports, receive ports, host instances, and enable/disable them in PowerShell.

First run the InstallUtil

C:\Windows\system32>c:\Windows\Microsoft.NET\Framework64\v4.0.30319\InstallUtil.exe "c:\Program Files (x86)\Microsoft BizTalk Server 2013\SDK\Utilities\PowerShell\BizTalkFactory.PowerShell.Extensions.dll"

Output:
Microsoft (R) .NET Framework Installation utility Version 4.7.2558.0
Copyright (C) Microsoft Corporation. All rights reserved.

Running a transacted installation.

Beginning the Install phase of the installation.
See the contents of the log file for the c:\Program Files (x86)\Microsoft BizTal
k Server 2013\SDK\Utilities\PowerShell\BizTalkFactory.PowerShell.Extensions.dll
assembly’s progress.
The file is located at c:\Program Files (x86)\Microsoft BizTalk Server 2013\SDK\
Utilities\PowerShell\BizTalkFactory.PowerShell.Extensions.InstallLog.
Installing assembly ‘c:\Program Files (x86)\Microsoft BizTalk Server 2013\SDK\Ut
ilities\PowerShell\BizTalkFactory.PowerShell.Extensions.dll’.
Affected parameters are:
logtoconsole =
assemblypath = c:\Program Files (x86)\Microsoft BizTalk Server 2013\SDK\Utili
ties\PowerShell\BizTalkFactory.PowerShell.Extensions.dll
logfile = c:\Program Files (x86)\Microsoft BizTalk Server 2013\SDK\Utilities\
PowerShell\BizTalkFactory.PowerShell.Extensions.InstallLog

The Install phase completed successfully, and the Commit phase is beginning.
See the contents of the log file for the c:\Program Files (x86)\Microsoft BizTal
k Server 2013\SDK\Utilities\PowerShell\BizTalkFactory.PowerShell.Extensions.dll
assembly’s progress.
The file is located at c:\Program Files (x86)\Microsoft BizTalk Server 2013\SDK\
Utilities\PowerShell\BizTalkFactory.PowerShell.Extensions.InstallLog.
Committing assembly ‘c:\Program Files (x86)\Microsoft BizTalk Server 2013\SDK\Ut
ilities\PowerShell\BizTalkFactory.PowerShell.Extensions.dll’.
Affected parameters are:
logtoconsole =
assemblypath = c:\Program Files (x86)\Microsoft BizTalk Server 2013\SDK\Utili
ties\PowerShell\BizTalkFactory.PowerShell.Extensions.dll
logfile = c:\Program Files (x86)\Microsoft BizTalk Server 2013\SDK\Utilities\
PowerShell\BizTalkFactory.PowerShell.Extensions.InstallLog

The Commit phase completed successfully.

The transacted install has completed.

Next, in Powershell (assuming you have already set your execution policy):

If your SQL Server is on same machine as BizTalk this should work:

Add-PSSnapIn -Name BiztalkFactory.PowerShell.Extensions

#Verify it works by listing the BizTalk Applications 
cd "Biztalk:\Applications\"
Get-ChildItem | ft -auto 

If you SQL Server is on a different machine than the BizTalk server, then add the New-PSDrive statement as below (StackOverflow)

Add-PSSnapIn -Name BiztalkFactory.PowerShell.Extensions
New-PSDrive -Name BizTalk -Root BizTalk:\ -PsProvider BizTalk -Instance MySqlServer -Database BizTalkMgmtDb

#Verify it works by listing the BizTalk Applications 
cd "Biztalk:\Applications\"
Get-ChildItem | ft -auto 

The field “FilterBytes” contains the subscription filters for batching. We are converting from 2010 to 2013, and I need the a report of the “source of truth” as to which of our customers goes to which party, so we build the same parties correctly in 2013.

select top 1000 
  p1.Name as PartnerNameA, 
  p2.Name as PartnerNameB, 
  ag.Name as AgreementName,
  bd.OnewayAgreementId,
  bd.Name as BatchDescriptionName,
  ag.PartnershipId,
  --bd.ReleaseCriteriaType,
  --bd.MessageCount,
  --bd.MessageScope,
  --bd.InterchangeSize,
  --bd.RecurrenceType,
  --bd.FilterBytes,
  --Convert(varchar(max), bd.FilterBytes) as 'ConvertFilterBytes',
  replace(replace(replace(replace(replace(replace(replace(CONVERT(VARCHAR(max), FilterBytes),'','') ,'',''), '',''),'',''),'',''),'',''),'','') as FilterBytesStripped,
  p1.PartnerId as P1_PartnerID,
  p2.PartnerId as P2_PartnerID,
  ps.PartnerAId ,
  ps.PartnerBId 
  from tpm.BatchDescription bd 
  inner join tpm.Agreement ag on ag.ReceiverOnewayAgreementId = bd.OnewayAgreementId 
  inner join tpm.Partnership ps on ps.PartnershipId = ag.PartnershipId 
  inner join tpm.Partner p1 on p1.PartnerId = ps.PartnerAId 
  inner join tpm.Partner p2 on p2.PartnerId = ps.PartnerBID 
  where bd.Protocol = 'x12' 
  order by p1.Name 

The replace functions above remove some of the special characters (but not all of them). Some of those special characters are used as separators between keywords like filterOperator, filterValue, and property and the following value. Sorry if the replace characters don’t come through to the blog; not sure how to easily put them here.
FilterBytes is stored in hex, so you can use the convert to VARCHAR(max) to make it somewhat more readable.

You can take a sample value of FilterBytes and put in NotePad++.
If you split the FilterBytes on the at sign, it looks something like this:

@FilterPredicateOhttp://schemas.datacontract.org/2004/07/Microsoft.BizTalk.B2B.PartnerManagement i)http://www.w3.org/2001/XMLSchema-instance@groups@FilterGroup
@ statements
@FilterStatement
@filterOperator™Equals
@filterValue™?http://schemas.microsoft.com/BizTalk/EDI/X12/2006#X12_00501_210
@property™BTS.MessageType
@FilterStatement
@filterOperator™Equals@filterValue™123456
@property™5ABC.Client.Schemas.EDI210_5010.ST03_CustId

Note: Some of the other special characters seem to prevent the find command from working in NotePad++ and even in Powershell. I never stopped to figure that out or research more.

I don’t have access to 2010 prod where I work, so I asked the BizTalk admin to run the above query then save to a CSV. I then wrote a Powershell to do the more difficult work of parsing the filterBytes there. I might be able to share that in the future.

You might open a file like this:

ISA*00* *00* *ZZ*123000013 *ZZ*ABC3001 *160719*1600*U*00401*201160030*0*P*;~

and then want to know what is company with ID=123000013? I don’t know of any any way in the BizTalk Admin console to do a reverse lookup like this.


select 
       P.PartnerId as 'ParterID', 
       P.Name as 'PartyName', 
       BP.Name as 'ProfileName', 
	   BP.ProfileID,
	   BI.Qualifier, 
	   BI.Description, 
	   BI.Value, 
       P2.PartnerId as 'PartnerID', 
	   P2.Name as 'OtherPartyName', 
	   BP2.Name as 'OtherProfileName' ,
	   BP2.ProfileID,
	   BI2.Qualifier, 
	   BI2.Description, 
	   BI2.Value, 
	   * 
from tpm.BusinessIdentity  BI 
inner join tpm.BusinessProfile BP   on BI.ProfileId = BP.ProfileId
inner join tpm.Partner P            on P.PartnerID = BP.PartnerID 
inner join tpm.Partnership ps       on P.PartnerID = ps.PartnerAId 
inner join tpm.Partner P2           on P2.PartnerID = ps.PartnerBId
inner join tpm.BusinessProfile BP2  on P2.PartnerID = BP2.PartnerID 
inner join tpm.BusinessIdentity BI2 on BI2.ProfileId = BP2.ProfileId
where BI.Value = '123000013' 
-- and BI2.Value = 'xxxxxxx'
order by P.Name, BP.Name 

I’ve been doing a lot of “forensic” research on converting old systems lately. Sometimes I need to look at hundreds of ports at once, and opening each one separately to check the map is way to slow!

There are several joins required. The actual mapname is in the more generic bts_item table. You can get there by joining bt_MapSpec.

use BizTalkMgmtDb
select app.nvcName,
       sp.nvcName as SendPort, 
       item.Name as MapName,
       spt.nvcAddress, 
       bTwoWay, 
       sp.nvcSendPipelineData, 
       sp.nvcFilter
  from bts_sendport sp 
inner join bts_sendport_transport spt on spt.nSendPortID = sp.nID and spt.nTransportTypeId is not null 
inner join bts_sendport_transform spMap on spMap.nSendPortID = sp.nID 
inner join bt_MapSpec ms on ms.id = spmap.uidTransformGUID 
inner join bts_item item on ms.itemid = item.id 
inner join bts_application app on sp.nApplicationID = app.nID 
where sp.nvcName like '%210%'
order by app.nvcName, sp.nvcName 

select app.nvcName,
       rp.nvcName as ReceivePort, 
       item.Name as MapName,
       bTwoWay
from bts_receiveport rp 
inner join bts_receiveport_transform tr on tr.nReceivePortID = rp.nID 
inner join bt_MapSpec ms on ms.id = tr.uidTransformGUID 
inner join bts_item item on ms.itemid = item.id 
inner join bts_application app on rp.nApplicationID = app.nID 
where rp.nvcName like '%210%'
order by app.nvcName, rp.nvcName 

This program uses a mix of Object Explore Model (ExplorerOM) and BizTalk Provider for Powershell
to delete Send/ReceivePorts that match certain naming conventions.

We were refactoring an application and re-adding ports under a different application, so needed a script to delete the old pots.

If you run this script, I do of course suggest you run it once with the Delete Statements commented out!
Recode the selection criteria as needed for your purposes.

Parts of code from examples here: https://docs.microsoft.com/en-us/biztalk/core/receiveports-biztalk-server-sample

#===============================================================#
#=== ===#
#=== Delete the receive port named “My Receive Port” ===#
#=== ===#
#===============================================================#
Function DeleteSendPort ($sendPortName)
{
$receivePort = $catalog.ReceivePorts[$sendPortName]

if ($receivePort -ne $null)
{
$catalog.RemoveReceivePort($receivePort)

#=== Try to commit the changes made so far. If the commit fails, ===#
#=== roll back all changes in the trap handler. ===#
$catalog.SaveChanges()
}
}

Function DeleteReceivePort ($receivePortName)
{
$receivePort = $catalog.ReceivePorts[$receivePortName]

if ($receivePort -ne $null)
{
$catalog.RemoveReceivePort($receivePort)

#=== Try to commit the changes made so far. If the commit fails, ===#
#=== roll back all changes in the trap handler. ===#
$catalog.SaveChanges()
}
}

#===================#
#=== Main Script ===#
#===================#

#=== Make sure the ExplorerOM assembly is loaded ===#

[void] [System.reflection.Assembly]::LoadWithPartialName(“Microsoft.BizTalk.ExplorerOM”)

#=== Connect to the BizTalk Management database ===#

$Catalog = New-Object Microsoft.BizTalk.ExplorerOM.BtsCatalogExplorer
$MyBTSQLServer = “.” #substitute your SQL server name here
$Catalog.ConnectionString = “SERVER=$MyBTSQLServer;DATABASE=BizTalkMgmtDb;Integrated Security=SSPI”

#==================================================================#
#=== Register a trap handler to discard changes on exceptions ===#
#=== Execution will continue in the event we want to delete the ===#
#=== receive port. ===#
#==================================================================#

$Script:NoExceptionOccurred = $true
$ErrorActionPreference=”silentlycontinue”
trap
{
$Script:NoExceptionOccurred = $false
“Exception encountered:`r`n”; $_; “`r`nDiscarding changes and continuing execution so we can attempt to clean up the receive port…`r`n”
$Catalog.DiscardChanges()
}

#=== Create the new receive port ===#
#Write-Host “`r`nAttempting to create `”My Receive Port`”…”
#CreateReceivePort

# used on EDI1 to get lists of TRLG send/receive ports related to 204/990
# and create CSV file
cls
$showDate = Get-Date -DisplayHint Date
Write-Host “Started at $showDate”

Add-PSSnapIn -Name BiztalkFactory.PowerShell.Extensions #NOTE: Must be in 32-bit version of Powershell to use this SnapIn
#New-PSDrive -Name BizTalk -Root BizTalk:\ -PsProvider BizTalk -Instance EDI1-BTSDB2014\DB04 -Database BizTalkMgmtDb

Write-Host “— SendPorts —”
cd “Biztalk:\Applications\Echo.BSSR.Surface\Send Ports”
$SendPorts = Get-ChildItem
ForEach ($SendPort in $SendPorts)
{
#Write-Host $SendPort.Name $SendPort.Status
if (
($SendPort.Name.Contains(‘204’) -and $SendPort.Name.ToUpper().Contains(‘OUT’)) -or
($SendPort.Name.Contains(‘990’) -and $SendPort.Name.ToUpper().Contains(‘IN’))
)
{
## two formats of SendPorts spOutbound204XXXX and spSurfaceXXXoutbound204
Write-Host “Delete SendPort $($SendPort.Name)”
#DeleteSendPort($SendPort.Name)

}
}

Write-Host “— Receives —”
cd “Biztalk:\Applications\Echo.BSSR.Surface\Receive Ports”
$ReceivePorts = Get-ChildItem
ForEach ($ReceivePort in $ReceivePorts)
{
#Write-Host $ReceivePort.Name
if ($ReceivePort.Name.Contains(‘990’) -and $ReceivePort.Name.ToUpper().Contains(‘INBOUND’) )
{
Write-Host “Delete ReceivePort $($ReceivePort.Name )”
#DeleteReceivePort($ReceivePort.Name)

}
}

$showDate = Get-Date -DisplayHint Date
Write-Host “Completed at $showDate”

If you try to delete an application or a SendPort, you might find that SendPort is used on an EDI Trading-Partner (party).
There is no easy xref in BizTalk to show you which party might be tied to that send port. Usually the naming conventions will help, but not always.
This SQL query gives you the cross-reference.

USE BizTalkMgmtDb

SELECT 
   party.nvcname [PartyName],
   application.nvcDescription [Application],
   sendport.nvcname [SendPort]
FROM bts_sendport sendport
INNER JOIN bts_party_sendport partysendport ON partysendport.nsendportid = sendport.nid
INNER JOIN bts_party party ON partysendport.npartyid = party.nid
INNER JOIN bts_application application on application.nid = sendport.nApplicationID 
ORDER BY party.nvcname,  sendport.nvcname