Removing Suspended Service Instances

 

Use the following code to delete suspended service instances.

' terminate.vbs  
' Enter terminate.vbs with no arguments from a command prompt for usage  
' This script needs to be run under a user account that is a member of the BizTalk Administrators   
' group. This script needs to be run on a machine that is configured with BizTalk administration  
' tools.  
  
dim objBtsWmiNS, objMsg, svcinsts, inst, msg, ndx, size, savemessages  
  
Dim aryClassIDs()  
Dim aryTypeIDs()  
Dim aryInstanceIDs()  
Dim aryHostNames()  
Dim aryObjQueues()  
Dim aryHostBatchSize()  
  
Dim strKey2Instance  
Dim strQuery2Msg  
maxBatchSize = 200 'Terminate in batches. Max supported batch size is 2K-1 (2047)  
  
On Error Resume Next  
Dim objArgs: Set objArgs = WScript.Arguments  
If ( objArgs.Count = 0 OR objArgs.Count > 2) Then  
     PrintUsage()  
     wscript.quit 0  
End If  
  
wmiQuery = ""  
  
'ServiceStatus = 16 - 'Completed With Discarded Messages' in BizTalk Server 2004  
'ServiceStatus = 32 - 'Suspended (not resumable)'  
'ServiceStatus = 4 - 'Suspended (resumable)'  
'ServiceClass = 64 - 'Routing Failure Report'  
'ErrorId = "0xC0C01B4C" - is how 'Completed With Discarded Messages' are exposed in BizTalk Server 2009  
  
If (objArgs(0) = "-Z" OR objArgs(0) = "-z") Then  
     wmiQuery = "select * from MSBTS_serviceinstance where ServiceStatus=16"  
End If  
  
If (objArgs(0) = "-A" or objArgs(0) = "-a") Then  
     wmiQuery = "select * from MSBTS_serviceinstance where ServiceStatus=4 OR ServiceStatus=32 OR ServiceStatus=16 OR ErrorId='0xC0C01B4C' OR ServiceClass=64"  
End If  
  
If (objArgs(0) = "-SR" or objArgs(0) = "-sr") Then  
     wmiQuery = "select * from MSBTS_serviceinstance where ServiceStatus=4"  
End If  
  
If (objArgs(0) = "-SNR" or objArgs(0) = "-snr") Then  
     wmiQuery = "select * from MSBTS_serviceinstance where ServiceStatus=32"  
End If  
  
If (objArgs(0) = "-DIS" or objArgs(0) = "-dis") Then  
     wmiQuery = "select * from MSBTS_serviceinstance where ServiceClass=32 AND ServiceStatus=8"  
'ServiceClass = 32 'Isolated Adapter  
'ServiceStatus = 8 'Dehydrated  
End If  
  
saveMessagesBeforeTermination = True  
If ( objArgs.Count > 1) Then  
     If (objArgs(1) = "-NOSAVE" OR objArgs(1) = "-nosave") Then  
          saveMessagesBeforeTermination = False  
     Else  
          PrintUsage()  
          wscript.quit 0  
     End If  
End If  
  
If(wmiQuery = "") Then  
     PrintUsage()  
     wscript.quit 0  
End If  
  
wscript.echo "-->Connecting to BizTalk WMI namespace"  
Set objBtsWmiNS = GetObject("WinMgmts:{impersonationLevel=impersonate, (security)}\\.\root\MicrosoftBizTalkServer")   
If Err <> 0 Then  
     CheckWMIError  
     wscript.quit 0  
End If          
  
wscript.echo "-->Getting BizTalk host collection"  
Set hosts = objBtsWmiNS.ExecQuery("select * from MSBTS_HostSetting")  
If Err <> 0 Then  
     CheckWMIError  
     wscript.quit 0  
End If          
  
hostCount = hosts.count  
  
ReDim aryHostNames(hostCount - 1)  
ReDim aryObjQueues(hostCount - 1)  
ReDim aryHostBatchSize(hostCount - 1)  
  
wscript.echo "-->Retrieve BizTalk host names and loading host queues"  
ndx = 0  
For Each host in hosts  
     wscript.echo "Found host " & host.Properties_("Name")  
     aryHostNames(ndx) = host.Properties_("Name")  
     Set aryObjQueues(ndx) = objBtsWmiNS.Get("MSBTS_HostQueue.HostName=""" & aryHostNames(ndx) & """")  
     If Err <> 0 Then  
          CheckWMIError  
          wscript.quit 0  
     End If          
     ndx = ndx + 1  
Next  
  
wscript.echo "-->Getting collection of service instances"  
Set svcinsts = objBtsWmiNS.ExecQuery(wmiQuery)  
  
ReDim aryClassIDs(hostCount, maxBatchSize-1)  
ReDim aryTypeIDs(hostCount, maxBatchSize-1)  
ReDim aryInstanceIDs(hostCount, maxBatchSize-1)  
  
'Iterate through instances and save them in host-specific arrays.  
'Terminate instances from host-specific array when array gets to a maxBatchSize  
wscript.echo "-->Start iterating service instances"  
totalCount = 0  
saveMessages = saveMessagesBeforeTermination  
For Each inst in svcinsts  
     saveMessagesBeforeTermination = saveMessages  
     wscript.echo "Found suspended instance """ & inst.Properties_("ServiceName") & """ on host " & inst.Properties_("HostName")  
     'Resolve host index  
     For hostIdx = 0 To hostCount-1   
          If aryHostNames(hostIdx) = inst.Properties_("HostName") Then  
               Exit For  
          End If  
     Next  
  
    '16 is an internal service class that cannot be terminated  
     If 16 = inst.Properties_("ServiceClass") Then  
          wscript.echo "Skipping BizTalk internal service instances (they cannot be terminated anyway)"  
     Else  
          '64 is a routing failure report and doesn't have messages that can be saved  
          If 64 = inst.Properties_("ServiceClass") Or 16 = inst.Properties_("ServiceClass") Then  
               saveMessagesBeforeTermination = False  
          End If  
  
          errorCountSavingMessages = 0  
          If saveMessagesBeforeTermination Then  
               strQuery2Msg = "select * from MSBTS_MessageInstance where ServiceInstanceID=""" & inst.Properties_("InstanceId") & """"  
               Set msgInsts = objBtsWmiNS.ExecQuery(strQuery2Msg)  
               For Each msg in msgInsts  
                    msg.SaveToFile "C:\Temp"  
                  If Err <> 0 Then  
                         CheckWMIError  
                       wscript.echo "Failed to save MSBTS_MessageInstance"  
                       wscript.echo Err.Description & Err.Number  
                       errorCountSavingMessages = errorCountSavingMessages + 1  
                  Else  
                         wscript.echo "Saved message " & msg.Properties_("MessageInstanceID")  
                  End If          
              Next  
         End If  
  
         If 0 = errorCountSavingMessages Then 'Only terminate when we had no problems saving messages  
               aryClassIDs(hostIdx, aryHostBatchSize(hostIdx)) = inst.Properties_("ServiceClassId")  
               aryTypeIDs(hostIdx, aryHostBatchSize(hostIdx)) = inst.Properties_("ServiceTypeId")  
               aryInstanceIDs(hostIdx, aryHostBatchSize(hostIdx)) = inst.Properties_("InstanceId")  
               aryHostBatchSize(hostIdx) = aryHostBatchSize(hostIdx) + 1 'Keep track of newly added instace for that host  
          Else  
               wscript.echo "Skipping the instance since couldn't save its messages"  
          End If  
  
          totalCount = totalCount + 1  
          If(aryHostBatchSize(hostIdx) = maxBatchSize) Then  
               TerminateAccumulatedInstacesForHost hostIdx  
          End If  
     End If  
Next  
  
' Delete whatever is left  
For hostIdx = 0 To hostCount-1   
     If aryHostBatchSize(hostIdx) > 0 Then  
          TerminateAccumulatedInstacesForHost hostIdx  
     End If  
Next  
  
wscript.echo "SUCCESS> " & totalCount & " instances were found and attempted to be terminated"  
  
Sub     TerminateAccumulatedInstacesForHost(hostIdx)  
     wscript.echo "Sending termination request for host " & aryHostNames(hostIdx) & " service instances"  
     Dim aryClassIDs4Host()  
     Dim aryTypeIDs4Host()  
     Dim aryInstanceIDs4Host()  
     ReDim aryClassIDs4Host(aryHostBatchSize(hostIdx)-1)  
     ReDim aryTypeIDs4Host(aryHostBatchSize(hostIdx)-1)  
     ReDim aryInstanceIDs4Host(aryHostBatchSize(hostIdx)-1)  
  
     For i = 0 to aryHostBatchSize(hostIdx)-1  
          aryClassIDs4Host(i) = aryClassIDs(hostIdx, i)  
          aryTypeIDs4Host(i) = aryTypeIDs(hostIdx, i)  
          aryInstanceIDs4Host(i) = aryInstanceIDs(hostIdx, i)  
     Next  
     aryObjQueues(hostIdx).TerminateServiceInstancesByID aryClassIDs4Host, aryTypeIDs4Host, aryInstanceIDs4Host  
     CheckWMIError  
     aryHostBatchSize(hostIdx) = 0  
End Sub  
  
'This subroutine deals with all errors using the WbemScripting object.    
'Error descriptions are returned to the user by printing to the console.  
Sub CheckWMIError()  
  
     If Err <> 0 Then  
          On Error Resume Next  
          Dim strErrDesc: strErrDesc = Err.Description  
          Dim ErrNum: ErrNum = Err.Number  
          Dim WMIError : Set WMIError = CreateObject("WbemScripting.SwbemLastError")  
  
          If (TypeName(WMIError) = "Empty" ) Then  
               wscript.echo strErrDesc & " (HRESULT: " & Hex(ErrNum) & ")."  
          Else  
               wscript.echo WMIError.Description & "(HRESULT: " & Hex(ErrNum) & ")."  
               Set WMIError = nothing  
          End If  
  
          'wscript.quit 0  
     End If  
  
End Sub   
  
Sub PrintUsage()  
     wscript.echo "Usage:"  
     wscript.echo "cscript Terminate.vbs < -Z | -A | -DIS | -SR | -SNR > [-nosave]"  
     wscript.echo  
     wscript.echo "  -Z terminates all ""Zombie"" instances (e.g. completed with discarded messages)"  
     wscript.echo "  -A terminates all suspended and zombie instances as well as all routing failure reports"  
     wscript.echo "  -SR terminates suspended resumable instances only"  
     wscript.echo "  -SNR terminates suspended non-resumable instances only"  
     wscript.echo "  -DIS terminates all dehydrated 'isolated adapter' instances"  
     wscript.echo "  -nosave terminates instances without saving messages they reference"  
     wscript.echo "  Default action is to save instances to the C:\Temp folder on the local computer"  
     wscript.echo  
     wscript.echo "  Ensure that the C:\Temp folder exists before running terminate if you want to save instances"  
     wscript.echo  
     wscript.echo "  Example: cscript Terminate.vbs -z -nosave"  
     wscript.echo  
End Sub