BizTalk Receive Location Duplicator in PowerShell

How do you create an identical copy of a BizTalk ReceiveLocation within a Receive Port? Well, it cannot be 100% identical. The name will of course have to change, the URI will have to be unique as well.

I recently had a case where we wanted the same orchestration to run on two different databases that had the same structure. The ReceiveLocation used WCF-SQL typed polling. I wanted to copy the original ReceiveLocation, and add a new one, except changing just a few minor things.

I wrote a PowerShell to do this. In the prior blog, I also created a “BizTalk Send Port Duplicator”.

The script builds a binding file to modify the ReceivePort and optionally imports it.

Receive Locations are children of the Receive Port, and so in the binding file, you cannot just add a Receive location by itself, You must specify the whole Receive Port with it’s other Receive Locations plus the new one.

You can edit it on disk and manually import yourself if you want. You might need to change some of the other options (such as directory name if using the FILE adapter), or SQL Stored Proc if using the WCF-SQL adapter. You can do that in the disk file, or let the script import the bindings, and then make the changes in BizTalk Admin Console. Set the variable $ynImportNewBinding to “Y” or “N” based on your preference.

Before running the script, find the eight parameters (or just variables actually), and change them in the script below:

#SET THESE EIGHT PARAMETERS
$applicationName = “TMW.Integration”
$sqlServer = “DLBizTalkDev1” #where the BizTalkMgmtDB is
$duplicateReceivePortName = “rp_ABC”
$duplicateReceiveLocation = “rl_ABC_Division1”
$newReceiveLocationName = “rl_ABC_Division2”
#Use the next two fields to change the URI or other patterns
$rcvLocChangeFrom = “Division1”
$rcvLocChangeTo = “Division2”
$ynImportNewBinding = “Y” #set to “Y” to auto-import the new SendPort, set to “N” if you want to edit it on disk

$BTSTaskFilePath = “e:\BizTalkServer2016\BTSTask.exe”

<pre>
<#

       Author: Neal Walters 
         Date: 04/15/2020 
  Description: Description: Duplicate a BizTalk ReceiveLocation 
         
      Details: User has to set five variables/parameters 
               (search for #SET THESE FIVE PARAMETERS). 

               Logic is as follows: 
               Export bindings, make a copy to work file, 
               remove each "collection", 
               pull out one ReceivePort, 
               duplicate the ReceiveLocation within it, put it back in, 
               and import new SendPort bindings. 
#>

function removeSection ($filename, $section) 
{
   $contents = [System.IO.File]::ReadAllText($filename)

   #Handle EmptyTag 
   $contents2 = $contents.Replace("<$section />", "") 
   if ($contents2 -eq $contents)
      {
           $contents = $contents2 

           #Handle non-EmptyTag
           $startPos = $contents.IndexOf("<$section") 
           $endPos = $contents.IndexOf("/$section>") 
           $endPos = $endPos + $section.Length 
           if ($endPos -gt $startPos) 
           {
              #Write-Host "$section : startPos=$startPos endPos=$endPos" 
              $contentsBefore = $contents.Substring(0,$startPos-1)
              $contentsAfter = $contents.Substring($endPos + 2) 
              $contents = $contentsBefore + $contentsAfter
           }
       }
       else 
       {
         $contents = $contents2 
       }
       [System.IO.File]::WriteAllText($filename, $contents) 
}

function AddReceivePort ($filename, $sendPortXML) 
{
   $contents = [System.IO.File]::ReadAllText($filename)
   $contents = $contents.Replace("</Timestamp>","</Timestamp>`n`n<ReceivePortCollection>`n$sendPortXML`n</ReceivePortCollection>`n")
   [System.IO.File]::WriteAllText($filename, $contents) 
}

function dupReceiveLocation ($filename, $receiveLocName, $receiveLocNewName, $rcvLocChangeFrom, $rcvLocChangeTo ) 
{
   $contents = [System.IO.File]::ReadAllText($filename)
   $startPos = $contents.IndexOf("<ReceiveLocation Name=`"$receiveLocName`"") 
   $endPos = $contents.IndexOf("</ReceiveLocation>",$startPos) 
   $extractLength = $endPos - $startPos + $section.Length + 18
   $rcvLocBinding = $contents.Substring($startPos, $extractLength) 
   $rcvLocBinding = $rcvLocBinding.Replace($receiveLocName, $receiveLocNewName) 
   $contents = $contents.Replace("</ReceiveLocations>","`n$rcvLocBinding`n</ReceiveLocations>")
   $contents = $contents.Replace($rcvLocChangeFrom, $rcvLocChangeTo)
   #we don't want to copy the flag that says this is the primary Receive Location 
   $contents = $contents.Replace("<Primary>true</Primary>","<Primary>false</Primary>")
   [System.IO.File]::WriteAllText($filename, $contents) 
}

function getReceivePort ($filename, $rcvPortName) 
{
   $contents = [System.IO.File]::ReadAllText($filename)
   $startPos = $contents.IndexOf("<ReceivePort Name=`"$rcvPortName`"") 
   $endPos = $contents.IndexOf("</ReceivePort",$startPos) 
   $extractLength = $endPos - $startPos + $section.Length + 14
   $endPos = $endPos + $section.Length 
   #Write-Host "$section : startPos=$startPos endPos=$endPos Length=$extractLength" 
   $contentsReceivePort = $contents.Substring($startPos, $extractLength) 
   return $contentsReceivePort 
}

function fileReplace ($filename, $searchString, $replaceString) 
{
   $contents = [System.IO.File]::ReadAllText($filename)
   #Write-Host "fileReplace: Read Len of contents= $($contents.length)" 
   $contents = $contents.Replace($searchString, $replaceString) 
   #Write-Host $contents 
   #Write-Host "fileReplace: Write Len of contents= $($contents.length) from $filename" 
   [System.IO.File]::WriteAllText($filename, $contents) 
}

cls
$ErrorActionPreference = "Stop"
$formatDate = Get-Date -Format "yyyy-MM-dd hh:mm:ss"
Write-Host “Start: DateTime= $formatDate”

#------------------------------------------------------
#SET THESE EIGHT PARAMETERS
$applicationName = "TMW.Integration" 
$sqlServer = "DLBizTalkDev1"    #where the BizTalkMgmtDB is 
$duplicateReceivePortName = "rp_ABC" 
$duplicateReceiveLocation = "rl_ABC_Division1"
$newReceiveLocationName   = "rl_ABC_Division2"
#Use the next two fields to change the URI or other patterns 
$rcvLocChangeFrom = "Division1" 
$rcvLocChangeTo = "Division2" 
$ynImportNewBinding = "Y"   #set to "Y" to auto-import the new SendPort, set to "N" if you want to edit it on disk 
#------------------------------------------------------

$formatDateTimeForFile = get-date -f yyyy_MM_dd
$username = $env:username
$exportFileNameOrig = "c:\Users\$username\AppData\Local\BizTalk\Binding Files\$applicationName.Binding_$formatDateTimeForFile.xml" 
$exportFileNameWork = "c:\Users\$username\AppData\Local\BizTalk\Binding Files\$applicationName.Binding_$($formatDateTimeForFile)_ReceivePort.xml" 

Write-Host $exportFileNameOrig
Write-Host $exportFileNameWork

#------------------------------------------------------

#$BTSTaskResults = &"$BTSTaskFilePath" ImportBindings /ApplicationName:$applicationName /Source:"$($file.FullName)" /Server:"$SqlServer" /Database:BizTalkMgmtDb 
$BTSTaskResults = &"$BTSTaskFilePath" ExportBindings /ApplicationName:$applicationName /Destination:"$exportFileNameOrig" /Server:"$SqlServer" /Database:BizTalkMgmtDb 
copy-item -Path $exportFileNameOrig -Destination $exportFileNameWork
Write-Host "============================================="
Write-Host "EXPORT BINDINGS RESULTS:" 
Write-Host $BTSTaskResults
Write-Host "============================================="

#call functions 
removeSection $exportFileNameWork "ModuleRefCollection" 
removeSection $exportFileNameWork "DistributionListCollection" 
removeSection $exportFileNameWork "SendPortCollection" 
fileReplace   $exportFileNameWork "<PartyCollection xsi:nil=`"true`" />" ""
$receivePortBindings = getReceivePort $exportFileNameWork $duplicateReceivePortName
#$receivePortBindings = $receivePortBindings.Replace($duplicateSendPort, $newSendPortName) 
Write-Host "receivePortBindings.Len=$($receivePortBindings.Length)" 
removeSection $exportFileNameWork "ReceivePortCollection" 
addReceivePort $exportFileNameWork $receivePortBindings 
dupReceiveLocation $exportFileNameWork $duplicateReceiveLocation $newReceiveLocationName $rcvLocChangeFrom $rcvLocChangeTo 

Write-Host "FileName: $exportFileNameWork" 
if ($ynImportNewBinding -eq "Y")   
   {
        $BTSTaskResults = &"$BTSTaskFilePath" ImportBindings /ApplicationName:$applicationName /Source:"$exportFileNameWork" /Server:"$SqlServer" /Database:BizTalkMgmtDb 
        Write-Host "============================================="
        Write-Host "IMPORT BINDINGS RESULTS:" 
        Write-Host $BTSTaskResults
        Write-Host "============================================="
   }
else 
    {
       Write-Host "You have chose to manually edit the file (not improted into Biztalk)" 
    }

$formatDate = Get-Date -Format "yyyy-MM-dd hh:mm:ss"
Write-Host “End : DateTime= $formatDate”

</pre>

Here are references to other tools that do similar functions. I like the idea of having a simple script that I can customize as needed and I know exactly what it’s doing.

Sandro Pereira Port Multiplier Tool (Aug 2019)
Richard Seroter’s BizTalk Send Port Duplicator (from 2007)

Uncategorized  

Leave a Reply