Monday, October 10

BizTalk automated applications installation in multi-domain environment - Powershell sample

In this sample, I have created a bat file which is used to automatically install BizTalk applications onto a BTS installation via powershell using the brilliant BizTalk Powershell Provider from codeplex (http://psbiztalk.codeplex.com/),. This script can be used for installing one application or for a full deployment of a suite of applications on the same server.

Next, i will post a sample which backs up the current BizTalk Server Applications and Restores if a deployment goes wrong

The scenario assumes the development domain (MyCompanyDEV) is different from the test domain (MyCompanyTEST) and therefore calls different environment variables depending on domains

A shared location will need to be created on a computer which is accessible from each domains. A folder on this computer will be used to store the settings files for each computer

file://sharedcomputerdev/Bank/BizTalkServerSettings/ for the dev domain
file://sharedcomputertest/Bank/BizTalkServerSettings/ for the test domain

Name the settings file after your installation computer and create a similar one (to the one below) in this location for every computer on which the installation scripts will be used

FILE 1
SetInputs-sampleinputs.xml

<?xml version="1.0" encoding="utf-8" ?>
<BTS2010Config>
 <BTS2010InstallationPath>
C:\Program Files (x86)\Microsoft BizTalk Server 2010\
</BTS2010InstallationPath>
 <ESBToolkitInstallationPath>
C:\Program Files (x86)\Microsoft BizTalk ESB Toolkit 2.1\
</ESBToolkitInstallationPath>
 <BTSConnectionString>
server=<your bts server name>;database=BizTalkMgmtDb;Integrated Security=SSPI
</BTSConnectionString>
 <BTSDBServerName>localhost</BTSDBServerName>
 <BTSDBName>BizTalkMgmtDb</BTSDBName>
</BTS2010Config>

(We are assuming the computer running the script is called devcomputer101)
FILE 2
SetInputs-devcomputer101.xml

<?xml version="1.0" encoding="utf-8" ?>
<BTS2010Config>
 <BTS2010InstallationPath>
C:\Program Files (x86)\Microsoft BizTalk Server 2010\
</BTS2010InstallationPath>
 <ESBToolkitInstallationPath>
C:\Program Files (x86)\Microsoft BizTalk ESB Toolkit 2.1\
</ESBToolkitInstallationPath>
 <BTSConnectionString>
server=<your bts server name>;database=BizTalkMgmtDb;Integrated Security=SSPI
</BTSConnectionString>
 <BTSDBServerName>localhost</BTSDBServerName>
 <BTSDBName>BizTalkMgmtDb</BTSDBName>
</BTS2010Config>

The bat file calls a script file (release.ps1) which contains the instructions for the deployment
The script file in turn calls a functions file which loads all the functions defined for the deployment tasks

This scripts have been written for powershell 2.0

The sample simulates a canonical scenario where there is an application (myapps.biztalk.commonartifacts) containing shared schemas used by 2 other applications. This commonartifacts application therefore needs to be installed first and removed last in any deployment scripts

After each application is installed, control comes back to the calling script for further user input - this step could be removed for full automation

The folder structure is as follows:
D:\BizTalk is the source folder

All applications and their artifacts are stored in an application folder within the source folder
so we have:
D:\BizTalk\MyApps.BizTalk.CommonArtifacts
D:\BizTalk\MyApps.BizTalk.Application2\
D:\BizTalk\MyApps.BizTalk.Application3\



Each application folder contains:
- a folder called applications which contains the msi file
- a folder called bindings which is further divided into two folders, MyCompanyDEV and MyCompanyTEST which contain the xml bindings configuration files for the dev and test domains respectively
- a folder called assemblies which contains required assemblies
- a folder called scripts which contains required scripts 





Since we want to install the CommonArtifacts application first, we run install.bat from this application folder (D:\BizTalk\MyApps.BizTalk.CommonArtifacts\Install.bat). This will execute all the required deployment scripts and install all three applications one by one


FILE 3
Install.bat:
checks the powershell version
sets the executionpolicy
calls the script files to install each application
Install.bat

:: Copied from launch.bat for AutoSpInstaller
@ECHO OFF
SETLOCAL
@TITLE -- Install BizTalk Solution (s)

COLOR 02
FOR /F "tokens=2-4 delims=/ " %%i IN ('date /t') DO SET SHORTDATE=%%i-%%j-%%k
FOR /F "tokens=1-3 delims=: " %%i IN ('time /t') DO SET SHORTTIME=%%i-%%j%%k

SET LaunchedFromBAT=1
:START
:: Check for Powershell v2.0
ECHO - Checking for Powershell 2.0...
"%SYSTEMROOT%\sysWOW64\windowspowershell\v1.0\powershell.exe" $host.Version.Major | find "2" >nul
IF ERRORLEVEL 1 (
 COLOR 0C
 ECHO - This script requires PowerShell version 2.0!
 ECHO - Please uninstall v1.0, install v2.0, then re-run this script.
 COLOR
 pause
 EXIT
 )

ECHO - OK

:: Get existing Powershell ExecutionPolicy
Echo Check Powershell Execution Policy
FOR /F "tokens=*" %%x in ('"%SYSTEMROOT%\sysWOW64\windowspowershell\v1.0\powershell.exe" Get-ExecutionPolicy') do (set ExecutionPolicy=%%x)


:: Set Bypass, in case we are running over a net share or UNC
IF NOT "%ExecutionPolicy%"=="Bypass" (
 ECHO - PS ExecutionPolicy is %ExecutionPolicy%, setting ExecutionPolicy to Bypass.
 "%SYSTEMROOT%\sysWOW64\windowspowershell\v1.0\powershell.exe" Set-ExecutionPolicy Bypass
 )
Echo.
Echo.
pause

GOTO LAUNCHSCRIPT1
::Launch myapps.biztalk.commonartifacts

:LAUNCHSCRIPT1
Echo Calling \Scripts\Release.ps1
"%SYSTEMROOT%\sysWOW64\windowspowershell\v1.0\powershell.exe" -command "& '%~dp0Scripts\Release.ps1' '%LaunchedFromBAT%'"
GOTO END1

:END1
Echo Review the output and then press a key to move on to the next application
Echo MyApps.BizTalk.Application2
pause

GOTO LAUNCHSCRIPT2

::Launch MyApps.BizTalk.Application2
:LAUNCHSCRIPT2
Echo.
Echo.
Echo Calling Myapps.BizTalk.Application2\Scripts\Release.ps1
"%SYSTEMROOT%\sysWOW64\windowspowershell\v1.0\powershell.exe" -command "& '..\MyApps.BizTalk.Application2\Scripts\Release.ps1' '%LaunchedFromBAT%'"
GOTO END2


:END2
Echo Review the output and then press a key to move on to the next application
Echo MyApps.BizTalk.Application3

pause
GOTO LAUNCHSCRIPT3

::Launch MyApps.BizTalk.Application3
:LAUNCHSCRIPT3
Echo.
Echo.
Echo Calling MyApps.BizTalk.Application3\Scripts\Release.ps1
"%SYSTEMROOT%\sysWOW64\windowspowershell\v1.0\powershell.exe" -command "& '..\MyApps.BizTalk.Application3\Scripts\Release.ps1' '%LaunchedFromBAT%'"
GOTO END


:END
Echo All Done. Review the output and then press a key to end
pause
ENDLOCAL



FILE 4
functions.ps1: -  
checks that the environment settings file exists and creates one if it does not exist
declares the functions to be used from the ExplorerOM dll
loads the BizTalk.Powershell.Extensions
Functions.ps1

# Functions for BizTalk (some taken from the ESB Toolkit sample installation scripts)
# Get Server settings

if ($env:USERDOMAIN -eq "MyCompanyDEV")
    {
    $inputFileRelativePath = "
\\SharedComputerdev\Bank\BizTalkServerSettings\"
    }
if ($env:USERDOMAIN -eq "MyCompanyTEST")
    {
     $inputFileRelativePath = "
\\SharedComputertest\Bank\BizTalkServerSettings\"    }
    }


# Colour for sections headers
$sectHeadColour = "Gray"
$sectHeadColourALLGo = "Black"
$sectBackColourAllGo = "Green"
$sectBackColourIsOK = "Yellow"
$sectBackColourToDo = "Yellow"

Write-Host "Color Coding"
Write-Host "Good" -ForegroundColor $sectHeadColourALLGo -BackgroundColor $sectBackColourAllGo
Write-host "Ignore" -ForegroundColor $sectHeadColourALLGo -BackgroundColor $sectBackColourIsOK
Write-Host "Bad" -BackgroundColor Red -ForegroundColor White

Write-Host "`nChecking settings for $env:COMPUTERNAME" -ForegroundColor $sectHeadColour


#check for environment settings file
$inputFile = $inputFilerelativePath + "SetInputs-" + $env:COMPUTERNAME + ".xml"
if (test-path -path $inputFile)
    {
        Write-Host $inputFile  "found" -Separator " "
        Write-Host "`nExiting settings check process" -ForegroundColor $sectHeadColourALLGo -BackgroundColor $sectBackColourAllGo
    }
Else
    {
        Write-Host $inputFile  "does not exist" -Separator " "  -ForegroundColor $sectHeadColourALLGo -BackgroundColor $sectBackColourAllGo
  Write-Host "Computer name is $env:COMPUTERNAME" -ForegroundColor $sectHeadColour
  Write-Host "`nCreating environment settings file for $env:COMPUTERNAME in $inputFileRelativePath" -ForegroundColor $sectHeadColour
     Copy-Item $inputFileRelativePath\SetInputs-$sampleinput.xml $inputFileRelativePath\SetInputs-$env:COMPUTERNAME.xml
       if (test-path -path $inputFile)
       {
       Write-Host "`nSettings file successfully created" -ForegroundColor $sectHeadColourALLGo -BackgroundColor $sectBackColourAllGo
       }
    }
 

#Read xml nodes from environments settings file
$xmlinput = [xml] (get-content $inputFile)   
   
# Get the top node

$item = $xmlinput.BTS2010Config

# Read the parameters we want 
$BTSInstallPath = $item.BTS2010InstallationPath
$ESBToolkitPath = $item.ESBToolkitInstallationPath
$btsConnectionString = $item.BTSConnectionString
$btsDBServerName = $item.BTSDBServerName
$btsDBName = $item.BTSDBName


if ($BTSInstallPath.EndsWith("\"))
{
 $BTSInstallPath = $BTSInstallPath.Substring(0, $BTSInstallPath.Length - 1)
}

if ($ESBToolkitPath.EndsWith("\"))
{
 $ESBToolkitPath = $ESBToolkitPath.Substring(0, $ESBToolkitPath.Length - 1)
}

$RulesDeployerPath = $ESBToolkitPath + "\bin\Microsoft.Practices.ESB.RulesDeployer.exe"
$ESBImportUtilPath = $ESBToolkitPath + "\bin\EsbImportUtil.exe"


# Load the commands for the http://psbiztalk.codeplex.com/ add in
Write-Host "`nLoading the Powershell Biztalk Extensions" -ForegroundColor $sectHeadColour
$snapin = Get-PSSnapin | Where-Object {$_.Name -eq 'BizTalkFactory.PowerShell.Extensions'}
if ($snapin -eq $null)
    {
        $InitializeDefaultBTSDrive = $false
        Add-PSSnapin –Name BizTalkFactory.PowerShell.Extensions
  New-PSDrive -Name BizTalk -PSProvider BizTalk -ROOT BizTalk:\ -Instance $btsDBServerName -Database $btsDBName
    }

Function Biztalk: { Set-Location Biztalk: }
Function Biztalk:\ { Set-Location Biztalk:\ }

Write-Host "Finished Loading the Powershell Biztalk Extensions" -ForegroundColor $sectHeadColourALLGo -BackgroundColor $sectBackColourAllGo

Write-Host "`nVariables" -ForegroundColor $sectHeadColour
if ($BTSInstallPath -eq $null)
    {
        Write-Host "Cannot find BizTalk Install Path, check server and correct settings" -BackgroundColor Red -ForegroundColor White
        Return
    }
    Else
    {
        Write-Host "BizTalk Install Path" $BTSInstallPath -Separator `t
    }

if ($ESBToolkitPath -eq $null)
    {
        Write-Host "Cannot find ESB Tookit Install Path, check server and correct settings" -BackgroundColor Red -ForegroundColor White
        Return
    }
    Else
    {
        Write-Host "ESB Toolkit Install Path" $ESBToolkitPath -Separator `t
    }

if ($RulesDeployerPath -eq $null)
    {
        Write-Host "Cannot find Rules Deployer Path, check server and correct settings" -BackgroundColor Red -ForegroundColor White
        Return
    }
    Else
    {
        Write-Host "Rules Deployer Path" $RulesDeployerPath
    }
   
if (test-path -path $RulesDeployerPath)
    {
        Write-Host $RulesDeployerPath  "found" -Separator `t -ForegroundColor $sectHeadColourALLGo -BackgroundColor $sectBackColourAllGo
    }
Else
    {
        Write-Host $RulesDeployerPath  "does not exist" -Separator " "  -BackgroundColor Red -ForegroundColor White
        Return
    }  

if ($btsConnectionString -eq $null)
    {
        Write-Host "Cannot find BTS connection string, check server and correct settings" -BackgroundColor Red -ForegroundColor White
        Return
    }
Else
    {
        Write-Host "BizTalk connection string" $btsConnectionString -Separator `t -ForegroundColor $sectHeadColourALLGo -BackgroundColor $sectBackColourAllGo
    }
 

function CreateBTSApplication
{
 param([string]$appName, [string]$logFile)
   
    Write-Host "`nCreating Application $appName" -ForegroundColor $sectHeadColour
   
    [System.Reflection.Assembly]::LoadFrom($BTSInstallPath +"\Developer Tools\Microsoft.BizTalk.ExplorerOM.dll") | Out-Null

 $exp = New-Object Microsoft.BizTalk.ExplorerOM.BtsCatalogExplorer
    $exp.Applications
   
    Write-Host $exp
 $exp.ConnectionString = $btsConnectionString
 $app = $exp.Applications[$appName]

 if($app -eq $null)
 {
  Write-Output "Creating BizTalk Application "$appName 
  $returnCode = (BTSTask.exe AddApp /A:$appName)

  if ($LASTEXITCODE -eq 0)
  {
   Write-Host "$appname application created sucessfully" -ForegroundColor $sectHeadColourALLGo -BackgroundColor $sectBackColourAllGo
  }
  Else
  {
   Write-Host "Create application $appname failed - review log file at $logFile" -BackgroundColor Red -ForegroundColor White
   $returnCode | Out-File $logFile
  }
 }
 else
 {
  Write-Host "Application " $appName " exists"
 }
}


function RemoveBTSApplication
{
 param([string]$appName, [string]$logfile)

 Write-Host "`nRemoving BTS Application $appName" -ForegroundColor $sectHeadColour
 $returnCode =(BTSTask.exe RemoveApp /A:$appName)

 if ($LASTEXITCODE -eq 0)
 {
  Write-Host "$appname application removed sucessfully" -ForegroundColor $sectHeadColourALLGo -BackgroundColor $sectBackColourAllGo
 }
 Else
 {
  Write-Host "Failure removing application $appname - review log file at $logFile" -ForegroundColor $sectHeadColourALLGo -BackgroundColor $sectBackColourIsOK
  $returnCode | Out-File $logFile
 }

}

function ImportBTSApplication
{
    param([string]$appName, [string]$msiPath, [string] $logFile)
    Write-Host "`nImporting $appName Application" -ForegroundColor $sectHeadColour
    $returnCode = BTSTask.exe ImportApp -Package:$msiPath -ApplicationName:$appname -Overwrite

 if ($LASTEXITCODE -eq 0)
 {
  Write-Host "$appname application imported sucessfully" -ForegroundColor $sectHeadColourALLGo -BackgroundColor $sectBackColourAllGo
 }
 Else
 {
  Write-Host "$appname application import failed - review log file at $logFile" -BackgroundColor Red -ForegroundColor White
  $returnCode | Out-File $logFile
 }
}


function ImportBindings
{
 param([string]$appName, [string]$bindingFilePath, [string]$logFile)
    Write-Host "`nImporting $appName Bindings" -ForegroundColor $sectHeadColour

 BTSTask.exe ImportBindings  /A:$appName /So:$bindingFilePath

 if ($LASTEXITCODE -eq 0)
 {
  Write-Host "$appname binding imported sucessfully" -ForegroundColor $sectHeadColourALLGo -BackgroundColor $sectBackColourAllGo
 }
 Else
 {
  Write-Host "$appname binding import failed - review log file at $logFile" -BackgroundColor Red -ForegroundColor White
  $returnCode | Out-File $logFile
 }
}


function InstallMSI
{
    param([string] $msiPath, [string] $logFilePath)
    Write-Host "`nInstalling $appName MSI" -ForegroundColor $sectHeadColour

    $parameters = "-qr -i " + $msiPath + " -lcwe+ " + $logFilePath
    Write-Host "Parameters:" $parameters
    $installStatement = [System.Diagnostics.Process]::Start("msiexec", $parameters)
    $installStatement.WaitForExit()
    Write-Host "Done" -ForegroundColor $sectHeadColourALLGo -BackgroundColor $sectBackColourAllGo
}


function UninstallMSI
{
    param([string] $msiPath, [string] $logFilePath)
    Write-Host "`nUninstalling $appName MSI" -ForegroundColor $sectHeadColour

    $parameters = "-qr -x " + $msiPath + " -lcwe " + $logFilePath
    Write-Host "Parameters:" $parameters
    $installStatement = [System.Diagnostics.Process]::Start("msiexec", $parameters)
    $installStatement.WaitForExit()
    Write-Host "Done" -ForegroundColor $sectHeadColourALLGo -BackgroundColor $sectBackColourAllGo


function StartBTSApplication
{
 param([string]$appName, [string]$logfile)
 cd BizTalk:\Applications
 Write-Host "`nStarting BTS Application $appName" -ForegroundColor $sectHeadColour
 Start-Application -Path $appName -StartOption StartAll

}

function StopBTSApplication
{
 param([string]$appName, [string]$logfile)
 Write-Host "`nStopping BTS Application $appName" -ForegroundColor $sectHeadColour
 cd BizTalk:\Applications

 if (test-path -path $appName)
 {
   Stop-Application -Path $appName -StopOption StopAll

     if ($LASTEXITCODE -eq 0)
  {
  Write-Host "$appname application stopped sucessfully" -ForegroundColor $sectHeadColourALLGo -BackgroundColor $sectBackColourAllGo
  }
  Else
  {
  Write-Host "Stopping $appname application failed - review log file at $logFile" -BackgroundColor Red -ForegroundColor White
  $returnCode | Out-File $logFile
  }
    }
 Else
 {
   Write-Host "$appname application does not exist - moving on..." -ForegroundColor $sectHeadColourALLGo -BackgroundColor $sectBackColourIsOK
 }
}


function RestartHostInstances
{
 param([string]$logfile)

 Write-Host "`nRestarting Host Instances......" -ForegroundColor $sectHeadColour
 cd BizTalk:\Applications

 Set-Location '\Platform Settings\Host Instances'
 Get-ChildItem | `
 Where-Object { $_.ServiceState -eq 'Running' } | `
    Restart-HostInstance

    if ($LASTEXITCODE -eq 0)
 {
  Write-Host "Restarted host instances sucessfully" -ForegroundColor $sectHeadColourALLGo -BackgroundColor $sectBackColourAllGo
 }
 Else
 {
  Write-Host "Restarting host instances failed - review log file at $logFile" -BackgroundColor Red -ForegroundColor White
  $returnCode | Out-File $logFile
 }

}



function UnGacAssemblies
{
    param([string[]]$assemblyNames, [string]$assemblyFolder, [string]$logFile)
   
    [bool]$errors = 0
    [string[]]$errorString = "GacAssemblies"
   
    Write-Host "`nRemoving assemblies from GAC" -ForegroundColor $sectHeadColour

    foreach ($assemblyName in $assemblyNames)
    {
        # Remove assembly from GAC
        Write-Host "Removing $assemblyName from GAC" -NoNewline
        $returnCode = . $scriptsPath\gacutil.exe /u $assemblyName
       
     if ($LASTEXITCODE -eq 0)
     {
      Write-Host " - Ok" -ForegroundColor $sectHeadColourALLGo -BackgroundColor $sectBackColourAllGo
     }
     Else
     {
            $errors = 1
      Write-Host " - Failed" -ForegroundColor Red
            $errorString = "$errorString", "Removing $assemblyName from GAC Failed", "$returnCode"
     }
    }

    if ($errors)
    {
     Write-Host "Failed - review log file at $logFile" -BackgroundColor Red -ForegroundColor White
     $errorString | Out-File $logFile
    }

}

function UnGacAssembly
{
    param([string]$assemblyName)
   
    # Remove assembly from GAC
    Write-Host "Removing $assemblyName from GAC" -NoNewline
    $returnCode = . $scriptsPath\gacutil.exe /u $assemblyName
}


function GacAssembly
{
    param([string]$assemblyName, [string]$assemblyFolder)
   
    # Add assembly to GAC
    Write-Host "Adding $assemblyName to GAC" -NoNewline
    $returnCode = . $scriptsPath\gacutil.exe /if $assemblyFolder\$assemblyName
}


FILE 5
Release.ps1: - create one file like this in the scripts folder of each application directory 
loads the functions from functions.ps1 for re-usability
performs the required deployment tasks for each application
Release.ps1

# Deploy BizTalk solution - Myapps.Biztalk.CommonArtifacts
# This must be called from a batch file
param
(
    [string]$launchedFromBatch = $(throw '- This script should be launched from a batch file as it relies on relative paths')
)


#Get current location of calling bat file
[string]$path = Get-Location

#Use current location as reference for correct folder structure
$applicationPath = $path + "\Applications"
$logPath = $path + "\Logs"
$scriptsPath = $path + "\Scripts"


# Call BTSFunctions file
Write-Host "Loading functions list"
. .\Scripts\Functions.ps1


# Application specific variables
$biztalkApplication = "Myapps.Biztalk.CommonArtifacts"

#Applications depending on this must be removed first
[string[]]$biztalkapplicationnames = ("Myapps.Biztalk.Application2", "Myapps.Biztalk.Application3")

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
Write-Host "`nVariables" -ForegroundColor Gray
Write-Host "Installation Path" $path -Separator `t
Write-Host "Applications Path" $applicationPath -Separator `t
Write-Host "Log Path " $logPath -Separator `t
Write-Host "Scripts Path " $scriptsPath -Separator `t
$Error.Clear()

#Create folders... you can create any required file receive location or send port folders too!Write-Host "Creating Inbound Folder... Ignore errors here"
Write-Host "Folder D:\BizTalkFileDrops\CommonArtifacts\Inbound"
if (!(Test-Path -path D:\BizTalkFileDrops\CommonArtifacts\Inbound\))
{
 New-Item D:\BizTalkFileDrops\CommonArtifacts\Inbound\ -type directory

  if (Test-Path -path D:\BizTalkFileDrops\CommonArtifacts\Inbound\)
  {
    Write-Host "`nD:\BizTalkFileDrops\CommonArtifacts\Inbound\ folder successfully created" -ForegroundColor $sectHeadColourALLGo -BackgroundColor $sectBackColourAllGo
  }
}


# Remove applications dependent on this application
Write-Host "Removing Application dependencies... ignore errors here"
If ($biztalkapplicationnames) {
ForEach ($ap in $biztalkapplicationnames)
     {
       StopBTSApplication $ap "$logPath\StopApplication_$ap.log"
       RemoveBTSApplication $ap "$logPath\RemoveApplication_$ap.log"
    }
}


#Remove this application
StopBTSApplication $biztalkApplication "$logPath\StopApplication_$biztalkApplication.log"

# Now Re-Create application
CreateBTSApplication $biztalkApplication "$logPath\CreateApplication_$biztalkApplication.log"

# Import application msi into BizTalk

ImportBTSApplication $biztalkApplication "$applicationPath\$biztalkApplication.msi" "$logPath\ImportApplication_$biztalkApplication.log"

# Import application bindings

ImportBinding "$biztalkApplication" "$bindingsPath\$biztalkApplication.BindingInfo.xml" "$logPath\Importbindings_$biztalkApplication.log"

# Install msi

InstallMSI "$applicationPath\$biztalkApplication.msi" "$logPath\$biztalkApplication.msi.install.log"

# Restart Host Instances

RestartHostInstances $logPath + "\RestartHostinstances.log"

#Start application (if necessary)
StartBTSApplication $biztalkApplication "$logPath\StartApplication_$ap.log"

..............................................................................
this script could be further modified to do a host of useful things like:
replacing the BTs config file (BTSNTSvc64.exe.config) to include app configurations
removing and adding assemblies to gac
adding pipeline components and custom functoids etc

No comments:

Post a Comment