Category Archives: BizTalk

My experiences with BizTalk Server

Function App throws “System.OutOfMemoryException” Error

One of our Function Apps has been frequently failing with the “Out of Memory” exception. It was an intermittent access, where Function App crashes at random intervals.

Error Message –

After lots of troubleshooting, we realized it could be because of App Service Plan’s 32-bit platform architecture configuration.

RAM Usage for Bit Architecture – In terms of Random Access Memory, 32-bit architectures can address 4GB of memory, maximum. A 64-bit architecture, in turn, has a theoretical limit of addressing 16 million TB of memory.

We changed this value to 64 bit and it resolved the error, permanently.

Resolution –

Hope, this article saves you from many troubleshooting hours.

I believe this issue must be common for any app service plan either for Web App or Logic Apps as well.

Contact Me:- 

@MyYouTubeChannel, @Gmail@Facebook , @Twitter, @LinkedIn @MSDNTechnet, @My Personal Blog

BizTalk Export MSI and Binding files to a Folder programmatically C#

Many times as developers we may need to export Binding files and MSIs in bulk for all the applications especially in case of any major update to keep as backup or DR scenarios.

I have written a utility to achieve this, hope it helps.

This utility uses “BTSTask.exe” to export MSIs and Bindings by calling it from cmd.exe.

Export Binding Files –

private static void exportBindingFile(string strApplicationName)
        {
                string strBindingFile = string.Format("{0}\\{1}{2}", strExportFolder, strApplicationName, strBindingPostFix);
                string exportArgumentsBindings = string.Format("ExportBindings -Destination:\"{0}\"  -ApplicationName:\"{1}\"", strBindingFile, strApplicationName);
                exceuteCommand(strBTSTaskCMD, exportArgumentsBindings);
        }

Export MSIs –

private static void exportMSIFile(string strApplicationName)
        {
           string strMSIFile = string.Format("{0}\\{1}{2}", strExportFolder, strApplicationName, strMSIPostFix);
           string exportArgumentsMSIs = string.Format("ExportApp -Package:\"{0}\"  -ApplicationName:\"{1}\"", strMSIFile, strApplicationName);
           exceuteCommand(strBTSTaskCMD, exportArgumentsMSIs);
        }

Function to execute this command using cmd.exe –

private static int exceuteCommand(string command, string arguments)
        {
            try
            {
                Process process = new Process();
                process.StartInfo.FileName = command;
                process.StartInfo.RedirectStandardOutput = true;
                process.StartInfo.UseShellExecute = false;
                process.StartInfo.Arguments = arguments;
                process.Start();
                string stdout = process.StandardOutput.ReadToEnd();
                process.WaitForExit();
                var exitCode = process.ExitCode;
                logMsg(string.Format("ExitCode: {0}", exitCode));
                logMsg(string.Format("Response: {0}", stdout));
                process.Close();
                if (exitCode == 0)
                {
                    logMsg("Command executed successfully");
                }
                else
                {
                    logMsg("Command execution failed");
                }
                return exitCode;
            }
            catch (Exception excep)
            {
                strFunctionName = "exceuteCommand";
                string strExceptionMessage = " - An exception occurred in Function - " + strFunctionName + ". " + Environment.NewLine + "Message - " + excep.Message;
                throw new Exception(strExceptionMessage);
            }
        }

Hope it’s helpful.

Download the complete code from here

Contact Me:- 

@Gmail@Facebook @Twitter@LinkedIn@MSDNTechnet@My Personal Blog 

C# Programmatically Create BizTalk Host, Host Instances and Set Orchestration (Xlang) and Messaging Polling Interval (Performance Tuning)

Recently we were getting too many SQL timeout issues in our BizTalk environment. After multiple troubleshooting one of the performance optimization suggestion was to increase the Polling Interval for all the host instances to decrease the load on SQL Server.

Polling interval of Host means how frequently it polls the Messaging(Send & Receive) or Orchestration (Xlang) Host queue to check for new messages as Subscriber.

In Low Latency scenarios, users prefer to set this value to very low up to 50ms, but this definitely puts extra load on SQL Server. This situation can be even worsened in the case of a large BizTalk Group or with many host instances.

The polling interval of a host is of 2 types and has a default value of 500ms

  • Messaging – Polling interval for messaging(send & receive) host queues
  • Orchestration – Polling Interval for Orchestration (Xlang) host queue
BizTalk Host – Polling Interval of two types Messaging and Orchestration, default value as 500ms

As a best practice to reduce the load on SQL Server it’s recommended to set a very high value of Messaging Polling interval for dedicated Process Hosts – running Orchestration, because it’s just running the orchestration and not Receive or Send Port.

Similarly, for dedicated Send Port hosts, it’s recommended to set the Orchestrations polling interval to a very high value.

For Receive Port(One-Way) hosts it is recommended to increase both Messaging and Orchestration polling interval to a very high value because Receive Locations acts as Publisher and not Subscriber to pick new messages.

To disable polling, you can set the polling interval to a very big number as listed in the table.

Server HostsMessagingOrchestration
ReceiveHost

Because we are only publishing incoming messages to the BizTalk message box through a one-way receive location, polling is not required on the ReceiveHost.
200000200000
TransmitHost

Because we are only receiving messaging instances from the BizTalk message box, orchestration polling is not required on the TransmitHost (send host).
50 – Low Latency/
500
200000
ProcessHost

Because we are only receiving orchestration instances from the BizTalk message box, messaging polling is not required on the ProcessHost (Processing host).
20000050 – Low Latency/
500

You can set this value from BizTalk Admin Console as well –

Use the Settings Dashboard to configure the polling intervals of a given host, across the BizTalk Group.

  1. In the BizTalk Server Administration Console, expand BizTalk Server Administration, right-click BizTalk Group, and then click Settings.
  2. In the BizTalk Settings Dashboard dialog box, on the Hosts page, on the General tab, under Polling Intervals, you will find the Messaging and Orchestration values. By default, both these values are set to 500 milliseconds.

In my case, we needed this activity for all host instances so we decided to achieve it programmatically using C# console app.

It talks about other Host related operations like – Creating Host & Host Instances, Update Host Instances, Delete Host Instances and most importantly changing the Polling Interval settings.

Find the code piece for all these activities –

Create Host –

public static void CreateHost(
string HostName, int HostType, string NTGroupName, bool AuthTrusted)
    {
        try
        {
            PutOptions options = new PutOptions();
            options.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;

            //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);
        }
    }

Create Host Instance –

<code>public static void CreateHostInstanceMapAndInstall(
string hostName, string svrName, string uid, string pwd)
 {
            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();
                ManagementClass svrHostClass = new ManagementClass("root\\MicrosoftBizTalkServer", "MSBTS_ServerHost", svrHostOptions);
                ManagementObject 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();
                ManagementClass hostInstClass = new ManagementClass("root\\MicrosoftBizTalkServer", "MSBTS_HostInstance", hostInstOptions);
                ManagementObject 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);
            }
        }</code>

Update Polling Interval –

<code>public static void updatePollingInterval()
        {
            try
            {
                //Create EnumerationOptions and run wql query  
                EnumerationOptions enumOptions = new EnumerationOptions();
                enumOptions.ReturnImmediately = false;

                //Search for all HostInstances of 'InProcess' type in the Biztalk namespace scope  
                ManagementObjectSearcher searchObject = new ManagementObjectSearcher("root\\MicrosoftBizTalkServer", "Select * from MSBTS_HostSetting", enumOptions);

                //Enumerate through the result set and start each HostInstance if it is already stopped  
                foreach (ManagementObject inst in searchObject.Get())
                {
                    bool boolUpdateRequired = false;
                    //Orchestration Hosts
                    if (inst["Name"].ToString().ToUpper().Contains(xlangHostNameFilter))
                    {
                        inst["MessagingMaxReceiveInterval"] = messagingPollingInterval;
                        Console.WriteLine("HostName - " + inst["Name"].ToString() + " MessagingMaxReceiveInterval updated");
                        boolUpdateRequired = true;
                    }
                    //Transmit Hosts
                    else if (inst["Name"].ToString().ToUpper().Contains(transmitHostNameFilter))
                    {
                        inst["XlangMaxReceiveInterval"] = xlangPollingInterval;
                        Console.WriteLine("HostName - " + inst["Name"].ToString() + " XlangMaxReceiveInterval updated");
                        boolUpdateRequired = true;
                    }

                    //Receive Hosts
                    else if (inst["Name"].ToString().ToUpper().Contains(receiveHostNameFilter))
                    {
                        inst["XlangMaxReceiveInterval"] = xlangPollingInterval;
                        Console.WriteLine("HostName - " + inst["Name"].ToString() + " XlangMaxReceiveInterval updated");

                        //Setting Messaging Receive Interval for One Way Host Instances 
                        if (!
                            (inst["Name"].ToString().ToUpper().Contains("WCF") ||
                            inst["Name"].ToString().ToUpper().Contains("JMS") ||
                            inst["Name"].ToString().ToUpper().Contains("SOAP") ||
                            inst["Name"].ToString().ToUpper().Contains("HTTP") ||
                            inst["Name"].ToString().ToUpper().Contains("GENERIC_RECEIVEHOST")
                            ))
                        {
                            inst["MessagingMaxReceiveInterval"] = messagingPollingInterval;
                            Console.WriteLine("HostName - " + inst["Name"].ToString() + " MessagingMaxReceiveInterval updated");
                        }
                        boolUpdateRequired = true;
                    }

                    //Tracking Host
                    else if (inst["Name"].ToString().ToUpper().Contains(trackingHostNameFilter))
                    {
                        inst["XlangMaxReceiveInterval"] = xlangPollingInterval;
                        Console.WriteLine("HostName - " + inst["Name"].ToString() + " XlangMaxReceiveInterval updated");
                        inst["MessagingMaxReceiveInterval"] = messagingPollingInterval;
                        Console.WriteLine("HostName - " + inst["Name"].ToString() + " MessagingMaxReceiveInterval updated");
                        boolUpdateRequired = true;
                    }
                    if (System.Convert.ToBoolean(ConfigurationManager.AppSettings["updatePollingInterval"]))
                    {
                        //Update Max Polling Interval
                        if (boolUpdateRequired)
                        {
                            //update the ManagementObject  
                            PutOptions options = new PutOptions();
                            options.Type = PutType.UpdateOnly;
                            inst.Put(options);
                        }
                    }
                }
            }
            catch (Exception excep)
            {
                Console.WriteLine("Function-updatePollingInterval- Failure while updating Polling Interval: " + excep.Message);
            }
        }</code>

Delete Host and Host Instances –

<code>public static void DeleteHost(string HostName)
        {
            try
            {
                ManagementObject objHostSetting = new ManagementObject();
                objHostSetting.Scope = new ManagementScope("root\\MicrosoftBizTalkServer");

                //define lookup query  
                string strQuery = "MSBTS_HostSetting.Name='" + HostName + "'";
                objHostSetting.Path = new ManagementPath(strQuery);

                //delete the Managementobject  
                objHostSetting.Delete();

                System.Console.WriteLine("Host - " + HostName + " - has been deleted successfully");
            }
            catch (Exception excep)
            {
                System.Console.WriteLine("DeleteHost - " + HostName + " - failed: " + excep.Message);
            }
        }</code>

Update Host –

public static void UpdateHost(string HostName, string NTGroupName, bool AuthTrusted)
        {
            try
            {
                PutOptions options = new PutOptions();
                options.Type = PutType.UpdateOnly;

                ManagementObject objHostSetting = new ManagementObject();
                objHostSetting.Scope = new ManagementScope("root\\MicrosoftBizTalkServer");

                //define lookup query  
                string strQuery = "MSBTS_HostSetting.Name='" + HostName + "'";
                objHostSetting.Path = new ManagementPath(strQuery);

                //redefine properties value  
                objHostSetting["NTGroupName"] = NTGroupName;
                objHostSetting["AuthTrusted"] = AuthTrusted;

                //update the ManagementObject  
                objHostSetting.Put(options);
                System.Console.WriteLine("Host - " + HostName + " - has been updated successfully");
            }
            catch (Exception excep)
            {
                System.Console.WriteLine("UpdateHost - " + HostName + " - failed: " + excep.Message);
            }
        }

Hope it’s helpful.

Download the complete code from here

Contact Me:- 

@Gmail@Facebook , @Twitter, @LinkedIn @MSDNTechnet, @My Personal Blog 

BizTalk Server High Availability and  Disaster Recovery Options

Watch the Video version –

It’s one of the most important topics for BizTalk Architects but most confusing as well. I’ll try my best to present the pros and cons of different HA & DR options available and used in the BizTalk world.

What is High Availability (HA) and Disaster Recovery (DR) and why do we need them?

High Availability – High availability ensures that the system can operate continuously without fail. It shouldn’t have any single point of failure and should ensure continuous operation or uptime.

Disaster Recovery – Disaster Recovery is a detailed plan on how to respond and regain access and functionality in case of unplanned incidents such as natural disasters, power outages, cyber-attacks and any other disruptive events.

BizTalk Server consists of 3 main components

  1. BizTalk Processing Server ( Host Instances for processing)
  2. BizTalk Databases (Persistence of Data)
  3. ENTSSO – Single Sign-On (Encryption)

Because of the clear-cut demarcation between Data and Processing, it becomes extremely easy to Scale BizTalk Hosts(processing server) and BizTalk Databases independently, solving issues of High Availability and Load Balancing.

Note: – For True High Availability or Disaster Recovery complete end-to-end components (i.e. frontend, middleware, backend) should be highly available and configured for DR. Here our focus is only on BizTalk Server.

BizTalk Server High Availability

Designing a BizTalk Server deployment that provides high availability involves implementing redundancy for each functional component involved in an application integration or business process integration scenario. 

BizTalk Server HA focusses broadly on the below points –

  1. Processing Server High Availability – BizTalk Application Server 
    • In-Proc Host Instances 
    • Isolated Host Instances (For incoming message from IIS) 
  2. BizTalk Databases High Availability Options
    • SQL Active-Passive Failover Cluster
    • SQL Server Always On
  3. SSO Master Secret High Availability Options
    • Cluster ENTSSO Service on Existing SQL Server Cluster or independent cluster

BizTalk Application (Processing) Server High Availability

BizTalk Server lets you separate hosts and run multiple host instances to provide high availability for key functions such as receiving messages, processing orchestrations, and sending messages. 

In-Process Host Instances

BizTalk Server automatically distributes workload across multiple servers through host instances, you do not require any additional clustering or load balancing mechanisms.

Every host should have at least 2 host instances running.

Note – Adapters like FTP, SFTP, POP3 result in duplicate or incomplete message processing if multiple host instances are running, due to the nature of these protocols. These adapters don’t put an exclusive lock on the file while reading, which causes duplicate/incomplete read if multiple host instances are running. 

To avoid this many customers, disable other host instances except one, but this provides only manual failover. It’s recommended to use Windows Clustering, which ensures only one active host instance.

Refer to the below articles for more detail –

Known Issues with the FTP Adapter

Clustering Receiving Hosts

How to Configure a BizTalk Host as a Cluster Resource  

Isolated Host Instances –

Hosts running the receive handler for the HTTP, SOAP, WCF-BasicHTTP, WCF-WebHTTP and other IIS-related adapters require a load-balancing mechanism such as Network Load Balancing (NLB) to provide high availability.

Overall Architecture - BizTalk High Availability

BizTalk Databases High Availability

High availability for the BizTalk Server databases can be achieved in two ways –

  1. SQL Server Active-Passive Failover Cluster ( Traditional)
  2. SQL Server Always On (BizTalk 2016 onwards)
SQL Server Active-Passive Failover Cluster

Typically consists of two or more database computers configured in an active/passive server cluster configuration. These computers share a common disk resource (such as a RAID5 SCSI disk array or storage area network) and use Windows Clustering to provide backup redundancy and fault tolerance. The shared disk should be highly available, many customers use SAN drives.

Pros

  1. Traditional SQL Server clustering method.
  2. A bit better performant that SQL Server Always-On

Cons –

  1. As the datafiles are stored in a common location, it may act as a single point of failure. Many customers use highly available file share like SAN to overcome this limitation.
  2. Secondary node can’t be used for any operation.
SQL Server Always-On

Always-On works with the concept of synchronous data replication from Active node to passive nodes. All the SQL Servers configured in one availability group has their own datafiles and are not shared.

SQL Server Always On

An availability group supports a replicated environment for a discrete set of databases, known as availability databases. 

You can create an availability group for high availability (HA) – A group of databases that failover together.

Deploying AlwaysOn Availability Groups requires a Windows Server Failover Clustering (WSFC) cluster. Each availability replica of a given availability group must reside on a different node of the same WSFC cluster. A WSFC resource group is created for every availability group that you create. The WSFC cluster monitors this resource group to evaluate the health of the primary replica. The following illustration shows an availability group that contains one primary replica and four secondary replicas.

Clients can connect to the primary replica of a given availability group using an availability group listener.  An availability group supports one set of primary databases and one to eight sets of corresponding secondary databases.

Pros –

  1. Doesn’t have single point of failure as all the SQL Servers have their own copy of Databases which is replicated via sync service
  2. Secondary databases can be used in Read-Only mode. This may be helpful in serving reports, websites etc. for BAM or other databases from secondary nodes.

Cons –

  1. As every request comes only to primary node and then gets replicated to secondary nodes, it may add little latency. Better network speed can mitigate this issue.

Refer to the below article for more details –

High Availability using SQL Server Always On Availability Groups – BizTalk Server

Master Secret (SSO) High Availability

It has two main components –

  1. SSO key – Generated at the time of configuration as SSO<text>.bak file. It should be backed up carefully in TFS, mails etc. along with Password. It’s required at the time of restore.
  2. SSO Service – It should be clustered to ensure atleast 1 Master Secret sub-service is running to cache the key.

Detailed Explanation –

BizTalk Server uses SSO to secure information for port configuration. It’s encrypted and stored in the SSO database. Each BizTalk server has an SSO – Enterprise Single Sign-On service (ENTSSO.exe) which performs all these operations.

When an SSO service starts up, it retrieves the encryption key from the master secret server. This encryption key is called the master secret. The master secret server has an additional sub-service that maintains and distributes the master secret. After a master secret is retrieved, the SSO service caches it. Every 60 seconds, the SSO service synchronizes the master secret with the master secret server.

If the master secret server fails, and the SSO service detects the failure in one of its refresh intervals, the SSO service and all run-time operations that were running before on the server failed, including decryption of credentials, continue successfully. However, you cannot encrypt new credentials or port configuration data. Therefore, the BizTalk Server environment has a dependency on the availability of the master secret server.

Making the Master Secret Server Key HA

It’s critical to back up the master secret as soon as it is generated. If you lose it, you lose the data that the SSO system encrypted by using that master secret.  For more information about backing up the master secret, see How to Back Up the Master Secret (http://go.microsoft.com/fwlink/?LinkID=151934) in BizTalk Server Help.

Making Master Secret Service HA– 

Use Windows Clustering on a separate master secret server cluster or use an existing database cluster. The services provided by the master secret server do not consume many resources, and typically do not affect database functionality or performance when installed on a database cluster. 

The following figure shows how you can make the master secret server highly available.

Refer to the below article for detailed steps on how to cluster Master Secret –

Clustering the Master Secret Server How to Cluster the Master Secret Server

How to Cluster the Master Secret Server

Cluster ENTSSO Service on existing SQL Server Cluster

We can configure ENTSSO on the existing SQL Server Cluster as shown below –

Benefits –        

Limited Change and no extra resources – As Master Secret service is configured on existing SQL Server Failover Cluster, it utilizes existing setup and requires very little changes.

Messages suspended on one server can resume processing on another

Limitation – System will require downtime for patching etc.

Custom HA – Creating Parallel BizTalk Server Environment

Another option is to create a parallel environment (PROD2). All the incoming traffic can be diverted using Network Load Balancer to PROD1 and PROD2 environments.

Benefits –
1) Increased Processing Capacity – Load Balancing

Additional BizTalk and SQL server setup bring additional processing capacity. As more applications are about to come to existing system more capacity will be required.

  • Zero Downtime – Downtime requirement during patching or any other mishappening can be averted now.

Deployment or patching activity can be first performed in PROD1. Meantime all the requests can be rendered by PROD2 providing true HA. Later PROD1 can be brought online and changes can be performed in PROD2.

  • High Risk Tolerance – Many times patching or any other activity brings complete environment to halt. This can be avoided with this setup, as one server may act as playground or buffer for changes.
Limitations – 
  • Requires additional resources – Although, due to performance limitation if 2 additional processing servers are required, it’s better to increase it as a parallel setup. As only additional cost will be of 1 SQL Server.
  • Both the environments act as a different BizTalk Group – This means both the groups are totally unrelated. Message in one group has no relation to another.

So, messages suspended in 1 group can’t be retrieved on another. However, as per ABFSS requirements suspended messages are not required to be process after some time.

Custom HA – Creating Secondary setup in cloud for HA and DR

Secondary setup can also be hosted in Azure cloud as (Infrastructure as a Service) IaaS VMs.

Benefits –
  1. Increased Processing Capability in cloud.
  2. High Availability in Cloud
  3. Access to Azure Capabilities and Benefits
  4. Integration to Azure SaaS/PaaS becomes easy
  5. Provides Disaster Recovery as well if configured in different region

Note – Using Express Route can result in great connectivity speed between on-prem and cloud servers.

Cons –

Requires additional resources

Refer –

What is Infrastructure as a service (IaaS)?

Windows virtual machines in Azure

Benefits of IaaS –
  • Eliminates capital expense and reduces ongoing cost. IaaS sidesteps the upfront expense of setting up and managing an on-site datacentre, making it an economical option for start-ups and businesses testing new ideas.
  • Improves business continuity and disaster recovery. Achieving high availability, business continuity and disaster recovery is expensive since it requires a significant amount of technology and staff. But with the right service level agreement (SLA) in place, IaaS can reduce this cost and access applications and data as usual during a disaster or outage.
  • Innovate rapidly. As soon as you have decided to launch a new product or initiative, the necessary computing infrastructure can be ready in minutes or hours, rather than the days or weeks—and sometimes months—it could take to set up internally.
  • Respond quicker to shifting business conditions. IaaS enables you to quickly scale up resources to accommodate spikes in demand for your application— during the holidays, for example—then scale resources back down again when activity decreases to save money.
  • Focus on your core business. IaaS frees up your team to focus on your organization’s core business rather than on IT infrastructure.
  • Increase stability, reliability and supportability. With IaaS there is no need to maintain and upgrade software and hardware or troubleshoot equipment problems. With the appropriate agreement in place, the service provider assures that your infrastructure is reliable and meets SLAs.
  • Better security. With the appropriate service agreement, a cloud service provider can provide security for your applications and data that may be better than what you can attain in-house.
  • Gets new apps to users faster. Because you don’t need to first set up the infrastructure before you can develop and deliver apps, you can get them to users faster with IaaS.

Inbuilt monitoring of BizTalk using Application Insight, Log Analytics and Alerts.

Disaster Recovery of BizTalk Server

Disaster recovery procedures improve system availability by employing steps to resume operation of a failed system. Disaster recovery differs from fault tolerance in that disaster recovery typically requires manual intervention to restore the failed system whereas a fault-tolerant design can continue to operate without manual intervention if the system encounters a failure condition.

Refer below articles for detailed steps –

Here we will only focus on DR of BizTalk Server, excluding –

  • Non-BizTalk applications
  • Application source code
  • Certificates
  • Application operations

Using BizTalk Backup Job and Log Shipping – Only MS Supported DR way

The only supported way of backing up the BizTalk databases is by using the out of the box Backup BizTalk Server Job combined the BizTalk Log Shipping for automatic restoration.

Configure and activate the BizTalk Backup Job to generate the only supported BizTalk backup files and ensure you are able to restore them by planning and testing a disaster recovery plan. The success criteria for a Disaster Recovery plan happens only when you test your scenario.

BizTalk DR Setup High Level Overview –

1) BizTalk Application Servers – 

Configure DR BizTalk Servers with “Join an existing group” settings. BizTalk Servers should be part of same group to understand and process messages.

2) ENTSSO Servers – 

Configure ENTSSO with “Join an existing group” settings.

3) BizTalk Databases –

Configure BizTalk Backup Job to create backup files and log shipping to restore it to DR servers

Detailed steps are given in another document.

Benefits –

  • Only supported DR methodology.
  • DR Servers can be brought within very less time
  • As DR and DC system both are part of same group, this allow suspended messages in DR to be recovered in DR.

Limitations – Bringing DR Servers to operation requires manual intervention and may take some time.

Using Parallel DR Setup in Cloud/On-Prem (Custom -Not supported by MS)

Create parallel BizTalk Setup in DR environment in On-Prem or Cloud.

Benefits –

  • Load Balancing – Load can be split between DR and DC servers. If latency is not a major concern.
  • No Downtime – As DR is always available, during disaster downtime is not required
  • DR will always be tested and Updated – As DR is always used for message processing it will always be updated with latest code and tested

Limitations –

•    It’s a custom DR, where both the BizTalk groups are un-related. Messages being processed on one server will not be present in another. This poses risk of data loss during disaster.

As, complete message processing is atomic. So, resuming suspended instances/in-flight on DR server is not required.

Note – 

Configuring this parallel setup in the cloud will provide HA and DR both as cloud setup can be set in another region.

Contact Me –

Check out my other blogs –

Function App throws “System.OutOfMemoryException” Error

One of our Function Apps has been frequently failing with the “Out of Memory” exception. It was an intermittent access, where Function App crashes at random intervals. Error Message – Exception while executing function: FuncApp. One or more errors occurred.An exception was thrown by a TaskScheduler.Exception of type ‘System.OutOfMemoryException’ was thrown. After lots of troubleshooting,…

BizTalk Automated migration Tool

Watch the Video – BizTalk Automated Migration Tool Demo –

Microsoft has recently released BizTalk 2020 with lots of new features and enhancements.

There are many reasons to Migrate and can be broadly summarized as below –

  1. Avail Microsoft Support
    • BizTalk 2016 Mainstream Support Ends – 1st November 2022
    • All other previous versions like 2013R2, 2013 or 2010 etc. are long out of mainstream support
    • Upgrade to latest software version of OS, VS, SQL & BizTalk
  2. Enhance BizTalk Environment
    • For better infrastructure, performance, topology – HA/DR
  3. Avail New features
    • BizTalk 2020 is loaded with new features of Hybrid Integration, Improvised Security (TLS1.2, GDPR compliance), Better Monitoring etc.
    • Feature packs – Get new features faster, without waiting for new versions
    • New Features for Developers and Administrators
  4. Move to Cloud
    • Ability to run on Azure VM as IaaS

Microsoft has always attempted to make migrations as seamless and easy as possible by providing various options like –

  1. In-Place Upgrade – Only possible for N-1 version
  2. BizTalk Server Migration Tool – Which performs as-is migration by exporting artifacts from previous environment to new environment.

In-Place Upgrade is more like installing Cumulative Updates or Feature Pack. It requires minimum efforts and is mostly very seamless but has many limitations like only possible for N-1 version, rollback is tough.

BizTalk Server Migration Tool is provided by MSIT and is extremely helpful for as-is migration. On high level it exports MSI, Binding Files etc. from old environment and imports in the new environment. So all the dlls still point to previous version and nothing is changed.

For as-is migration, it definitely saves lots of time and efforts but has limitations like we can’t use new features or enhancements.

Therefore, many organisations prefer to use traditional method of code migration, which allows code upgrade, enhancement and possibility of modernization. But, this process is definitely very lengthy, time taking and resource intensive.

As a BizTalk Developer, Migration operations can be broadly categorized as below –

  1. Migrate to Latest Visual Studio – Open each solution in Visual Studio, by default it will upgrade solution to VS. It will also change the dotnet framework for all BizTalk projects.
  2. DotNet Framework Change – You may have to change DotNet Framework for Class Library Projects like Pipeline Components, Helper/Components, Custom Functoids, Custom Adapters etc. as they aren’t changed with VS Upgrade.
  3. Add BTDF or other Deployment Project (Optional)
  4. Rebuild Solution – Review failures and fix them.
  5. E2E Testing – End to end testing with all the parties involved in integration.

Performing all these operations for hundreds and thousands of applications is extremely tedious, time taking and resource intensive work. Every year companies spend thousands of man-hours just to perform these migration activities.

We attempted to reduce the efforts for developers by automating most of the routine tasks and only focus on things which really require human intervention.

We created a tool which solves top 4 problems and performs them as One-Click Operation.

Operations which can be Automated By Tool –

  1. Upgrade Solutions to Latest Visual Studio
  2. Change .Net Framework for all Projects
  3. Add BTDF Project to solution, with proper changes to Deployment.btdfproj file
  4. Rebuild Solution
  5. Detailed Logging of each operation for quick access to successful and failed solutions

Proposed Steps –

  1. “Browse for Folder with all applications”
  2. Get List of all the Solutions in that folder.
  3. Choose from Migration Operations –
    • Upgrade – This will upgrade to latest visual studio version.
    • Add BTDF Project – Adds BTDF Template project will all the files
    • Build – Performs Rebuild of all the solutions/projects
    • Change .Net Framework to Target Version – Select target Dotnet framework from the dropdown. All the projects will be migrated to this version, especially helpful for helper libraries like pipeline component, custom functoids, adapters and other helper components
  4. Detailed logging can be performed for each operation.
  5. You can also create folders and generate/move outputs to different folders like – “UpgradeSuccessful”, “UpgradeFailed”, “BuildSuccessful” and “BuildFailed”

This will allow developers to only focus on applications for which Build Operation failed.

Detailed Description of each operation –

Migrate Operation –

This operation is similar to manually Opening each solution in Visual Studio which performs

a) Upgrade to latest VS and

b) Change of .Net framework (to default version) for BizTalk projects (only BizTalk Projects(.btproj) not class library projects(.csproj)).

It also creates Backup folder with migrated projects and log file – “UpgradeLog.htm” next to the solution file. You can also choose to delete the backup file, to save space if you already have another backup.

To perform this operation from tool you can use visual studio command line arguments –

devenv {SolutionFile|ProjectFile} /Upgrade [/Out OutputFilename]

Refer –

https://docs.microsoft.com/en-us/visualstudio/ide/reference/upgrade-devenv-exe?view=vs-2019

Build Operation –

Similarly, Build operation can also be performed by VS command line arguments –

devenv SolutionName /Rebuild [SolnConfigName [/Project ProjName [/ProjectConfig ProjConfigName]] [/Out OutputFilename]]

Refer –

https://docs.microsoft.com/en-us/visualstudio/ide/reference/rebuild-devenv-exe?view=vs-2019

Most of the errors in this step is related to missing reference of common libraries or helper projects/dlls.

Change DotNet Framework –

You can allow users to choose target framework from a dropdown and later upgrade all projects in solution to this version. These values can be fetched from App.config file.

Your tool can get the list of all the projects referred/present in Solution (Solution file) and later change their .Net framework for each of them.

Regular VS Upgrade option performs .Net framework changes only to BizTalk projects and that too only to default version.

Note – For BizTalk 2020 we found a bug with .Net Framework 4.7.2, so we decided to migrate all the projects to 4.8 version. This tool was extremely helpful in mass framework change.

It’s also extremely helpful for helper class libraries like pipeline components, custom functoids, custom adapters or C# Helper/Component projects which are not upgraded in Upgrade process.

Dotnet Framework can be upgraded just by updating the project files.

  1. In the project file, locate the entry for the target Framework version. Ex -if your project is designed to use the .NET Framework 4.5, locate <TargetFrameworkVersion> v4.5 </ TargetFrameworkVersion > in the <PropertyGroup Label = “Globals” > element of the < Project > element.

If the<TargetFrameworkVersion> element isn’t present, your project doesn’t use the.NET Framework and no change is required.

2. Change the value to the Framework version you want, such as v4.7.2 or v4.8.

You can also use Regex pattern replace –

string regexPattern = “<TargetFrameworkVersion>*.*</TargetFrameworkVersion>”;

3. Save the changes and close the editor.

Refer –https://docs.microsoft.com/en-us/cpp/build/how-to-modify-the-target-framework-and-platform-toolset?view=vs-2019

Add BTDF Project –

In this step you can add a template of BTDF project with Deployment folder and all other files –

  • Deployment.btdfproj
  • InstallWizard.xml
  • License.rtf
  • PortBindingsMaster.xml
  • SettingsFileGenerator.xml
  • UnInstallWizard.xml

You can also update the Deployment.btdfproj file with many routine information like –

  • ProjectName – Solution File
  • Include Options for
    • Schemas
    • Maps/Transforms
    • Orchestrations
    • PipelineComponents
    • Helpers – Components
    • And many other options

These options can be read and populated by fetching project names or file’s extension from solution file.

For ex – If Solution contains projects with name as “Schemas” or files with extension “.xsd” then make IncludeSchemas as True.

Similarly, for Maps, Orchestrations, PipelineComponents etc. can be deduced either from name( if naming convention is followed for BizTalk projects) or file extensions like .btm for maps, .odx for orchestrations, .btp for pipelines etc.

Sample BTDF Template -
    <!-- Deploy schemas -->
    <IncludeSchemas>True</IncludeSchemas>
    <!-- Deploy orchestrations -->
    <IncludeOrchestrations>True</IncludeOrchestrations>
    <!-- Deploy maps -->
    <IncludeTransforms>True</IncludeTransforms>

Many sections like below which are mostly constant can be updated from App.Config file.

    <!-- Deploy custom functoids -->
    <IncludeCustomFunctoids>False</IncludeCustomFunctoids>
    <!-- Deploy BRE -->
    <IncludeVocabAndRules>False</IncludeVocabAndRules>
    <!-- Deploy IIS virtual directories -->
    <IncludeVirtualDirectories>False</IncludeVirtualDirectories>
    <!-- Deploy NUnit tests -->
    <IncludeDeploymentTest>False</IncludeDeploymentTest>
    <!-- Deploy Log4Net configuration file named $(ProjectName).log4net -->
    <Includelog4net>False</Includelog4net>

This tool has helped us in saving thousands of hours monotonous, repetitive and boring work.

Hope it helps you as well.

Feel free to get in touch in case of any query. Thanks.

Contact Me:- 

@Gmail@Facebook , @Twitter, @LinkedIn @MSDNTechnet, @My Personal Blog 

Related Article –

PLANNING MIGRATION TO BIZTALK 2020 – WHY AND HOW?

https://prashantbiztalkblogs.wordpress.com/2020/08/28/planning-migration-to-biztalk-2020-why-and-how/

Contact Me –

Check out my other blogs –

Microsoft Teams Connector in Logic Apps

Recently, I ran into Microsoft Teams connector in Logic Apps and explored its few offerings – Actions/Triggers. It opens a host of new possibilities to be used in numerous use cases. One scenario, I was able to think of immediately is to notify Support Teams of possible issues and provide details if something goes wrong.…

Logic Apps Secure SQL Connection String in Azure Key Vault

While using SQL Connector in Logic Apps we don’t get a straightforward way to fetch/store connection string in Key Vault or in config. By default, the connection string we provide goes and sits in the Configurations as shown in the pics below and is in plaintext format. The regular process to use SQL Connector -…

Planning Migration to BizTalk 2020 – Why and how?

Watch the Video – Migration to BizTalk 2020 – Why and How?

As BizTalk 2020 is out most of the customers have either started migration to planning to migrate.

In this short blog, I will try to give a broader picture of planning BizTalk Migration and answer most basic questions.

So, let’s start with the most mammoth question first –

Question 1 – Why to Migrate?

There are many reasons to Migrate and can be broadly summarized as below –

  1. Avail Microsoft Support
    • BizTalk 2016 Mainstream Support Ends – 1st November 2022
    • All other previous versions like 2013R2, 2013 or 2010 etc. are long out of mainstream support
    • Upgrade to latest software version of OS, VS, SQL & BizTalk
  2. Enhance BizTalk Environment
    • For better infrastructure, performance, topology – HA/DR
  3. New features
    • BizTalk 2020 is loaded with new features of Hybrid Integration, Improvised Security (TLS1.2, GDPR compliance), Better Monitoring etc.
    • Feature packs – Get new features faster, without waiting for new versions
    • New Features for Developers and Administrators
  4. Move to Cloud
    • Ability to run on Azure VM as IaaS

Question 2 – What are my Migration Options?

a) Migrate to Azure –

Migration from BizTalk to Logic Apps/Azure Integration Services –

FeaturesBizTalk ServerAzureCapability/Description
Message RoutingBizTalk MessageBoxAzure Service Bus Queues & Topics, Event GridUse Topics and subscriptions, Event-driven approach
SchemasBizTalk SchemasLogic Apps along with Integration AccountSchemas can be uploaded to Integration Accounts or converted to JSON Schema and used
MappingBizTalk Mapper, XSLT, C#Azure Integration AccountUpload map(XSL file) to integration account
PipelinesBizTalk Pipeline and pipeline componentsLogic Apps along with Function AppLogic App for workflow and Function App for pipeline component logic
AdaptersOut-of-the-box adapters (application and protocol)Logic AppConnectors
OrchestrationBizTalk OrchestrationLogic AppDefine Workflow definition
RulesBizTalk Rule Engine (BRE)Function AppCreate custom Azure Functions to perform BRE like operations
BAMBizTalk Activity MonitoringLogic App, OMSTracked-properties, OMS portal. Specially Log Analytics for Logic Apps is very helpful
EDI Parties, Aggreement, PartnersBizTalk EDIAzure B2B integration using Logic Apps and Integration AccountUse Integration accounts to manage EDI Parties, Aggreements, Partners, upload certificates, maps, schemas etc.
API (REST, SOAP)Bindings in adapters (WCF)API ManagementAPI Management offers OpenAPI Swagger, WSDL and many other API related functionalities. API Logic can be written in Logic Apps and Function App
OperationsBizTalk Admin ConsoleAzure Portal, OMS, ARM-templates, PowerShell, CLIManaging Azure Services
MonitoringBizTalk Health MonitorAzure Monitor, Application InsightsAuzre OMS offers complete monitoring solution. Can also use Serverless360.
ArchivingBizTalk databases (Archive)Azure Storage, Application Insights, Log AnalysisLogging & Tracking

Refer below articles for more detail –

Migration BizTalk to Azure

https://www.serverless360.com/blog/migrating-biztalk-to-azure

b) Migrate to BizTalk Server 2020 –

  • In-Place Upgrade
  • Migrate to New Environment
    • As-Is Migration
    • BizTalk Server Migration Tool
  • Migrate and Improve
    • Traditional Migration of every solution
    • Possible enhancement of code, removing/replacing deprecated items

Let’s understand these options in detail –

In-Place Upgrade

Upgrading of the existing BizTalk Server 2016 Infrastructure to BizTalk Server 2020(In House Upgrade).

In-Place Upgrade Pros & Cons –

Advantages –

  • Extremely minimal efforts and time to upgrade
  • One click upgrade of BizTalk within the existing infrastructure.

Disadvantages –

  • Can only be done with N-1 Version
  • High Risk of Failure – Big stack to upgrade, must be compatible
    • OS, SQL, VS, BizTalk, 3rd Party softwares etc.
  • As-Is Upgrade – No change to infrastructure or code.
  • Code changes later will require upgrade to new VS, rebuild etc.
  • This approach is only suitable for small businesses with simpler integration

Migration – Parallel New Setup

Advantages –

  • Migration can be done in phases
  • Can scale up infrastructure
  • No downtime as we will have parallel environments
  • Can do the enhancements like replace deprecated features, code enhancements etc.

Disadvantages –

  • Requires a lot of efforts as all the applications need to be rebuilt, deployed and tested
  • Time taking, costly and lots of monotonous activities

Question 3 – I chose Migration to parallel environment. Can it be automated?

Option 1 – Migration Options – BizTalk Migration Tool(MSIT)

Microsoft’s internal team has provided a tool which assists heavily in as-is migration.

Refer https://docs.microsoft.com/en-in/archive/blogs/biztalk_server_team_blog/announcing-biztalk-server-migration-tool

Migration Tool Features –

Traditional Migration –

High Level Steps involved –

  • Recreate BizTalk Servers 2020 environment with desired (supported) Hardware & Software’s.
  • Application Level Migration
    • Uprade to Latest Visual Studio
    • Change of .Net Framework
    • Incorporate references and 3rd Party Softwares
    • Rebuild Code
    • End to End Testing
  • Possible Enhancements if any

Question 4 – Can we automate BizTalk Traditional Migration?

Yes, you can create One click Upgrade & Migration of thousands of BizTalk Solutions following below steps –

Major Operations to Automate –

  • Upgrade to Latest Visual Studio using Command line –

devenv {SolutionFile|ProjectFile} /Upgrade [/Out OutputFilename]

  • Rebuild Solutions – Use VS command line –

devenv SolutionName /Rebuild [SolnConfigName [/Project ProjName [/ProjectConfig ProjConfigName]] [/Out OutputFilename]]

  • Change DotNet Framework Version for all Projects – Specially helpful for class libraries – Pipeline components, custom functoids, custom adapters, Helpers

Replace TargetFrameworkVersion section in project files (.btpproj) with target framework

<TargetFrameworkVersion>DotNetTargetFramework</TargetFrameworkVersion>

  • Add BTDF Project or BizTalk Deployment project to solution

Use a template Deployment Folder with all the required files, update it with proper values at runtime

Refer below article for detailed implementation –

BIZTALK AUTOMATED MIGRATION

https://prashantbiztalkblogs.wordpress.com/2020/08/28/biztalk-automated-migration/

Question 5 – What’s New in BizTalk 2020?

I’m sure many of you must be already aware of the new features, so I’m bringing this topic in the end. It’s better to refer MS Official articles for more detail. I will just provide a brief summary.

Refer – What’s New in BizTalk Server 2020

https://docs.microsoft.com/en-us/biztalk/install-and-config-guides/whats-new-in-biztalk-server-2020

Support for newer platforms –

  • Visual Studio 2019
  • Windows Server 2019, Windows Server 2016, Windows 10
  • SQL Server 2019, SQL Server 2017, SQL Server 2016 SP2
  • DotNet Framework 4.7.2 or above
  • Office 2019, Office 2016

Hardware and Software Requirements for BizTalk Server 2020

New Office 365 Adapters – SMTP & POP3 Adapters are deprecated and replaced by Office 365 Adapters
– Adapter for Mail – Send and receive messages using Office 365 e-mail
– Adapter for Schedule – Set and update appointments using Office 365 schedules
– Adapter for Contact – Define people and groups using Office 365 contacts

Note – A new BizTalk TMS service will run in BizTalk application server, which will be responsible to get token for every office 365 account and cache it.

Better Integration with Azure – Hybrid Integration
– Logic Apps Adapter
– Service Bus Adapter
– Event Hubs Adapter
– Integration with API Management
– Publish Orchestration/Schema endpoints via APIM
– Publish APIs to external, partner & internal developers to unlock the potential of their data and services
– Adapter for Blob Storage
– Read and write binary files in Azure Blob Storage

Deployment and Administration Enhancements –

  • Design and Deploy with VSTS
    • Aplication Lifecycle Management to deploy and update BizTalk Server instances using Visual Studio Team System (VSTS)
  • Backup to Blob Storage
    • Backup BizTalk Server database job log files to Azure Blob Storage
  • Advanced Scheduling
    • Set up recurrence on BizTalk Server receive locations with greater precision using time zones, months, weeks, and days

Security Enhancements –

  • Transport Layer Security 1.2
    • Securely deploy BizTalk Server using TLS 1.2 authentication and encryption
  • SQL Server Encrypted Data
    • Read and write to SQL Server always encrypted columns using BizTalk Adapter for SQL Server

Enhanced Administration

  • REST APIs
    • Manage BizTalk environments using standard RESTful Web services APIs. Can create BizTalk 360 like portal yourself.
  • Read-only Operator Role
    • Facilitate dev ops model, provide production stamp without ability to update anything
  • Group Managed Service Accounts
    • Extend windows GMSA support to BizTalk operations and services
    • No need to change passwords anymore with GMSA
  • Audit Log
    • Making BizTalk further secure by maintaining audit trails of all management operation – One of the most important feature for auditing

Enhanced Analytics and Reporting

  • Application Insights
    • Send BizTalk tracking data to Application Insights for extensible analytics – understand Trends on usage, performance, custom business metrics
  • Event Hubs
    • Send BizTalk Server tracking data to Event Hubs
  • Power BI
    • Analyze & report BizTalk tracking data using Power BI. Use operational data templates for visualizations and dashboards

Few Other Important Enhancements –

  • XSLT 3.0 Support
    • Out of box wiring to work with Saxon XSLT3.0
  • SQL Availability Group support for BAM DTS Package via SSIS Catalog
    • Helpful for HA of BAM DBs
  • Partially disabled receive locations
    • Allows Fault Tolerance
  • Throughput improvements for Dynamic Send Ports with Ordered Delivery
  • Support for SSO Affiliate applications in SFTP adapter

Deprecated Features

ProgramStatusReplacement
POP3 and SMTP adaptersDeprecatedOffice 365 adapters
Support for ACS authentication in adaptersRemovedSAS authentication
SOAP adapterDeprecatedWCF-BasicHttp Adapter
Old SQL adapterRemovedWCF-SQL Adapter
BPEL supportDeprecatedNone
JDE OneWorld adapterDeprecatedNone
OWC redistRemovedNone
BAM PortalDeprecatedNone
WCF-NetTcpRelay adapterDeprecatedNone
SamplesRemovedRemoved from BizTalk Server installation.

Hope this article helps you in you BizTalk Migration Planning.

Feel free to contact me in case of any query. Thanks.

Contact Me:- 

@Gmail@Facebook , @Twitter, @LinkedIn @MSDNTechnet, @My Personal Blog 

Related Blog – BizTalk Migration Tool Demo

BizTalk Automated migration Tool | Prashant BizTalk And Azure Integration Blogs (wordpress.com)

Contact Me –

Check out my other blogs –

Save Full File in SQL table Column (varbinary) and read the contents back in t

What if you needed to save full file as-is to a SQL Server table column. I ran into a similar scenario and had to save CSV Files in a table. For this we created a table with a column of type “varbinary” CREATE TABLE [dbo].[TableWithCompleteFiles]([SNo] [int] IDENTITY(1,1) NOT NULL,[FileName] varchar NULL,[FileContent] varbinary NULL) ON [PRIMARY]…

Export SQL Table content as CSV File to a File Location

Recently, I got a request to save the contents of the table to a File. Wroking Command – Above commands will fail with below error – Msg 15281, Level 16, State 1, Procedure xp_cmdshell, Line 1 [Batch Start Line 43]SQL Server blocked access to procedure ‘sys.xp_cmdshell’ of component ‘xp_cmdshell’ because this component is turned off…

SSMS – Export Table and its data as SCRIPT

Many times while exporting the data from SQL Server you might have felt the need to script data along with the table. This can be done in just 3 easy steps – Generate SQL Scripts with data – 2. Choose Objects – Tables, Stored Procedures etc. to be exported 3. In Set Scripting Options ->…



PROGRAMMATICALLY Change service account’s password for Windows Services, iis app pools and scheduled tasks – C#

Nowadays it’s becoming a very common practice to frequently (half-yearly or quarterly) change password for all AD Service Accounts. It’s done as part of password compliance security guidelines, specially post GDPR implementations.

Earlier for BizTalk environments we use to have “Never Expire Password” for service account and used it to configure BizTalk Services (like Host Instances, ENTSSO & RuleEngineUpdateService), IIS App Pools or SQL Server Services. We also used BizTalk Service Account to configure multiple custom utilities for maintenance purpose as Windows Scheduled Tasks or as Windows Service.

Changing password manually for all these services, scheduled tasks and App Pools is very cumbersome and risky task. If we fail miss or mistype password even at one place, it may cause account lock issue and bring complete environment at halt.

Note – This problem can be addressed in BizTalk 2020 by using Group Managed Service Accounts as they don’t require Passwords.

Refer – What’s New in BizTalk Server 2020

Group Managed Service AccountsExtend windows GMSA support to BizTalk operations and services.

Using Group Managed Service Account

To address this issue in, I have created Windows Application (works for all the version of BizTalk)

It takes three inputs –

  1. Servers (Comma Separated) => List of Servers where change is required. Ex – In a multi-server BizTalk Server, provide name of all the BizTalk App Servers
  2. Service Account => AD Account for which password needs to be changed
  3. New Password

Along with a Checkbox to select features which require change –

  1. Windows Services – Includes BizTalk Host Instances, ENTSSO, RuleEngineUpdateService, IIS and other custom services using this account
  2. IIS App Pools
  3. Scheduled Tasks – Configured with custom utilities

Along with a Checkbox to select features which require change –

  1. Windows Services– Includes BizTalk Host Instances, ENTSSO, RuleEngineUpdateService, IIS and other custom services using this account
  2. IIS App Pools
  3. Scheduled Tasks – Configured with custom utilities

Allowed Operations –

  1. Stop All Services – It acts as a “Pre-requisites/Prepare for change” step to bring down BizTalk Environment. It will STOP all the Host Instances – irrespective of service account, ENTSSO, RuleEngineUpdateService and IIS.
  2. Get List – It retrieves a list of all the services, app pools and scheduled task which are configured with given service account.
  3. Change Password – It changes password for all the services, app pools and scheduled task configured with given service account.
  4. START all Services – It acts as post change activity to bring BizTalk online by starting all the Host Instances, ENTSSO, RuleEngineUpdateService and IIS

You can choose to perform password change activity on all or any – Services, App Pools and Windows Scheduled Tasks.

Code Explanation –

  1. Windows Services – Getting and Updating password for the list of services configured for given service account.

I have used WMI Queries –

SELECT * FROM Win32_Service where StartName like '%<Service Account Name>%'

Sample Code to get the list –

string strWMINamespace = @"\\" + strServer + @"\root\cimv2";
                if (strServiceAccount.Contains('\\'))
                {
                    //If service account contains backslash it needs to be replaced by double backslash
                    strServiceAccount = strServiceAccount.Replace(@"\", @"\\");
                }
                //SELECT * FROM Win32_Service where StartName like '%<Service Account Name>%'
                //Yields BizTalk Host Instances and ENTSSO
                string strWMIQuery = @"SELECT * FROM Win32_Service where StartName like '%" + strServiceAccount + "%'";
                //Create EnumerationOptions and run wql query  
                EnumerationOptions enumOptions = new EnumerationOptions();
                enumOptions.ReturnImmediately = false;
                using (ManagementObjectSearcher searchObject = new ManagementObjectSearcher(strWMINamespace, strWMIQuery, enumOptions))
                {
                    //Enumerate through the result set and stop if not stopped and Change Password  
                    foreach (ManagementObject service in searchObject.Get())
                    {
                     //Iterate over list of services and perform operation
                     //Updating the Password, refer - https://morgantechspace.com/2015/03/csharp-change-service-account-username-and-password.html#wmi
                     //Creating account object
                     object[] accountParams = new object[11];
                     //Updating Username for testing
                     //accountParams[6] = testUserName;
                     accountParams[7] = strNewPassword;
                     uint returnCode = (uint)service.InvokeMethod("Change", accountParams);
                     if (returnCode == 0)
                       {
                         updateLogs("Password changed successfully for service - " + service["Name"] + "");
                       }
                    }
                }

2. IIS App Pools => Getting and updating password for list of App Pools configured using given service account.

 using (ServerManager IISServerManager = ServerManager.OpenRemote(strServer))
 {
   foreach (ApplicationPool appPool in IISServerManager.ApplicationPools)
    {
      if (appPool.ProcessModel.UserName.ToLower().Contains(strServiceAccount))
      {
         updateLogs("Attempting to change password for App Pool - " + appPool.Name);
         appPool.ProcessModel.IdentityType = ProcessModelIdentityType.SpecificUser;
         //Updating Username for testing
         //appPool.ProcessModel.UserName = testUserName;
         appPool.ProcessModel.Password = strNewPassword;
         boolRequiresCommit = true;
       }
    } 
    if (boolRequiresCommit)
     {
         IISServerManager.CommitChanges();
         updateLogs("Password changed successfully for App Pools");
     }
 }

3. Windows Scheduled Tasks => Getting and updating password for list of Windows Scheduled Tasks configured using given service account.

Microsoft.Win32.TaskScheduler.TaskService taskService = new TaskService(strServer);
foreach (Microsoft.Win32.TaskScheduler.Task task 
in taskService.RootFolder.GetTasks().Where
                    (t => t.Definition.Principal.UserId.ToString().ToLower().Contains
                          (strServiceAccount.ToLower())))
{
     //Disabling each task if it's enabled
     if (task.State != TaskState.Disabled)
       {
         task.Stop();
         task.Enabled = false;
       }
string strRunAsCMD = @"/C schtasks.exe /CHANGE /S " + strServer + " /TN \"" + task.Name + "\" /RU " + strServiceAccount + " /RP " + strNewPassword + "";
    updateLogs("Attempting to change password for task - " + task.Name + "");
    executeCommand(strRunAsCMD);
    updateLogs("Password changed successfully for task - " + task.Name + "");
}
 
//Code to Execute command in Windows Command Prompt                
private void executeCommand(string strRunAsCMD)
{
    var processInfo = new ProcessStartInfo("cmd.exe", strRunAsCMD);
    processInfo.CreateNoWindow = true;
    processInfo.UseShellExecute = false;
    processInfo.RedirectStandardError = true;
    processInfo.RedirectStandardOutput = true;
    processInfo.Verb = "runas";

    var process = Process.Start(processInfo);

    string strProcessOutput = process.StandardOutput.ReadToEnd();
    string strProcessError = process.StandardError.ReadToEnd();

    string logMsg = "executeCommand - Command Execution Completed with Exit Code : " +               process.ExitCode.ToString() + Environment.NewLine;
   if (String.IsNullOrEmpty(strProcessError))
    {
      logMsg += "Process Output - " + strProcessOutput;
    }
    else
    {
       logMsg += "Process Error - " + strProcessError;
    }
    updateLogs(logMsg);

    process.WaitForExit();
    //Checking if the process execution failed
    if (process.ExitCode != 0)
    {
       updateLogs("executeCommand - Error occurred in execution." + Environment.NewLine + "Exit Code - " + process.ExitCode + Environment.NewLine +
                           "Process Error Output - " + strProcessError);
     }

 process.Close();
}

4. Starting/Stopping Host Instances

private async System.Threading.Tasks.Task stopOrStartAllHostInstances(HostAction hostAction)
        {
            try
            {
                //Create EnumerationOptions and run wql query  
                System.Management.EnumerationOptions enumOptions = new System.Management.EnumerationOptions();
                enumOptions.ReturnImmediately = false;

                //Search for all HostInstances of 'InProcess' type in the Biztalk namespace scope  
                System.Management.ManagementObjectSearcher searchObject =
                    new System.Management.ManagementObjectSearcher("root\\MicrosoftBizTalkServer",
                    "Select * from MSBTS_HostInstance Where HostType=1 And IsDisabled=False", enumOptions);

                //Enumerate through the result set and stop each HostInstance if it is running  
                foreach (ManagementObject inst in searchObject.Get())
                {
                    Task<string> task = new Task<string>(() => stopOrStartHostInstance(inst, hostAction));
                    task.Start();
                    updateLogs("Attempting to perform " + hostAction + " on HostInstance of Host: " + inst["HostName"] + " on Server: " + inst["RunningServer"] + "." + Environment.NewLine);
                    string status = await task;
                    updateLogs("Successfully performed " + hostAction + " action on " + inst["HostName"] + " on Server: " + inst["RunningServer"] + "." + Environment.NewLine + Environment.NewLine);
                }
            }
            catch (Exception excep)
            {
                string strExceptionMessage = "Exception occurred in function-stopOrStartAllHostInstances. Message - " + excep.Message + Environment.NewLine + "StackTrace - " + excep.StackTrace;
                throw new Exception(strExceptionMessage);
            }
        }

private string stopOrStartHostInstance(ManagementObject inst, HostAction hostAction)
        {
            try
            {
                if (hostAction == HostAction.STOP)
                {
                    //Check if ServiceState is 'Started'  
                    if (inst["ServiceState"].ToString() == "4")
                    { inst.InvokeMethod("Stop", null); }
                }
                if (hostAction == HostAction.START)
                {
                    if (inst["ServiceState"].ToString() == "1")//Status Stopped
                    {
                        inst.InvokeMethod("Start", null);
                    }
                }
                return "Success";
            }
            catch (Exception excep)
            {
                string strExceptionMessage = "Exception occurred in Function - stopOrStartHostInstance. Message - " + excep.Message + Environment.NewLine + "StackTrace - " + excep.StackTrace;
                throw new Exception(strExceptionMessage);
            }
        }

5. Starting/Stopping Services

private async System.Threading.Tasks.Task stopORStartAdditionalServices(string strServerNames, ServiceAction serviceAction)
        {
            try
            {
                foreach (string strServerName in strServerNames.Split(','))
                {
                    ServiceController[] scServices = ServiceController.GetServices(strServerName);
                    foreach (ServiceController service in scServices)
                    {
                        //Stopping/Starting IIS, RULEEngineUpdateService and ENTSSO services
                        if (service.ServiceName.ToUpper().Contains("W3SVC") || service.ServiceName.ToUpper().Contains("RULEENGINEUPDATESERVICE") || service.ServiceName.ToUpper().Contains("ENTSSO"))
                        {
                            Task<string> task = new Task<string>(() => stopOrStartService(service, serviceAction));
                            task.Start();
                            updateLogs("Attempting to " + serviceAction + " - " + service.DisplayName + " On Server - " + strServerName + Environment.NewLine);
                            string result = await task;
                            updateLogs("Successfully performed Action - " + serviceAction + " on Service - " + service.DisplayName + " On Server - " + strServerName + Environment.NewLine);
                        }
                    }
                }
            }
            catch (Exception excep)
            {
                string strException = "Exception Occurred in function - stopORStartAdditionalServices. Message - " + excep.Message + Environment.NewLine + "Stack Trace - " + excep.StackTrace + Environment.NewLine;
                throw new Exception(strException);
            }
        }

private string stopOrStartService(ServiceController service, ServiceAction serviceAction)
        {
            try
            {
                if ((serviceAction == ServiceAction.STOP) && (service.Status != ServiceControllerStatus.Stopped))
                {
                    //stopping the service 
                    service.Stop();
                }
                else if ((serviceAction == ServiceAction.START) && (service.Status == ServiceControllerStatus.Stopped))
                {
                    //Starting the services
                    service.Start();
                }
                return "Success";
            }
            catch (Exception excep)
            {
                string strException = "Exception Occurred in function - stopOrStartService. Message - " + excep.Message + Environment.NewLine + "Stack Trace - " + excep.StackTrace + Environment.NewLine;
                throw new Exception(strException);
            }
        }

Note – Post this password change activity we started getting error for BAM Portal

Configuration Error –

Description: An error occurred during the processing of a configuration file required to service this request. Please review the specific error details below and modify your configuration file appropriately.

Parser Error Message: Could not create Windows user token from the credentials specified in the config file. Error from the operating system ‘The user name or password is incorrect.’

This error is bound to come and is explained in detail in below article –

https://prashantbiztalkblogs.wordpress.com/2020/07/14/bam-portal-error-could-not-create-windows-user-token-from-the-credentials-specified-in-the-config-file-error-from-the-operating-system-the-user-name-or-password-is-incorrect/

Hope it’s helpful.

Download the complete code from here

Contact Me:- 

@Gmail@Facebook , @Twitter, @LinkedIn @MSDNTechnet, @My Personal Blog 

Contact Me –

Check out my other blogs –

DSC Open Firewall Ports and Enable existing Firewall Rules

Recently I have been working with Powershell DSC to automate and configure Desired State of Windows Server Post Provisioning. Desired State Configuration (DSC) is a feature in PowerShell 4.0 and above that helps administrators to automate the configuration of Windows and Linux operating systems (OSes). DSC provides a set of PowerShell language extensions, cmdlets and…

Monitoring BizTalk Resources programmatically using C#

Hi friends many times as a BizTalk Administrator we fall into situations when we need to constantly monitor BizTalk Servers resources and services like Host Instances Status, any custom services like IIS, CPU usage, available disk space etc.

So, in this blog I have created a console application which can be scheduled to run continuously using Windows Scheduler. It will constantly monitor all resources and attempt to correct it and later drop an email to configured group or user (only if there is an issue).

Note – Proactive Monitoring ensures early resolutions, thus solving upcoming big future problems.

Currently I have focused on monitoring of below items –

1) Check ENTSSO, IIS And Other Custom Services
2) Enumerate And Start All Host Instances
3) Check If Host is in Hung State – “Start Pending” or “Stop Pending”
4) Check Available Drive Space
5) Check CPU Reading
6) Check All Suspended and Custom Orchestration Suspended instance count
7) Check Spool Count, Parts Count and row count for other tables if it is more than the configured value
8) Check BizTalkMsgBox Table Index Fragmentation

In multi-server BizTalk environment this tool needs to be scheduled in only in one environment and provide all other server names of this group in configuration file – “ServerNames” as shown below. It will remotely query all the servers.

<add key=”ServerNames” value=”BTSServer1;BTSServer2″/>

1) Check ENTSSO IIS And Other CustomServices –

In this function we can monitor important Services like ENTSSO, IIS(World Wide Web Publishing Service – w3svc) or any other custom service. You just need to mention these services in config file as shown below –

<add key=”AdditionalServicesToBeMonitored” value=”ENTSSO;W3SVC;RTI”/>

I have used “ServiceController” class to fetch list of all the services, check its status and finally start the same.

ServiceController[] scServices = ServiceController.GetServices(strServerName);

foreach (ServiceController service in scServices)
 {
 if ((service.ServiceName.ToUpper().Contains(strServiceName)) && service.Status != ServiceControllerStatus.Running)
 {
service.Start();}
 }

2) Enumerate And Start Host Instances –

In this function we are enumerating all the In-Process Host Instances using WMI Query and starting them.

Sample code -

//Create EnumerationOptions and run wql query 
 EnumerationOptions enumOptions = new EnumerationOptions();
 enumOptions.ReturnImmediately = false;

//Search for all HostInstances of 'InProcess' type in the Biztalk namespace scope 
 ManagementObjectSearcher searchObject = new ManagementObjectSearcher("root\\MicrosoftBizTalkServer", "Select * from MSBTS_HostInstance Where HostType=1 And IsDisabled=False", enumOptions);


//Enumerate through the result set and start each HostInstance if it is already stopped 
 foreach (ManagementObject inst in searchObject.Get())
 {
 //Check if ServiceState is 'Stopped' 
 if (inst["ServiceState"].ToString() == "1")
 { inst.InvokeMethod("Start", null); }}

3) Check If Host is in “Start Pending” or “Stop Pending” State

Many times BizTalk Host Instances go in Hung State like “Stop Pending” or “Start Pending” and if you leave these processes for days they won’t come up. So, the only option left is to Terminate(End Process Tree) using Task Manager and start the host again. I totally agree that it’s not the best way as the service will not exit gracefully but in production environment we are left with very little option.

Here also I am using WMI queries to find if the Host is in Hung State i.e. Start or Stop Pending state.

 //Search for all HostInstances of 'InProcess' type in the Biztalk namespace scope which is not Disabled and have Service State as 2(Start Pending) or 3(Stop Pending) 
 ManagementObjectSearcher searchObject = new ManagementObjectSearcher("root\\MicrosoftBizTalkServer", "Select * from MSBTS_HostInstance Where HostType=1 And IsDisabled=False and (ServiceState = 2 or ServiceState = 3)", enumOptions);

After waiting for some configured milliseconds I am performing again a WMI Query to check if the Host Status is changed or not. Because this issue occurs very rarely and mostly “Start Pending” or “Stop Pending” state will last for very less time when you have performed Start or Stop of Host Instances.

//Query again to check if the status is still the same i.e. Stopped/Start Pending
 ManagementObjectSearcher searchObject1 = new ManagementObjectSearcher("root\\MicrosoftBizTalkServer", "Select * from MSBTS_HostInstance Where HostName='" + inst["HostName"].ToString() + "' and (ServiceState = 2 or ServiceState = 3)", enumOptions);

Once if it’s confirmed that Host is hung we will go ahead and Terminate it forcibly along with all other processes with same parentID.

 var connectionOptions = new ConnectionOptions();
 ManagementScope scope = new ManagementScope(@"\\" + strServerName + @"\root\cimv2", connectionOptions);
 scope.Connect();
 //Get Process ID of the service. BizTalk Host Instance Name is always like BTSSvc$ like BTSSvc$BizTalkServerApplication
 var query = new SelectQuery("select ProcessID from Win32_Service where Name ='BTSSvc$" + strHostName + "'");
 using (var searcher = new ManagementObjectSearcher(scope, query))
 {
 foreach (ManagementObject obj in searcher.Get())
 {
 uint processId = (uint)obj["ProcessId"];
 //Hard Terminate(Terminate Related Processes) - Loop through all the processes on the machine and Kill it if it's Parent Process ID or ProcessID matches the Service Process ID
 var parentIdQuery = new SelectQuery("Select * from Win32_process");
 using (var vsearcher = new ManagementObjectSearcher(scope, parentIdQuery))
 { 
foreach (ManagementObject process in vsearcher.Get()) {
 //Checking if Parent Process ID or ProcessID is same as Service ProcessID
 if (processId == (uint)process["ParentProcessID"] || processId == (uint)process["ProcessID"])
 { process.InvokeMethod("Terminate", null); } } } } }

4) Check Available Drive Space – 

Here too I am using WMI query to check Available Disk Space. This query will be performed remotely on all the servers.

For remotely connecting WMI Query, namespace will be  –

string strNameSpace = @”\\” + strServerName + @”\root\cimv2″;

Query – select FreeSpace,Size,Name from Win32_LogicalDisk where DriveType=3″, enumOptions

string strNameSpace = @"\\" + strServerName + @"\root\cimv2";
 //Get Fixed disk state. Check - http://www.csidata.com/custserv/onlinehelp/vbsdocs/vbs41.htm, DriveType = 2 - Fixed, Drive has fixed (nonremovable) media. This includes all hard drives, including hard drives that are removable. DriveType = 2 -Remote, Network drives. This includes drives shared anywhere on a network.
 ManagementObjectSearcher searchObject = new ManagementObjectSearcher(strNameSpace, "select FreeSpace,Size,Name from Win32_LogicalDisk where DriveType=3", enumOptions);

5) Check CPU Reading – 

I am using Performance Counter – % Processor Time. Here we Sleep the thread to achieve average value for configured time.

PerformanceCounter totalProcessorTimeCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total", strServerName);
 totalProcessorTimeCounter.NextValue();
 System.Threading.Thread.Sleep(intThreadSleepTimeInMilliSeconds);// seconds wait
 double CPUPercentage = totalProcessorTimeCounter.NextValue();
 if (CPUPercentage > doubleAcceptableCPUPercentage)

6) Check All Suspended and Custom Orchestration Suspended instance count

Here also I am using WMI query to fetch total suspended instances – ServiceClass=1(Orchestration) or ServiceClass=4(Messaging) and (ServiceStatus=4(Suspended (resumable)) or ServiceStatus=32(Suspended (non-resumable)) in the BizTalk namespace

//Search for all Service Instances for ServiceClass=1(Orchestration) or ServiceClass=4(Messaging) and (ServiceStatus=4(Suspended (resumable)) or ServiceStatus=32(Suspended (non-resumable)) in the Biztalk namespace scope 
 ManagementObjectSearcher searchObject = new ManagementObjectSearcher("root\\MicrosoftBizTalkServer", "Select * from MSBTS_ServiceInstance Where (ServiceClass=1 or ServiceClass=4) and (ServiceStatus=4 or ServiceStatus=32)", enumOptions);

It can be configured to get Suspended Instance for individual Orchestrations as well, which can be configured in app.config file.

WQL Query – string.Format(“SELECT * FROM MSBTS_ServiceInstance WHERE (ServiceClass=1 or ServiceClass=4) and (ServiceStatus=4 or ServiceStatus=32) AND ServiceName like \”%{0}%\””, strOrchName);

 //Now checking for individual Orchestrations Only If Suspended Instance Count not zero
 int count = 1;
 foreach (string strOrchName in strOrchestrationNames.Split(';'))
 {
 string strWQL = string.Format("SELECT * FROM MSBTS_ServiceInstance WHERE (ServiceClass=1 or ServiceClass=4) and (ServiceStatus=4 or ServiceStatus=32) AND ServiceName like \"%{0}%\"", strOrchName);

7) Spool Count, Parts Count and other tables with Row Count if more than configured value –

Performing SQL Query on BizTalk Message box to get top 10 tables with row count more that the acceptable configured value. This information can be used to proactively prevent throttling and avoid slowness of BizTalk message processing.

//Checking top 10 tables with Row count more than intAcceptableMsgBoxRowCount
string queryString = “SELECT top 10 T.name TableName, I.Rows NumberOfRows FROM sys.tables T JOIN sys.sysindexes I ON T.OBJECT_ID = I.ID “
+ “WHERE indid IN (0,1) and I.Rows > ” + intAcceptableMsgBoxRowCount.ToString() + “ORDER BY I.Rows DESC,T.name”;
8) Check BizTalkMsgBox Table Index Fragmentation –

To check Fragmentation I am using below query. It checks if Fragmentation is more than the configured acceptable value. In case of high Fragmentation you will have to Rebuild Indexes to improve performance. Execute below stored procedures during down time.

1.bts_RebuildIndexes —-   for BizTalkMsgBoxDb database
2.dtasp_RebuildIndexes—  for BizTalkDTADb database

//Checking Index Fragmentation if the value is more than Acceptable Fragmentation Percentage, Selecting top 10 values
“SELECT top 10 dbtables.[name] as ‘Table’, dbindexes.[name] as ‘Index’, indexstats.avg_fragmentation_in_percent, indexstats.page_count

FROM sys.dm_db_index_physical_stats (DB_ID(), NULL, NULL, NULL, NULL) AS indexstats INNER JOIN sys.tables dbtables on dbtables.[object_id] = indexstats.[object_id] INNER JOIN sys.schemas dbschemas on dbtables.[schema_id] = dbschemas.[schema_id] INNER JOIN sys.indexes AS dbindexes ON dbindexes.[object_id] = indexstats.[object_id] AND indexstats.index_id = dbindexes.index_id

WHERE indexstats.database_id = DB_ID()and avg_fragmentation_in_percent > ” + doubleAcceptableFragmentationPercentage.ToString() + ” “;

ORDER BY indexstats.avg_fragmentation_in_percent desc”;

Complete Solution Code can be found here

App.config file - 

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
 <startup> 
 <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
 </startup>
 <appSettings>
 <!--BizTalk Message Box Connection String-->
 <add key="BizTalkMsgBoxConnectionString" value="Data Source=WIN-4ACT86KFN44;Initial Catalog=BizTalkMsgBoxDb;Integrated Security=true;Pooling=false"/>
 
 <!--SMTP Details for Sending Emails-->
 <add key="IsEmailRequired" value="true"/>
 <add key="SMTPHost" value="smtp.gmail.com"/>
 <add key="FROMEmailID" value="youremailid@gmail.com"/>
 <add key="TOEmailID" value="toemailid@gmail.com,toemailid2@microsoft.com"/>
 <add key="SMTPServerPort" value="587"/>
 <!--<add key="Username" value="youremailid@gmail.com"/>
 <add key="Password" value="yourpassword"/>-->
 <add key="Subject" value="Monitoring BizTalk Resources"/>

<add key="ServerNames" value="WIN-4ACT86KFN44"/>
 <add key="AdditionalServicesToBeMonitored" value="ENTSSO;W3SVC;RTI"/>
 <add key ="AcceptableAvailableDriveFreeSpace" value="80"/>
 <add key="AcceptableTotalSuspendedInstanceCount" value="1"/>
 <add key="OrchestrationNames" value="ProcessCandidateAgeIfno;ProcessOrders;DummyOrchName"/>
 <add key="AcceptableSuspendedInstanceCountForAnOrch" value="1"/>
 <add key="AcceptableCPUPercentage" value="1"/>
 <add key="AcceptableMsgBoxRowCount" value="1"/> 
 <add key="AcceptableFragmentationPercentage" value="1"/>
 <add key ="ThreadSleepTimeInMilliSeconds" value="5000"/>
 </appSettings>
</configuration>

C# Code - 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Management;
using System.Net.Mail;
using System.Configuration;
using System.ServiceProcess;
using System.IO;
using System.Diagnostics;
using System.Data.SqlClient;

namespace MonitoringBizTalkResources
{
 class MonitoringBizTalkResources
 {
 static void Main(string[] args)
 {
 #region Get Values from Config File

string strEmailBody = "";
 var appSettings = ConfigurationManager.AppSettings;

//Get Server Names for checking services
 string strServerNames = "";
 if (appSettings["ServerNames"] != null && !String.IsNullOrEmpty(appSettings["ServerNames"].ToString()))
 { strServerNames = Convert.ToString(appSettings["ServerNames"].ToString()); }

//Getting Additional Services To Be Monitored
 string strAdditionalServicesToBeMonitored = "";
 if (appSettings["AdditionalServicesToBeMonitored"] != null && !String.IsNullOrEmpty(appSettings["AdditionalServicesToBeMonitored"].ToString()))
 { strAdditionalServicesToBeMonitored = Convert.ToString(appSettings["AdditionalServicesToBeMonitored"].ToString()); }




//Getting connection string from Config File
 string strConnectionString = "";
 if (appSettings["BizTalkMsgBoxConnectionString"] != null && !String.IsNullOrEmpty(appSettings["BizTalkMsgBoxConnectionString"].ToString()))
 { strConnectionString = Convert.ToString(appSettings["BizTalkMsgBoxConnectionString"].ToString()); }

//If value of IsEmailRequired field is present in config file will populate the same else will keep it as default = false
 bool IsEmailRequired = false;
 if (appSettings["IsEmailRequired"] != null && !String.IsNullOrEmpty(appSettings["IsEmailRequired"].ToString()))
 { IsEmailRequired = Convert.ToBoolean(appSettings["IsEmailRequired"].ToString()); }

//Get ThreadSleepTimeInMilliSeconds
 int intThreadSleepTimeInMilliSeconds = 5000;
 if (appSettings["ThreadSleepTimeInMilliSeconds"] != null && !String.IsNullOrEmpty(appSettings["ThreadSleepTimeInMilliSeconds"].ToString()))
 { intThreadSleepTimeInMilliSeconds = Convert.ToInt32(appSettings["ThreadSleepTimeInMilliSeconds"].ToString()); }

//If value of AcceptableAvailableDriveSpace field is present in config file will populate the same else will keep it as default
 double doubleAcceptableFreeDriveSpace = 80;
 if (appSettings["AcceptableAvailableDriveFreeSpace"] != null && !String.IsNullOrEmpty(appSettings["AcceptableAvailableDriveFreeSpace"].ToString()))
 { doubleAcceptableFreeDriveSpace = Convert.ToDouble(appSettings["AcceptableAvailableDriveFreeSpace"].ToString()); }

//If value of AcceptableTotalSuspendedInstanceCount field is present in config file will populate the same else will keep it as default
 int intAcceptableTotalSuspendedInstanceCount = 10000;
 if (appSettings["AcceptableTotalSuspendedInstanceCount"] != null && !String.IsNullOrEmpty(appSettings["AcceptableTotalSuspendedInstanceCount"].ToString()))
 { intAcceptableTotalSuspendedInstanceCount = Convert.ToInt32(appSettings["AcceptableTotalSuspendedInstanceCount"].ToString()); }

//Get Orchestration Names from Config
 string strOrchestrationNames = "";
 if (appSettings["OrchestrationNames"] != null && !String.IsNullOrEmpty(appSettings["OrchestrationNames"].ToString()))
 { strOrchestrationNames = appSettings["OrchestrationNames"].ToString(); }

//Get AcceptableSuspendedInstanceCountForAnOrch from config
 int intAcceptableSuspendedInstanceCountForAnOrch = 100;
 if (appSettings["AcceptableSuspendedInstanceCountForAnOrch"] != null && !String.IsNullOrEmpty(appSettings["AcceptableSuspendedInstanceCountForAnOrch"].ToString()))
 { intAcceptableSuspendedInstanceCountForAnOrch = Convert.ToInt32(appSettings["AcceptableSuspendedInstanceCountForAnOrch"].ToString()); }

//Get AcceptableCPUPercentage from Config
 double doubleAcceptableCPUPercentage = 80;
 if (appSettings["AcceptableCPUPercentage"] != null && !String.IsNullOrEmpty(appSettings["AcceptableCPUPercentage"].ToString()))
 { doubleAcceptableCPUPercentage = Convert.ToDouble(appSettings["AcceptableCPUPercentage"].ToString()); }

//Get AcceptableFragmentationPercentage from Config
 double doubleAcceptableFragmentationPercentage = 50;
 if (appSettings["AcceptableFragmentationPercentage"] != null && !String.IsNullOrEmpty(appSettings["AcceptableFragmentationPercentage"].ToString()))
 { doubleAcceptableFragmentationPercentage = Convert.ToDouble(appSettings["AcceptableFragmentationPercentage"].ToString()); }

//Get Acceptable MsgBox RowCount for a table from config
 int intAcceptableMsgBoxRowCount = 30000;
 if (appSettings["AcceptableMsgBoxRowCount"] != null && !String.IsNullOrEmpty(appSettings["AcceptableMsgBoxRowCount"].ToString()))
 { intAcceptableMsgBoxRowCount = Convert.ToInt32(appSettings["AcceptableMsgBoxRowCount"].ToString()); }

#endregion

try
 {
 //Check if Enterprise Single Sign-On Service or IIS is stopped
 checkENTSSOIISAndCustomServices(ref strEmailBody, ref strServerNames, ref strAdditionalServicesToBeMonitored);

//Check all the Host Instances
 enumerateAndStartHostInstances(ref strEmailBody);

//Check if Host is in Start Pending or Stop Pending State
 checkIfHostStartorStopPendingState(ref strEmailBody, ref intThreadSleepTimeInMilliSeconds);

//Check Drive Free Space 
 checkAvailableDriveSpace(ref strEmailBody, ref strServerNames, ref doubleAcceptableFreeDriveSpace);

//Check CPU Reading
 checkCPUReading(ref strEmailBody, ref strServerNames, ref doubleAcceptableCPUPercentage, ref intThreadSleepTimeInMilliSeconds);

//Check All Suspended Instance Count
 checkAllSuspendedInstances(ref strEmailBody, ref intAcceptableTotalSuspendedInstanceCount, ref strOrchestrationNames, ref intAcceptableSuspendedInstanceCountForAnOrch);

//Check Spool Count, Parts Count and other tables with Row Count more than intAcceptableMsgBoxRowCount
 checkBizTalkMsgBoxTablesCount(ref strEmailBody, ref strConnectionString, ref intAcceptableMsgBoxRowCount);

//Check BizTalkMsgBox Table Index Fragmentation
 checkBizTalkMsgBoxTablesIndexFragmentation(ref strEmailBody, ref strConnectionString, ref doubleAcceptableFragmentationPercentage);

if (IsEmailRequired && !String.IsNullOrEmpty(strEmailBody))
 {
 string strHTMLStyle = "";
 strEmailBody = "<html>"+strHTMLStyle+"<body>" + strEmailBody + "</body></html>";
 sendEmail(strEmailBody);
 }
 }
 catch (Exception excep)
 {
 string strExceptionMessage = "MonitoringBizTalkResource - An exception occurred. " + excep.Message;
 if (!String.IsNullOrEmpty(strEmailBody))
 strExceptionMessage = strExceptionMessage + Environment.NewLine + "Operations performed so far - " + strEmailBody;
 System.Diagnostics.EventLog.WriteEntry("BizTalk Server", strExceptionMessage, System.Diagnostics.EventLogEntryType.Error);
 }
 }
 //Function to check if any Host is in Stop Pending or Start Pending State
 private static void checkIfHostStartorStopPendingState(ref string strEmailBody, ref int intThreadSleepTimeInMilliSeconds)
 {
 try
 {
 //Create EnumerationOptions and run wql query 
 EnumerationOptions enumOptions = new EnumerationOptions();
 enumOptions.ReturnImmediately = false;

//Search for all HostInstances of 'InProcess' type in the Biztalk namespace scope which is not Disabled and have Service State as 2(Start Pending) or 3(Stop Pending) 
 ManagementObjectSearcher searchObject = new ManagementObjectSearcher("root\\MicrosoftBizTalkServer", "Select * from MSBTS_HostInstance Where HostType=1 And IsDisabled=False and (ServiceState = 2 or ServiceState = 3)", enumOptions);
 //ManagementObjectSearcher searchObject = new ManagementObjectSearcher("root\\MicrosoftBizTalkServer", "Select * from MSBTS_HostInstance Where HostType=1 And IsDisabled=False and (ServiceState = 1 or ServiceState = 4)", enumOptions); // Test Query for Stopped(1) and Running(4) State

int count = 1;
 //Enumerate through the result set
 foreach (ManagementObject inst in searchObject.Get())
 {
 if (count == 1) // Wait only for the first time
 {
 System.Threading.Thread.Sleep(intThreadSleepTimeInMilliSeconds);// seconds wait
 }

//Query again to check if the status is still the same i.e. Stopped/Start Pending
 ManagementObjectSearcher searchObject1 = new ManagementObjectSearcher("root\\MicrosoftBizTalkServer", "Select * from MSBTS_HostInstance Where HostName='" + inst["HostName"].ToString() + "' and (ServiceState = 2 or ServiceState = 3)", enumOptions);
 //ManagementObjectSearcher searchObject1 = new ManagementObjectSearcher("root\\MicrosoftBizTalkServer", "Select * from MSBTS_HostInstance Where HostName='" + inst["HostName"].ToString() + "' and (ServiceState = 1 or ServiceState = 4)", enumOptions); //Test Query for Stopped(1) and Running(4) State
 foreach (ManagementObject inst1 in searchObject1.Get())
 {
 //Terminate the Host Instance
 terminateHungProcess(inst1["RunningServer"].ToString(), inst1["HostName"].ToString());

//Start the Host Instance
 inst1.InvokeMethod("Start", null);
 //Draft Email Subject
 if (count == 1)
 {
 strEmailBody = strEmailBody + "Below Host Instances were in Stopped/Start Pending state, Terminated the process and attempted to Start<br>";
 strEmailBody = strEmailBody + "<table><tr><th>Server Name</th><th>Host Instance Name</th></tr>";
 ++count;
 }
 strEmailBody = strEmailBody + "" + inst1["RunningServer"] + "" + inst1["HostName"] + "";
 }
 }
 if (count > 1)
 { strEmailBody = strEmailBody + "</table>All above mentioned Host Instances have been Started Successfully<br><br>"; }
 }
 catch (Exception excep)
 {
 System.Diagnostics.EventLog.WriteEntry("BizTalk Server", "Exception Occurred in enumerateAndStartHostInstances fuction call. " + excep.Message, System.Diagnostics.EventLogEntryType.Error);
 throw new Exception("Exception Occurred in enumerateAndStartHostInstances fuction call. " + excep.Message);
 }
 }

//Function to Terminate a Process in Hung State and also terminate all related processes(Having same ParentPID)
 private static void terminateHungProcess(string strServerName, string strHostName)
 {
 try
 {
 var connectionOptions = new ConnectionOptions();
 ManagementScope scope = new ManagementScope(@"\\" + strServerName + @"\root\cimv2", connectionOptions);
 scope.Connect();
 //Get Process ID of the service. BizTalk Host Instance Name is always like BTSSvc
 
lt;HostName> like BTSSvc$BizTalkServerApplication  var query = new SelectQuery("select ProcessID from Win32_Service where Name ='BTSSvc$" + strHostName + "'");  using (var searcher = new ManagementObjectSearcher(scope, query))  {  foreach (ManagementObject obj in searcher.Get())  {  uint processId = (uint)obj["ProcessId"];  //Hard Terminate(Terminate Related Processes) - Loop through all the processes on the machine and Kill it if it's Parent Process ID or ProcessID matches the Service Process ID  var parentIdQuery = new SelectQuery("Select * from Win32_process");  using (var vsearcher = new ManagementObjectSearcher(scope, parentIdQuery))  {  foreach (ManagementObject process in vsearcher.Get())  {  //Checking if Parent Process ID or ProcessID is same as Service ProcessID  if (processId == (uint)process["ParentProcessID"] || processId == (uint)process["ProcessID"])  {  process.InvokeMethod("Terminate", null);  }  }  }  }  }  }  catch (Exception excep)  {  System.Diagnostics.EventLog.WriteEntry("BizTalk Server", "Exception Occurred in terminateHungProcess fuction call. " + excep.Message, System.Diagnostics.EventLogEntryType.Error);  throw new Exception("Exception Occurred in terminateHungProcess fuction call. " + excep.Message);  }  } //Function to check and start ENTSSO Service  private static void checkENTSSOIISAndCustomServices(ref string strEmailBody, ref string strServerNames, ref string strAdditionalServicesToBeMonitored)  {  try  {  int count = 1;  foreach (string strServerName in strServerNames.Split(';'))  {  ServiceController[] scServices = ServiceController.GetServices(strServerName);  foreach (string strServiceName in strAdditionalServicesToBeMonitored.Split(';'))  {  foreach (ServiceController service in scServices)  {  if ((service.ServiceName.ToUpper().Contains(strServiceName)) && service.Status != ServiceControllerStatus.Running)  {  if (count == 1)  {  strEmailBody = strEmailBody + "Below Services are not running, attempting to start.<br>";  strEmailBody = strEmailBody + "<table><tr><th>Server Name</th><th>Service Name</th></tr>";  ++count;  }  strEmailBody = strEmailBody + "" + strServerName + "" + service.DisplayName + "";  service.Start();  }  }  }  }  if (count > 1)  { strEmailBody = strEmailBody + "</table>All above mentioned Services have been Started Successfully<br><br>"; }  }  catch (Exception excep)  {  System.Diagnostics.EventLog.WriteEntry("BizTalk Server", "Exception Occurred in checkENTSSOIISAndCustomServices fuction call. " + excep.Message, System.Diagnostics.EventLogEntryType.Error);  throw new Exception("Exception Occurred in checkENTSSOServiceAndIIS fuction call. " + excep.Message);  }  } //Function to Enumerate all HostInstances of "InProcess" type and start them   private static void enumerateAndStartHostInstances(ref string strEmailBody)  {  try  {  //Create EnumerationOptions and run wql query   EnumerationOptions enumOptions = new EnumerationOptions();  enumOptions.ReturnImmediately = false; //Search for all HostInstances of 'InProcess' type in the Biztalk namespace scope   ManagementObjectSearcher searchObject = new ManagementObjectSearcher("root\\MicrosoftBizTalkServer", "Select * from MSBTS_HostInstance Where HostType=1 And IsDisabled=False", enumOptions);  int count = 1;  //Enumerate through the result set and start each HostInstance if it is already stopped   foreach (ManagementObject inst in searchObject.Get())  {  //Check if ServiceState is 'Stopped'   if (inst["ServiceState"].ToString() == "1")  {  if (count == 1)  {  strEmailBody = strEmailBody + "Below Host Instances are not running, attempting to start.<br>";  strEmailBody = strEmailBody + "<table><tr><th>Server Name</th><th>Host Instance Name</th></tr>";  ++count;  }  strEmailBody = strEmailBody + "" + inst["RunningServer"] + "" + inst["HostName"] + "";  inst.InvokeMethod("Start", null);  }  }  if (count > 1)  { strEmailBody = strEmailBody + "</table>All above mentioned Host Instances have been Started Successfully<br><br>"; }  }  catch (Exception excep)  {  System.Diagnostics.EventLog.WriteEntry("BizTalk Server", "Exception Occurred in enumerateAndStartHostInstances fuction call. " + excep.Message, System.Diagnostics.EventLogEntryType.Error);  throw new Exception("Exception Occurred in enumerateAndStartHostInstances fuction call. " + excep.Message);  }  } //Function to check available drive space  private static void checkAvailableDriveSpace(ref string strEmailBody, ref string strServerNames, ref double doubleAcceptableFreeDriveSpace)  {  try  {  int count = 1;  foreach (string strServerName in strServerNames.Split(';'))  {  //Connection credentials to the remote computer, not needed if the logged account has access  ConnectionOptions oConn = new ConnectionOptions(); //oConn.Username = "DummyUserName";//oConn.Password = "*********";   //Create EnumerationOptions and run wql query   EnumerationOptions enumOptions = new EnumerationOptions();  enumOptions.ReturnImmediately = false; string strNameSpace = @"\\" + strServerName + @"\root\cimv2";  //Get Fixed disk state. Check - http://www.csidata.com/custserv/onlinehelp/vbsdocs/vbs41.htm, DriveType = 2 - Fixed, Drive has fixed (nonremovable) media. This includes all hard drives, including hard drives that are removable. DriveType = 2 -Remote, Network drives. This includes drives shared anywhere on a network.  ManagementObjectSearcher searchObject = new ManagementObjectSearcher(strNameSpace, "select FreeSpace,Size,Name from Win32_LogicalDisk where DriveType=3", enumOptions);  //Enumerate through the result set   foreach (ManagementObject driveInfo in searchObject.Get())  {  double percentFreeSpace = 100 * (System.Convert.ToDouble(driveInfo["FreeSpace"]) / System.Convert.ToDouble(driveInfo["Size"]));  if (percentFreeSpace < doubleAcceptableFreeDriveSpace)  {  if (count == 1)  {  strEmailBody = strEmailBody + "Available Free Space for below drives is less than acceptable value of " + doubleAcceptableFreeDriveSpace.ToString() + "% ";  strEmailBody = strEmailBody + "<table><tr><th>Server Name</th><th>Drive Name</th><th>Total Size</th><th>Available Free Space</th><th>Percentage Free Space</th></tr>";  ++count;  }  strEmailBody = strEmailBody + "" + strServerName + "" + driveInfo["Name"] + "" + driveInfo["Size"] + "" + driveInfo["FreeSpace"] + "" + percentFreeSpace + "%";  }  }  }  if (count > 1)  { strEmailBody = strEmailBody + "</table><br><br>"; }  }  catch (Exception excep)  {  System.Diagnostics.EventLog.WriteEntry("BizTalk Server", "Exception Occurred in checkAvailableDriveSpace fuction call. " + excep.Message, System.Diagnostics.EventLogEntryType.Error);  throw new Exception("Exception Occurred in checkAvailableDriveSpace fuction call. " + excep.Message);  }  } //Function to check CPU Reading  private static void checkCPUReading(ref string strEmailBody, ref string strServerNames, ref double doubleAcceptableCPUPercentage, ref int intThreadSleepTimeInMilliSeconds)  {  try  {  int count = 1;  foreach (string strServerName in strServerNames.Split(';'))  {  PerformanceCounter totalProcessorTimeCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total", strServerName);  totalProcessorTimeCounter.NextValue();  System.Threading.Thread.Sleep(intThreadSleepTimeInMilliSeconds);// seconds wait  double CPUPercentage = totalProcessorTimeCounter.NextValue();  if (CPUPercentage > doubleAcceptableCPUPercentage)  {  if (count == 1)  {  strEmailBody = strEmailBody + "CPU Usage on below servers is higher than the acceptable value of " + doubleAcceptableCPUPercentage.ToString() + "% ";  strEmailBody = strEmailBody + "<table><tr><th>Server Name</th><th>CPU Percentage</th></tr>";  ++count;  }  strEmailBody = strEmailBody + "" + strServerName + "" + CPUPercentage.ToString() + "%";  }  }  if (count > 1)  { strEmailBody = strEmailBody + "</table><br><br>"; }  }  catch (Exception excep)  {  System.Diagnostics.EventLog.WriteEntry("BizTalk Server", "Error Occurred in checkCPUReading function call. " + excep.Message, System.Diagnostics.EventLogEntryType.Error);  throw new Exception("Error Occurred in checkCPUReading function call. " + excep.Message);  }  } //Function to Check Suspended Instances  private static void checkAllSuspendedInstances(ref string strEmailBody, ref int intAcceptableTotalSuspendedInstanceCount, ref string strOrchestrationNames, ref int intAcceptableSuspendedInstanceCountForAnOrch)  {  try  {  //Create EnumerationOptions and run wql query   EnumerationOptions enumOptions = new EnumerationOptions();  enumOptions.ReturnImmediately = false; //Search for all Service Instances for ServiceClass=1(Orchestration) or ServiceClass=4(Messaging) and (ServiceStatus=4(Suspended (resumable)) or ServiceStatus=32(Suspended (non-resumable)) in the Biztalk namespace scope   ManagementObjectSearcher searchObject = new ManagementObjectSearcher("root\\MicrosoftBizTalkServer", "Select * from MSBTS_ServiceInstance Where (ServiceClass=1 or ServiceClass=4) and (ServiceStatus=4 or ServiceStatus=32)", enumOptions);  if (searchObject.Get().Count > 0)  {  if (intAcceptableTotalSuspendedInstanceCount < searchObject.Get().Count)  {  strEmailBody = strEmailBody + "The total number of Suspended instances is higher than the acceptable value of " + intAcceptableTotalSuspendedInstanceCount.ToString() + " ";  strEmailBody = strEmailBody + "<table><tr><th>Total Number of Suspended Instance</th></tr>";  strEmailBody = strEmailBody + "" + searchObject.Get().Count.ToString() + "";  strEmailBody = strEmailBody + "</table><br><br>";  } //Now checking for individual Orchestrations Only If Suspended Instance Count not zero  int count = 1;  foreach (string strOrchName in strOrchestrationNames.Split(';'))  {  string strWQL = string.Format("SELECT * FROM MSBTS_ServiceInstance WHERE (ServiceClass=1 or ServiceClass=4) and (ServiceStatus=4 or ServiceStatus=32) AND ServiceName like \"%{0}%\"", strOrchName);  ManagementObjectSearcher searcherServiceInstance = new ManagementObjectSearcher(new ManagementScope("root\\MicrosoftBizTalkServer"), new WqlObjectQuery(strWQL), null);  if (searcherServiceInstance.Get().Count > intAcceptableSuspendedInstanceCountForAnOrch)  {  if (count == 1)  {  strEmailBody = strEmailBody + "The total number of Suspended instances for below Orchestrations is higher than the acceptable value of " + intAcceptableSuspendedInstanceCountForAnOrch.ToString() + " ";  strEmailBody = strEmailBody + "<table><tr><th>Orchestration Name</th><th>Number of Suspended Instances</th></tr>";  ++count;  }  strEmailBody = strEmailBody + "" + strOrchName + "" + searcherServiceInstance.Get().Count.ToString() + "";  }  }  if (count > 1)  { strEmailBody = strEmailBody + "</table><br><br>"; }  }  }  catch (Exception excep)  {  System.Diagnostics.EventLog.WriteEntry("BizTalk Server", "Error Occurred in checkAllSuspendedInstances function call. " + excep.Message, System.Diagnostics.EventLogEntryType.Error);  throw new Exception("Error Occurred in checkAllSuspendedInstances function call. " + excep.Message);  }  } //Function to check Fragmentation of BizTalkMsgBoxDb Table Indexes  private static void checkBizTalkMsgBoxTablesIndexFragmentation(ref string strEmailBody, ref string strConnectionString, ref double doubleAcceptableFragmentationPercentage)  {  try  {  if (!String.IsNullOrEmpty(strConnectionString))  {  //Checking Index Fragmentation if the value is more than Acceptable Fragmentation Percentage, Selecting top 10 values  string queryString = "SELECT top 10 dbtables.[name] as 'Table', dbindexes.[name] as 'Index', indexstats.avg_fragmentation_in_percent, indexstats.page_count ";  queryString = queryString + "FROM sys.dm_db_index_physical_stats (DB_ID(), NULL, NULL, NULL, NULL) AS indexstats INNER JOIN sys.tables dbtables on dbtables.[object_id] = indexstats.[object_id] INNER JOIN sys.schemas dbschemas on dbtables.[schema_id] = dbschemas.[schema_id] INNER JOIN sys.indexes AS dbindexes ON dbindexes.[object_id] = indexstats.[object_id] AND indexstats.index_id = dbindexes.index_id ";  queryString = queryString + "WHERE indexstats.database_id = DB_ID()and avg_fragmentation_in_percent > " + doubleAcceptableFragmentationPercentage.ToString() + " ";  queryString = queryString + "ORDER BY indexstats.avg_fragmentation_in_percent desc";  using (SqlConnection connection = new SqlConnection(strConnectionString))  {  using (SqlCommand command = new SqlCommand(queryString, connection))  {  connection.Open();  SqlDataReader reader = command.ExecuteReader();  if (reader.HasRows)  {  strEmailBody = strEmailBody + "Fragmentation Percentage for few indexes is more than the Acceptable Value of " + doubleAcceptableFragmentationPercentage.ToString() + "% <br>";  strEmailBody = strEmailBody + "<table><tr><th>Table Name</th><th>Index Name</th><th>Average Fragmentation in Percent</th><th>Page Count</th></tr>";  while (reader.Read())  {  strEmailBody = strEmailBody + "<tr><td>" + reader["Table"] + "</td><td>" + reader["Index"] + "</td><td>" + reader["avg_fragmentation_in_percent"] + "</td><td>" + reader["page_count"] + "</td></tr>";  }  strEmailBody = strEmailBody + "</table>" + Environment.NewLine + "<br><br>";  }  }  }  }  else throw new Exception("BizTalk Msg Box Connection String property is NULL");  }  catch (Exception excep)  {  System.Diagnostics.EventLog.WriteEntry("BizTalk Server", "Exception Occurred in checkBizTalkMsgBoxTablesIndexFragmentation fuction call. " + excep.Message, System.Diagnostics.EventLogEntryType.Error);  throw new Exception("Exception Occurred in checkBizTalkMsgBoxTablesIndexFragmentation fuction call. " + excep.Message);  }  } //Function to check Spool and Parts table Count and other tables with row count more than intAcceptableMsgBoxRowCount  private static void checkBizTalkMsgBoxTablesCount(ref string strEmailBody, ref string strConnectionString, ref int intAcceptableMsgBoxRowCount)  {  try  {  int count = 1;  if (!String.IsNullOrEmpty(strConnectionString))  {  //Checking top 10 tables with Row count more than intAcceptableMsgBoxRowCount  string queryString = "SELECT top 10 T.name TableName, I.Rows NumberOfRows FROM sys.tables T JOIN sys.sysindexes I ON T.OBJECT_ID = I.ID "  + "WHERE indid IN (0,1) and I.Rows > " + intAcceptableMsgBoxRowCount.ToString() + "ORDER BY I.Rows DESC,T.name";  using (SqlConnection connection = new SqlConnection(strConnectionString))  {  using (SqlCommand command = new SqlCommand(queryString, connection))  {  connection.Open();  SqlDataReader reader = command.ExecuteReader();  if (reader.HasRows)  {  while (reader.Read())  {  if (count == 1)  {  strEmailBody = strEmailBody + "The Row Count of Below Tables is more than the acceptable value of " + intAcceptableMsgBoxRowCount + " ";  strEmailBody = strEmailBody + "<table><tr><th>Table Name</th><th>Current Row Count(Exceeded Value)</th></tr>";  ++count;  }  strEmailBody = strEmailBody + "<tr><td>" + reader["TableName"].ToString() + "</td><td>" + reader["NumberOfRows"] + "</td></tr>";  }  if (count > 1)  strEmailBody = strEmailBody + "</table><br><br>";  }  }  }  }  else throw new Exception("BizTalk Msg Box Connection String property is NULL");  }  catch (Exception excep)  {  System.Diagnostics.EventLog.WriteEntry("BizTalk Server", "Exception Occurred in checkBizTalkMsgBoxTablesCount fuction call. " + excep.Message, System.Diagnostics.EventLogEntryType.Error);  throw new Exception("Exception Occurred in checkBizTalkMsgBoxTablesCount fuction call. " + excep.Message);  }  } //Functoin to send email  private static void sendEmail(string strEmailBody)  {  //Get SMTP details from config  var appSettings = ConfigurationManager.AppSettings; try  {  //if ((appSettings["SMTPHost"] != null) && (appSettings["FROMEmailID"] != null) && (appSettings["TOEmailID"] != null) && (appSettings["Subject"] != null) && (appSettings["SMTPServerPort"] != null) && (appSettings["Username"] != null) && (appSettings["Password"] != null))  //{  MailMessage mail = new MailMessage();  SmtpClient SmtpServer = new SmtpClient(appSettings["SMTPHost"].ToString());  mail.From = new MailAddress(appSettings["FROMEmailID"].ToString());  mail.To.Add(appSettings["TOEmailID"].ToString());  mail.Subject = appSettings["Subject"].ToString();  mail.Body = strEmailBody;  mail.IsBodyHtml = true; SmtpServer.EnableSsl = true;  //SmtpServer.DeliveryMethod = SmtpDeliveryMethod.Network;  //SmtpServer.UseDefaultCredentials = false;  SmtpServer.Port = System.Convert.ToInt32(appSettings["SMTPServerPort"]);  //SmtpServer.Credentials = new System.Net.NetworkCredential(appSettings["Username"].ToString(), appSettings["Password"].ToString()); SmtpServer.Send(mail);  System.Diagnostics.EventLog.WriteEntry("BizTalk Server", "Email Sent", System.Diagnostics.EventLogEntryType.SuccessAudit);  //}  //else  //{  // System.Diagnostics.EventLog.WriteEntry("BizTalk Server", "Error occurred while sending email - Some mandatory configuration values is missing. Please check if corresponding values are given in config file- SMTPHost, FROMEmailID, ToEmailID, Subject, SMTPServerPort, Username, Password");  // throw new Exception("Error occurred while sending email - Some mandatory configuration values is missing. Please check if corresponding values are given in config file- SMTPHost, FROMEmailID, ToEmailID, Subject, SMTPServerPort, Username, Password");  //}  }  catch (Exception expMailSend)  {  System.Diagnostics.EventLog.WriteEntry("BizTalk Server", expMailSend.Message, System.Diagnostics.EventLogEntryType.Error);  throw new Exception("Error occurred while sending email - " + expMailSend.Message);  }  }  } }

Sample Email –

SampleEmail

Download the complete code from here

I will keep on adding new functionality so make sure you donwload the latest version of code.

Contact Me:- 

@Gmail@Facebook , @Twitter, @LinkedIn @MSDNTechnet, @My Personal Blog 

Check out my other blogs –