Powershell script: Stop and Start BizTalk Services

Script to stop or start IIS and Biztalk services on local computer
Must be run with elevated rigths to stop services

#   ------------------------------------------------------------------
#   Author: Peter Lykkegaard
#   Created 9 sep 2017
#   Filename: SwitchHostsInstanceState.ps1
#   
#   Changelog
#   ------------------------------------------------------------------
#   Author              Date            Changes
#   Peter Lykkegaard    14 sep 2017     Added stop/start/status of IIS  
#
#   References
#   http://ittybizzy.blogspot.dk/2013/10/powershell-scripts-to-stopstart-biztalk.html
#   https://social.technet.microsoft.com/wiki/contents/articles/32946.biztalk-server-health-check-powershell-script.aspx
#
#   ------------------------------------------------------------------
param(
    [parameter(ValueFromPipeline)]
    [ValidateNotNullOrEmpty()]
    [string]$handle
    )

# Main routine
function Switch-HostInstanceState
{
  
    Clear-Host 
    
    if (Test-Arguments)
    {
        switch ($handle)
        {
            {$_ -in "Stop", "Start", "Status"} 
            {
                # Disable or Enable all host instances
                # If already disabled receive* hosts will not be enabled
                Invoke-ServiceState $handle

                if ($handle -ne 'Status')
                {
                    # Write current status
                    Invoke-ServiceState "Status"
                }
                break
            }
            default { 
                Show-HelpArguments 
                break
            }            
        }        

    }
}

function Test-Arguments
{
    Write-Host "`n**********************************************************"
    Write-Host ("Handle = '{0}'" -f $handle)

    if (
        $handle.length -eq 0
        ) 
    {
        Write-Host "`nOne or more arguments are missing`n"
        Show-HelpArguments
        return $False
    }
    else
    {
        return $True
    }
    
}

function Show-HelpArguments
{
    Write-Host "**********************************************************"
    Write-Host "Use the SwitchHostsInstanceState.ps1 to `n"
    Write-Host " Needed parameters:"
    Write-Host "   0 : -Handle`tStop|Start|Status"
    Write-Host "`n"
    Write-Host "Usages:  SwitchHostsInstanceState.ps1 -Handle [Switch]"
    Write-Host "Sample:  SwitchHostsInstanceState.ps1 -Handle ""Start"""
    Write-Host "`n**********************************************************`n`n"
    
}

Function Invoke-ServiceState([string] $handle)
{
    # Get all In-process BizTalk hostinstances (HostType=1)
    $inProcessHosts = "HostType=1"          # Exclude IsolatedHosts which does have a corresponding service
    $nsBTS = "root/MicrosoftBizTalkServer"  # Biztalk Namesspace in WMI
    $receiving = "Receiving*"               # All Receiving Hostinstances 

    $filter = "($inProcessHosts)"           # Filter for WMI lookup
    $hostInstances = Get-WmiObject MSBTS_HostInstance -Namespace $nsBTS -Filter $filter 
    $instances = $hostInstances 
    
    switch ($handle)
    {
        "Stop"
        {
            # Disable or Enable all host instances
            # If already disabled receive* hosts will not be enabled
            # Gracefully stop IIS services
            $iis =  Get-WmiObject Win32_Service -Filter "Name = 'W3SVC'"
            if ($iis.State -eq 'Running')
            {
                Write-Host "Stopping IIS Services /noforce..."
                & {iisreset /stop /noforce}
            }

            # Handle all selected host instances
            Write-Host "`nDisable and Stop BizTalk Host Instances "
            Write-Host "**********************************************************"
            break
        }
        "Start"
        {
            Write-Host "`nEnable and Start BizTalk Host Instances "
            Write-Host "**********************************************************"
            # Check for disabled hosts other than receiving, if any found loop through the collection
            $instances = $hostInstances | Where-Object {$_.hostname -notlike $receiving -and $_.IsDisabled -eq $True}
            if (!$instances)
            {
                $instances = $hostInstances
            }
            break
        }
        "Status"
        {
            # Handle all selected host instances
            Write-Host "`nStatus of IIS "
            Write-Host "**********************************************************"
            $iis =  Get-WmiObject Win32_Service -Filter "Name = 'W3SVC'"
            Write-Host ("Web server - {0}" -f $iis.State)

            Write-Host "`nStatus of BizTalk Host Instances "
            Write-Host "**********************************************************"
            break
        }

        default { 
            # Handle undefined or wrong value
            Show-HelpArguments 
            break
        }            
    }

    foreach ($hostInstance in $instances)
    {
        switch ($handle)
        {                           
            "Stop" {    # Disable all but stop only receiving hostinstances

                if (!$hostInstance.IsDisabled)
                {
                    $hostInstance.IsDisabled = $true
                    $hostInstance.Put() > $null

                    Write-Host $hostInstance.hostname 'is disabled'
                }
                if ($hostInstance.hostname -like $receiving)
                {
                    # if service is running
                    if ($hostInstance.servicestate -eq 4)
                    {
                        Write-Host ('Stopping service: {0} ...' -f $hostInstance.hostname)

                        try {
                            $windowsService = ('BTSSvc${0}' -f $hostInstance.HostName)
                            Get-Service -Name $windowsService | Stop-Service
                            #Stop-Service -DisplayName $windowsService
                        }
                        catch {
                            Write-Host ('Error stopping host {0}, please check eventlog for details' -f $hostInstance.hostname), -ForegroundColor "red"
                        } 
                        if ($hostInstance.servicestate -ne 4)
                        {
                            Write-Host ('{0} is stopped' -f $hostInstance.hostname)
                        }
                        
                    }
                }
                break
            }
            "Start" {   # Ensable all but do not start receiving hostinstances
                
                if ($hostInstance.IsDisabled)
                {
                    $hostInstance.IsDisabled = $False
                    $hostInstance.Put() > $null               
                    Write-Host $hostInstance.hostname 'is reenabled'
                }
                if ($hostInstance.hostname -notlike $receiving)
                {
                    # if service is not running try to start it
                    if ($hostInstance.servicestate -ne 4)
                    {
                        Write-Host ('Starting service: {0} ...' -f $hostInstance.hostname)
                        try {
                            $windowsService = ('BTSSvc${0}' -f $hostInstance.HostName)
                            Get-Service -Name $windowsService | Start-Service
                            #Start-Service -DisplayName $hostInstance.HostName
                        }
                        catch {
                            Write-Host ('Error starting host {0}, please check eventlog for details' -f $hostInstance.hostname), -ForegroundColor "red"
                        } 
                        # Notify is started
                        if ($hostInstance.servicestate -eq 4)
                        {
                            Write-Host ('{0} is started' -f $hostInstance.hostname)
                        }
                    }
            }
                break
            }
            "Status" {  # Status for BizTalk Services 
                switch ($hostInstance.servicestate) {
                    1 { $hostInstanceState = "Stopped" }
                    2 { $hostInstanceState = "Start pending" }
                    3 { $hostInstanceState = "Stop pending" }
                    4 { $hostInstanceState = "Running" }
                    5 { $hostInstanceState = "Continue pending" }
                    6 { $hostInstanceState = "Pause pending" }
                    7 { $hostInstanceState = "Paused" }
                    8 { $hostInstanceState = "Unknown" }
                }

                Write-Host $hostInstance.hostname '-' $hostInstanceState
                break
            }
        }
    }

    if ($handle -eq 'Start')
    {
        $iis =  Get-WmiObject Win32_Service -Filter "Name = 'W3SVC'"
        if ($iis.State -ne 'Running')
        {
            # Start IIS services
            Write-Host "`nAttempting to start IIS Services ..."
            & {iisreset /start }        
        }
    }
}

# Entry point
Switch-HostInstanceState

 

BizTalk Benchmark Wizard

Quick C# console to install host instances/prerequisites for BizTalk Benchmark Wizard

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Management;

namespace ConCreateAndMapBizTalkHost
{
    class Program
    {
        private const string ServerName = "";
        private const string NTGroupName = "Biztalk Application Users";
        private const string UserName = "bts_user";
        private const string Password = "Pa$$w0rd";
        private const string ReceiveHost = "Receiving";
        private const string SendHost = "Sending";
        private const string ProcessingHost = "Processing";

        static void Main(string[] args)
        {
            CreateHost("BBW_RxHost", 1, NTGroupName, false, false);
            MapInstallHostInstance("BBW_RxHost", ServerName, UserName, Password);
            CreateReceiveHandler("BBW_RxHost", "WCF-NetTcp", "MSBTS_ReceiveHandler");

            CreateHost("BBW_TxHost", 1, NTGroupName, false, false);
            MapInstallHostInstance("BBW_TxHost", ServerName, UserName, Password);
            CreateSendHandler("BBW_TxHost", "WCF-NetTcp", "MSBTS_SendHandler2");

            CreateHost("BBW_PxHost", 1, NTGroupName, false, true);
            MapInstallHostInstance("BBW_PxHost", ServerName, UserName, Password);

            System.Console.ReadKey();
        }


        //Basic WMI opertaion - Create  
        //Sample to show MSBTS_HostSetting instance creation  
        public static void CreateHost(string hostName, int hostType, string ntGroupName, bool authTrusted, bool hostTracking)
        {
            try
            {
                var options = new PutOptions
                {
                    Type = PutType.CreateOnly
                };

                //create a ManagementClass object and spawn a ManagementObject instance  
                ManagementClass objHostSettingClass = new ManagementClass("root\\MicrosoftBizTalkServer", "MSBTS_HostSetting", null);
                ManagementObject objHostSetting = objHostSettingClass.CreateInstance();

                //set the properties for the Managementobject  
                objHostSetting["Name"] = hostName;
                objHostSetting["HostType"] = hostType;
                objHostSetting["NTGroupName"] = ntGroupName;
                objHostSetting["AuthTrusted"] = authTrusted;
                objHostSetting["IsHost32BitOnly"] = false;
                objHostSetting["HostTracking"] = hostTracking;

                //create the Managementobject  
                objHostSetting.Put(options);
                System.Console.WriteLine("Host - " + hostName + " - has been created successfully");
            }
            catch (Exception excep)
            {
                System.Console.WriteLine("CreateHost - " + hostName + " - failed: " + excep.Message);
            }
        }

        public static void MapInstallHostInstance(string hostName, string svrName, string uid, string pwd)
        {

            ManagementClass svrHostClass = null;
            ManagementObject svrHostObject = null;

            ManagementClass hostInstClass = null;
            ManagementObject hostInstObject = null;

            try
            {
                //Build the name of the HostInstance - name has to be in the below format  
                string hostInstanceName = "Microsoft BizTalk Server" //Name of product  
                                          + " " + hostName //Name of Host of which instance is to be created  
                                          + " " + svrName; //Name of Server on which instance is to be created  

                //Create an instance of the ServerHost class using the System.Management namespace  
                ObjectGetOptions svrHostOptions = new ObjectGetOptions();
                svrHostClass = new ManagementClass("root\\MicrosoftBizTalkServer", "MSBTS_ServerHost", svrHostOptions);
                svrHostObject = svrHostClass.CreateInstance();

                //Set the properties of the ServerHost instance  
                svrHostObject["ServerName"] = svrName;
                svrHostObject["HostName"] = hostName;

                //Invoke the Map method of the ServerHost instance  
                svrHostObject.InvokeMethod("Map", null);

                //Create an instance of the HostInstance class using the System.Management namespace  
                ObjectGetOptions hostInstOptions = new ObjectGetOptions();
                hostInstClass = new ManagementClass("root\\MicrosoftBizTalkServer", "MSBTS_HostInstance", hostInstOptions);
                hostInstObject = hostInstClass.CreateInstance();


                //Set the properties of the HostInstance class  
                hostInstObject["Name"] = hostInstanceName;

                //Build a parameter array  
                object[] args = new object[2];
                args[0] = uid;
                args[1] = pwd;

                //Invoke the Install method of the HostInstance  
                hostInstObject.InvokeMethod("Install", args);

                Console.WriteLine("HostInstance was mapped and installed successfully. Mapping created between Host: " +
                                  hostName + " and Server: " + svrName);
                return;
            }
            catch (Exception excep)
            {
                Console.WriteLine("Failure during HostInstance creation: " + excep.Message);
            }
            finally
            {
                if (hostInstObject != null) hostInstObject.Dispose();
                if (hostInstClass != null) hostInstClass.Dispose();

                if (svrHostObject != null) svrHostObject.Dispose();
                if (svrHostClass != null) svrHostClass.Dispose();
            }

        }

        //Sample to show MSBTS_ReceiveHandler instance creation with CustomCfg property  
        public static void CreateReceiveHandler(string hostName, string adapterName, string handlerType)
        {
            try
            {
                var options = new PutOptions
                {
                    Type = PutType.CreateOnly
                };

                //create a ManagementClass object and spawn a ManagementObject instance  
                var objReceiveHandlerClass = new ManagementClass("root\\MicrosoftBizTalkServer", "MSBTS_ReceiveHandler", null);
                var objReceiveHandler = objReceiveHandlerClass.CreateInstance();

                //set the properties for the Managementobject  
                objReceiveHandler["AdapterName"] = adapterName;
                objReceiveHandler["HostName"] = hostName;
                // objReceiveHandler["CustomCfg"] = "<CustomProps><AdapterConfig vt=\"8\"><Config xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"><userName>Username</userName><password>Password</password><accountName>FTP Account</accountName><representationType>binary</representationType><spoolingFolder>c:\\Temp</spoolingFolder><maximumBatchSize>10</maximumBatchSize><maximumNumberOfFiles>10</maximumNumberOfFiles><passiveMode>False</passiveMode><beforeGet>BeforeGet</beforeGet><afterGet>AfterGet</afterGet><firewallType>NoFirewall</firewallType><firewallAddress>FireWallAddress</firewallAddress><firewallPort>21</firewallPort><firewallUserName>Username</firewallUserName><firewallPassword>Password</firewallPassword><commandLogFilename>c:\\Log</commandLogFilename><errorThreshold>10</errorThreshold><maxFileSize>100</maxFileSize></Config></AdapterConfig></CustomProps>";

                //create the Managementobject  
                objReceiveHandler.Put(options);
                System.Console.WriteLine("ReceiveHandler - " + adapterName + " " + hostName + " - has been created successfully");
            }
            catch (Exception excep)
            {
                System.Console.WriteLine("CreateReceiveHandler - " + adapterName + " " + hostName + " - failed: " + excep.Message);
            }
        }

        public static void CreateSendHandler(string hostName, string adapterName, string handlerType)
        {
            try
            {
                var options = new PutOptions
                {
                    Type = PutType.CreateOnly
                };

                //create a ManagementClass object and spawn a ManagementObject instance  
                var objSendHandlerClass = new ManagementClass("root\\MicrosoftBizTalkServer", handlerType, null);
                var objSendHandler = objSendHandlerClass.CreateInstance();

                //set the properties for the Managementobject  
                objSendHandler["AdapterName"] = adapterName;
                objSendHandler["HostName"] = hostName;
                // objReceiveHandler["CustomCfg"] = "<CustomProps><AdapterConfig vt=\"8\"><Config xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"><userName>Username</userName><password>Password</password><accountName>FTP Account</accountName><representationType>binary</representationType><spoolingFolder>c:\\Temp</spoolingFolder><maximumBatchSize>10</maximumBatchSize><maximumNumberOfFiles>10</maximumNumberOfFiles><passiveMode>False</passiveMode><beforeGet>BeforeGet</beforeGet><afterGet>AfterGet</afterGet><firewallType>NoFirewall</firewallType><firewallAddress>FireWallAddress</firewallAddress><firewallPort>21</firewallPort><firewallUserName>Username</firewallUserName><firewallPassword>Password</firewallPassword><commandLogFilename>c:\\Log</commandLogFilename><errorThreshold>10</errorThreshold><maxFileSize>100</maxFileSize></Config></AdapterConfig></CustomProps>";

                //create the Managementobject  
                objSendHandler.Put(options);
                System.Console.WriteLine("SendHandler - " + adapterName + " " + hostName + " - has been created successfully");
            }
            catch (Exception excep)
            {
                System.Console.WriteLine("CreateSendHandler - " + adapterName + " " + hostName + " - failed: " + excep.Message);
            }
        }

    }
}

Testing BizTalk Http Receive Adapter and streaming files

A simpel test

There is quite a few steps involved in setting up a receive port/location for the http receive adapter on the server side, firewall and client side

It is nice to have a toolbox to test the connectivity 

The following files should be stored in the root folder of the website added as the public address configured in the http transport properties dialog box

default.htm

After configuration in IIS and BizTalk access can be checked using a very simple html page, it could be index.htm or default.htm by preference

<html>
	<head>
		<title>BizTalk Service - HTTP Receive</title>
	</head> 
	<body> 
		<p>BizTalk Service - HTTP Receive</p> 
		<p><a href="form.htm">Connectivity test</a></p>
	</body> 
</html>
This page is used to test the firewall settings and https connectivity and whether the client certificate is properly installed on the client server

form.htm

On the page I have a link to a basic html form also to test BizTalk Server and IIS configuration. In BizTalk you should receive a basic document with content taking from the imput fields

<html> 
	<head>
		<title>BizTalk Service - HTTP Receive</title>
	</head> 
	<body> 
		<form name="Test" method="post" action="BTSHTTPReceive.dll"> 
			<p>Test BTSHTTPReceive port, please type a short message:</p> 
			<p>
				<input type="text" name="message"> 
				<input type="submit" value="Submit"> </p>
			<p>You will receive a response with an correlationtoken if the post is successfull</p>
		</form> 
	</body> 
</html>

Very basic and will send the data "message=<value>" to BizTalk and return a correlation token if confgured

Scale-up

Next step is to test the complete flow, I have created a small console application as an example on how to send data files to BizTalk through the Http Receive Adapter

WebClient

I will use the WebClient class for sending the data
First we need to create a sub class to add a X.509 certificate to HttpWebRequest ClientCertificates collection

class HttpsClient : WebClient
{
	public X509Certificate2 Certificate { get; set; }
	
	// Override GetWebRequest to add certificate to the request object
	protected override WebRequest GetWebRequest(Uri address)
	{
		HttpWebRequest request = (HttpWebRequest)base.GetWebRequest(address);
		request.ClientCertificates.Add(this.Certificate);
		return request;
	}
}

Get Certificate

The certificate is retrieved from current user/address book certificate store using the thumbprint, the method is part of the console main program class

static X509Certificate2 GetClientCertificateFromThumbPrint(string thumbPrint)
{
	if (string.IsNullOrEmpty(thumbPrint))
		throw new ArgumentException(
			String.Format("Error: Thumbprint is empty"), "ThumbPrint"
		);
	
	X509Store _store = new X509Store(
		StoreName.AddressBook, StoreLocation.CurrentUser
		);
	
	// Try to open the store.
	_store.Open(OpenFlags.ReadOnly);
	// Find the certificate that matches the thumbprint.
	X509Certificate2Collection _certs = _store.Certificates.Find(
		X509FindType.FindByThumbprint, thumbPrint, false
		);
	_store.Close();
	
	// Check to see if our certificate was added to the collection. 
	// If no, throw an error, if yes, create a certificate using it.
	if (_certs.Count == 0)
	{
		throw new ArgumentException(
			String.Format(
				"Error: No certificate found containing thumbprint: {0}", 
				thumbPrint
				), 
			"ThumbPrint"
			);
	}
	return _certs[0];
}

Main program

Code listing for the main program, get client certificate, open stram for sending data to the site and open another stream to the local file, use the copyto method and flush the stream to send the data across the boundaries

// Main program
static void Main(string[] args)
{
	byte[] _responseHeaders = null;
	string _clientResponse = string.Empty;


	X509Certificate2 _certificate = null;
	try
	{
		string _thumbPrint = "1a 2b 3c 4d 5e 6f a1 b2 c3 d4 e5 f6 1a 2b 3c 4d 5e 6f a1 b2";
		_certificate = GetClientCertificateFromThumbPrint(_thumbPrint);
	}
	catch (Exception exc)
	{
		Console.WriteLine(exc);
		throw new Exception("Certificate exception", exc);
	}

	// Url to BizTalk Http Receive ISAPI dll
	Uri _address = new Uri("https://biztalk.test.invalid/test/BTSHTTPReceive.dll");
	// Load file from disk or other source
	string _filePath = @"d:\data\test\Order.4b496d3c-6214-495b-bbce-1850516b6cf9.xml";

	try
	{
		using (HttpsClient _client = new HttpsClient())
		{
			_client.Certificate = _certificate;
			using (var _stream = _client.OpenWrite(_address))
			{
				using (FileStream _fileStream = 
					new FileStream(_filePath, FileMode.Open, FileAccess.Read))
				{
					_fileStream.CopyTo(_stream);
				}
				_stream.Flush();
			}

			Console.WriteLine("{0} at {1:F}", _client.StatusCodeDescription, _client.LastModified);
			Console.WriteLine("");

			_responseHeaders = _client.ResponseHeaders.ToByteArray();
			_clientResponse = _client.Response;

		}
	}
	catch (WebException exc)
	{
		Console.WriteLine(exc);
		throw new Exception("WebClient exception", exc);
	}

	finally
	{
		if (!string.IsNullOrEmpty(_clientResponse))
			Console.WriteLine(_clientResponse);

		if (_responseHeaders != null && _responseHeaders.Length > 0)
			// Decode and display the response.
			Console.WriteLine(Encoding.ASCII.GetString(_responseHeaders));
	}
}

Server Response

This is working ok but we are missing the correlation token received and status codes from the web server. From the content-length in the ReponseHeader we can see that we have the data

GetWebResponse

Override the GetWebResponse method in the WebClient class to read the reponse from WebRequest class as well as status code and description

// Intercept the WebRequest object to get response data from host
protected override WebResponse GetWebResponse(WebRequest request)
{
	this.StatusCode = HttpStatusCode.Accepted;
	
	// Get response stream from web request
	HttpWebResponse _webResponse = (HttpWebResponse)request.GetResponse();
	
	this.StatusCode = _webResponse.StatusCode;
	this.StatusCodeDescription = _webResponse.StatusDescription;
	this.LastModified = _webResponse.LastModified;
	
	// Is the stream readable?
	if (this.StatusCode == HttpStatusCode.Accepted && _webResponse.GetResponseStream().CanRead)
	{
		using (StreamReader _reader = new StreamReader(_webResponse.GetResponseStream(), true))
		{
		  // Get stream content
		  this.Response = _reader.ReadToEnd();
		}
	}
	return base.GetWebResponse(request);
}

Remember to add the four extra properties to our HttpsClient class and you can read the properties after the OpenWrite operation is finished and the Stream has been closed

Sample VS 2012 project attached to article  /have fun

BtsHttpReceiveTest.zip (35,83 kb)