by Ron Phillips, aka El Bo on Technet
If you've ever gotten a phone call at 2 am asking you to figure out why a system is down, you'll appreciate the utility of self-healing systems. With a little work with WMI we can apply this concept to BizTalk.
This article covers how to do this for BizTalk host instances, but the same techniques can be applied to receive locations and send ports.
Why do host instances go down in the first place? BizTalk sits atop SQL Server, and it's very clingy: if it loses connectivity for even a very brief interval, host instances will shut down. Sometimes after a reboot host instances fail to come up. Even
an interruption that doesn't involve all of SQL, such as the Enterprise Single Sign-On service going offline, can bring down host instances.
And don't underestimate your fat-fingered co-workers. I once saw a week's worth of testing go down the drain because a system administrator saw this one service on his QA environment taking up a ton of CPU, so he disabled it. That service was a host instance,
and so even though he wasn't a BizTalk admin he was able to halt the instance.
The key tricks at work here are all via WMI. The complete script is at the end of the article, but let's walk through the fun parts. All script here is good ol' fashioned vbScript, but it works the same via PowerShell, a .NET console program, or anything
else that lets you access WMI.
What we're going to develop is a script that can run as a scheduled Task in Windows. When it runs, it will look at our host instances, enable ones that are disabled, and email us if it can't get the instance up and running (as well as write to the Windows
application log). For clustered environments, where the host instance on the non-active node can't be started, the script will detect this and skip that instance.
The script can be scheduled to run frequently - say, every 10 minutes - and insure that we keep our processes running.
To start, you need to be able to query the BizTalk management database. WMI gives us an easy hook for this:
objWMIService = GetObject(
Now you can look around in the management database with SQL queries - well, really simple ones. Finding out what will and won't work requires some trial and error. For example, our final script lets you provide a list of host names to check (rather than
all of the hosts on the machine - there may be some where you want to sometimes take a host instance offline temporarily). So you would think we'd query the database for a specific host name.
But that WHERE clause on the query just doesn't work, so we grab all of them and iterate through the list:
colHostInstances = objWMIService.ExecQuery(
"Select * from MSBTS_HostInstance Where HostType=1 And IsDisabled=False "
That gives us a collection of host instances. In our script, we iterate through that to find a particular one.
If you're new to WMI, prepare to fall in love: you get a rich set of objects that you manipulate via straightforward properties and methods. In this case, once we've found the particular host we're interested in, we check whether it is stopped by looking
at the property ServiceState. A value of 1 indicates it has stopped, but as is good programming practice we define a constant for this so the code ends up being easy to read:
if Ucase(HostName)=Ucase(objHostInstance.HostName )
objHostInstance.ServiceState = HostInstServiceState_Stopped then
Next up we want to see if we're dealing with a clustered host, and if so, is the instance we're looking at on the active node or not. If you try to start a host instance on a non-active node in a cluster, it will fail, and this would cause our script to
email out a false alert.
We check for whether the host is clustered and then compare whether the server the instance is on is the active node of the cluster:
(objHostInstance.ClusterInstanceType = HostIsClustered) then
' is it on the active node?
(Ucase(objHostInstance.RunningServer) = Ucase(strActiveClusterNode)) then
iStartThis = 1
And for our final magic trick, let's look at that function we popped in there, GetActiveClusterNode(). It's a pretty simple vbScript function that performs a little more WMI magic to get the active cluster node. We start by getting the name of the computer
we're running on:
wshShell = WScript.CreateObject(
strComputerName = wshShell.ExpandEnvironmentStrings(
We use this to get information about the cluster the machine is part of:
"winmgmts:\\" & strComputer & "
colItems = objWMIService.ExecQuery(
"SELECT * FROM MSCluster_NodeToActiveGroup"
wbemFlagReturnImmediately + wbemFlagForwardOnly)
And then we iterate the list to find the active node (using a little knowledge of how the data from that query is formatted - it's not really something that can be explained better than just looking at the code).
The whole script is below. It looks for a file named HostInstancesList.txt to read from (one host name per line), but you can substitute WScript.Arguments.Item(0) if you'd prefer to pass it as a command line parameter.
To run it from the Windows Task scheduler, set the task action to: cscript startuphosts.vbs Remember to set the "Startup in" folder to the folder containing the script, otherwise the script won't find the file containing a list of hosts to check. You'll
also need to put in values for the variables strSMTPServer and strDestinationAddress to enable email notifications. For these types of things, I always recommend setting up an email distribution list, so all of your admin scripts can use the same hard-coded
address and you use the distrib list to manage who gets the notifications.
' StartUpHosts.vbs Start up host instances
' by Ron Phillips
' Original: 8/31/2012
' Usage: Cscript StartUpHosts.vbs
' Configuration: Looks for file HostInstancesList.txt with one host instance per line
CONST ForReading = 1
' Host Instance status number
CONST HostInstServiceState_Stopped = 1
' is the host clustered?
CONST HostIsClustered = 1
' Windows App Log status codes
CONST Log_Code_Error = 1
CONST Log_Code_Success = 0
CONST Log_Code_Warning = 2
' This next item is to let the email include a note as to which environment its from,
' so typical values might be "QA" "DEV" "UAT" etc.
oFS = CreateObject(
' read in the list
' now we're going to do this in an inefficient way - for each record in the list,
' we query the management database and iterate through the whole list
' we could keep the collections around and just query once
' but this is a read-only operation and very low resource impact, so it's not a big deal
for each sRow in arrReplayList
if len(sRow)>0 then
' pause a second between each one
"Finished starting up host instances"
' Support functions, if yo are adding/modifying please keep these generic/re-usable.
ocheckFS = CreateObject(
WScript.Echo thefile +
" is missing"
' get a WMI object to hook in to the management database
' query BizTalk host instances that are of type In-Process (within BizTalk Server installation)
' If any host instance is found check name
(colHostInstances.Count > 0)
"Checking status of "
objHostInstance in colHostInstances
' check the name
' To list the host name (space) server name :WScript.Echo("On " + objHostInstance.Name)
if Ucase(HostName)=Ucase(objHostInstance.HostName ) then
iFoundName = 1
' is it clustered?
if (objHostInstance.ClusterInstanceType = HostIsClustered) then
if (Ucase(objHostInstance.RunningServer) = Ucase(strActiveClusterNode)) then
if iStartThis=1 then
" Starting "
& objHostInstance.Name &
"BizTalk StartUpHosts.vbs script Attempting to start Host Instance "
"Cannot find sny enabled hosts instance for host "
+ HostName +
". Check to see if host instance is disabled or name is spelled wrong."
"BizTalk StartupHosts.vbs script error: Unable to find enabled host instance "
"Unable to find host instance "
+ HostName )
"Cannot find sny enabled hosts on this system"
"BizTalk StartupHosts.vbs script error: Unable to find any enabled host instances on this system"
"Unable to find any enabled host instances on this system "
computername = Trim(strcomputername)
count = count + 1
wbemFlagReturnImmediately = &h10
wbemFlagForwardOnly = &h20
arrComputers = Array(
strnodename = right(objItem.GroupComponent,count)
strnodename1 = left(strnodename,count1)
if len(strnodename1)>0 then
'WScript.Echo "Active Cluster node is " & strnodename1
"Active Cluster node is "
if Err <> 0
strErrDesc: strErrDesc = Err.Description
ErrNum: ErrNum = Err.Number
WMIError = CreateObject(
if ( TypeName(WMIError) =
FinalMessage strErrDesc &
" (HRESULT: "
& Hex(ErrNum) &
FinalMessage= WMIError.Description &
& Hex(ErrNum) &
WMIError = nothing
"Error starting "
& strGlobalHost &
' if you want to quit on err: wscript.quit 0
objShell = Wscript.CreateObject(
' This subroutine sends an email out for the error. email address and smtp server are hard coded
' set an email address to send errors to. This could also be taken from a command line parameter
objEmail = CreateObject(
objEmail.From = strDestinationAddress
+ What_Environment +
": Unable to start host instance"
objEmail.Textbody = strMessage
Another important place to find an extensive amount of BizTalk related articles is the TechNet Wiki itself. The best entry point is
BizTalk Server Resources on the TechNet Wiki.