Friday, May 23, 2014

BizTalk Atomic Update for Side-by-Side Orchestration Deployment

Deploying orchestration side-by-side, you have to unenlist the old version and start the new version. To minimize the down time, the process should be done in atomic way so that the old orchestration would not pick the new messages while the new orchestration instances will be activated by the new messages.

BizTalk exposes the .Net API to manage the orchestrations. The following script demonstrates how to use PowerShell to invoke the API so that unenlisting and starting the orchestrations could be done atomically.

To use the script, update $connectionString and the expressions in $unenlisting and $starting variables so that they could match the assembly qualified names of the orchestrations. The script also takes one argument "test" or "trial" so that you could test out whether the expressions are correct.
### Settings of the script ###
$test = $FALSE
If ($args.Count -gt 0) {
    $test = ($args[0] -eq "test") -or ($args[0] -eq "trial")
}
$connectionString = "SERVER=.;DATABASE=BizTalkMgmtDb;Integrated Security=SSPI"
$unenlisting = `
    @( `
      '^Test.*' `
    )
$starting = `
    @( `
      '.*SubOrchestration.*' `
    )

### Initialize BizTalk catalog explorer ###
add-type -assemblyName "Microsoft.BizTalk.ExplorerOM, Version=3.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"
$catalog = New-Object -TypeName Microsoft.BizTalk.ExplorerOM.BtsCatalogExplorer
$catalog.ConnectionString = $connectionString

### Matching orchestration's assembly qualified name with the regular expressions in $unenlisting and $starting array ###
If ($test) {
    Write-Host "TEST MODE" -foregroundcolor white
    Write-Host
}
Try
{
    $catalog.Applications | ForEach-Object {
        $_.Orchestrations | ForEach-Object {
            $target = $_
### -like is the wildcard operator
### -match is the regular expression operator

#            $unenlisting | Where-Object {($target.AssemblyQualifiedName -like $_) -and ($target.Status -ne [Microsoft.BizTalk.ExplorerOM.OrchestrationStatus]::Unenlisted)} | ForEach-Object {
            $unenlisting | Where-Object {($target.AssemblyQualifiedName -match $_) -and ($target.Status -ne [Microsoft.BizTalk.ExplorerOM.OrchestrationStatus]::Unenlisted)} | ForEach-Object {
                Write-Host "Unenlisting" $target.FullName $target.BtsAssembly.Version "..." -foregroundcolor yellow
                $target.Status = [Microsoft.BizTalk.ExplorerOM.OrchestrationStatus]::Unenlisted
            }
#            $starting | Where-Object {($target.AssemblyQualifiedName -like $_) -and ($target.Status -ne [Microsoft.BizTalk.ExplorerOM.OrchestrationStatus]::Started)} | ForEach-Object {
            $starting | Where-Object {($target.AssemblyQualifiedName -match $_) -and ($target.Status -ne [Microsoft.BizTalk.ExplorerOM.OrchestrationStatus]::Started)} | ForEach-Object {
                Write-Host "Starting   " $target.FullName $target.BtsAssembly.Version "..." -foregroundcolor green
                $target.Status = [Microsoft.BizTalk.ExplorerOM.OrchestrationStatus]::Started
            }
        }
    }
    Write-Host
    If ($test) {
        $catalog.DiscardChanges()
        Write-Host "Changes rolled-back for test mode" -foregroundcolor white
    }
    Else {
        $catalog.SaveChanges()
        Write-Host "Changes committed" -foregroundcolor white
    }
}
Catch {
    $catalog.DiscardChanges()
    Write-Host "Changes rolled-back" -foregroundcolor red
    Write-Host $Error cyan
}
Write-Host