You can set this up in Task Scheduler to run every x minutes and send you an email when there are any errors in the Application Event Log or the System Event Log.

There is a section in the code where you can create more user-friendly messages for complex or confusing error messages. It also allows you to filter out certain known errors that you get over-and-over, but do not want to fix. (For example, we saw an error from a tool called “Monitis” every hour or so, so we didn’t want emails to go out on those types of known and common errors. We were primarily interested in BizTalk errors. (Look for the code: “Where-Object {$_.Message -notlike ‘Monitis*'” to set up your own filters to exclude certain types of errors).

#########################################################
#
# EventLog Monitoring and reporting script
# Neal Walters - 06/18/2013 
# Modeled after DiskMonitor.ps1 
#
#  11/08/2013 - added Select-Object -Propert to only return desired columns (originally 10/17/2013)
#
#########################################################

cls
[string[]] $users = "test@abc.com","test2@abc.com","admin@xyz.com" # List of users to email your report to (separate by comma) or use a distribution list 

$TraceFilename = "E:\Scripts\EventLogExtract_Trace.txt"
$fromemail = "FromEmail@abc.com"
$server = "your.emailRelayServer.com" #enter your own SMTP or Relay server DNS name / IP address here
$minutes = $args[0]
$environment = $args[1] 
$skipmail = $args[2] 

#allow this to be run 
if ($minutes -eq $null)
   {
     $minutes = 10
   }
   
if ($environment -eq $null)
   {
     $environment = "New-BT-Cluster" 
   }

$activeClusterName = (Get-WMIObject Win32_ComputerSystem -ComputerName BIZTALK2013).Name
Write-Host "`$activeClusterName=$activeClusterName"
$computerName = get-content env:computername
if ($activeClusterName -eq $computerName)
 {
   $activeNodeMessage = "(Is-Active-Node)" 
 }
else 
 {
   $activeNodeMessage = "(Is-NOT-Active-Node)" 
 }

#Write-Host $minutes

$traceDateTime = get-date
Write-Host "$traceDateTime Starting Application EventLog"

#$tableFragment = Get-Eventlog -log application -after ((get-date).addMinutes(-120)) -EntryType Error, Warning | ConvertTo-Html -fragment
#multiple $minutes by negative 1 to go back x minutes 
$getEventLog = Get-Eventlog -log application -after ((get-date).addMinutes($minutes*-1)) -EntryType Error 
$traceDateTime = get-date
Write-Host "$traceDateTime Completed Application EventLog count=$($getEventLog.Count)"
Add-Content $TraceFilename "$traceDateTime TRC20 Completed Application EventLog count=$($getEventLog.Count)"
Write-Host "$traceDateTime Starting System EventLog"
$getEventLogSys = Get-Eventlog -log system -after ((get-date).addMinutes($minutes*-1)) -EntryType Error 
$traceDateTime = get-date
Write-Host "$traceDateTime Completed System EventLog count=$($getEventLogSys.Count)"
Add-Content $TraceFilename "$traceDateTime TRC21 Completed System EventLog count=$($getEventLogSys.Count)"

# list of events to exclude (based on text found in the message 
# NOTE: Powershell uses grave-accent mark as line continuation character, but when I split onto 3 lines 
#       I seemed to get emails that had the excluded phrases. 
$getEventLogFiltered = $getEventLog | Where-Object {$_.Message -notlike 'Monitis*' -and $_.Message -notlike '*MQQueueDepthMonitor.exe*' -and $_.Message -notlike '*The local computer may not have the necessary registry*' }
Add-Content $TraceFilename "$traceDateTime TRC22 Filtered EventLog count=$($getEventLog.Count)"
#to only select certain columns, use Select-Object -Property and list the property/columns 									   
$getEventLogColumns = 	$getEventLogFiltered	| Select-Object -Property TimeGenerated,Source,Message,EntryType,EventID

$getEventLogWithSelectedColumns = 	$getEventLogFiltered |   
    Select-Object -Property TimeGenerated,Source,@{name='FriendlyDescr';expression={Message}},Message,EntryType,EventID
	
#$getEventLogSysWithSelectedColumns = 	$getEventLogSys |   
#    Select-Object -Property TimeGenerated,Source,@{name='FriendlyDescr';expression={Message}},Message,EntryType,EventID

if ($getEventLogFiltered -ne $null -or $getEventLogSys -ne $null) 
   {
        $needToSendEmail = "Y" 
	
	
	    #
		# This is where you can put short friendly Messages that the average guy can understand. 
		#
		foreach ($item in $getEventLogWithSelectedColumns) 
		  {
		     $item.FriendlyDescr = "N.A."
			 #switch -wildcard ($item.Message) 
			 #  {
			 #     "*The file exists*" {$Item.FriendlyDescr="Duplicate File";break}
			 #  }
		     if ($item.Message.Contains("SQL Timeout")) 
			    {
				   $item.FriendlyDescr = "SQL-Timeout"
				}
			 
		  }
   }
   else 
   {
      Write-Host "No EventLog Rows selected" 
   }

#see http://foxdeploy.com/2014/05/23/using-html-formatting-to-create-useful-webpage-reports-from-powershell/
$head = @"
<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01 Frameset//EN” “http://www.w3.org/TR/html4/frameset.dtd”&gt;
<html><head><title>Unmigrated Systems Report</title><meta http-equiv=”refresh” content=”120? />
<style type=”text/css”>
<!–
body {
font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
}

#report { width: 1400px; }

table{
border-collapse: collapse;
border: none;
font: 10pt Verdana, Geneva, Arial, Helvetica, sans-serif;
color: black;
margin-bottom: 10px;
width: 1400px;
}

table td{
font-size: 12px;
padding-left: 0px;
padding-right: 20px;
text-align: left;
}

table th {
font-size: 12px;
font-weight: bold;
padding-left: 0px;
padding-right: 20px;
text-align: left;
}

h2{ clear: both; font-size: 130%;color:#354B5E; }

h3{
clear: both;
font-size: 75%;
margin-left: 20px;
margin-top: 30px;
color:#475F77;
}

p{ margin-left: 20px; font-size: 12px; }

table.list{ float: left; }

table.list td:nth-child(1){
font-weight: bold;
border-right: 1px grey solid;
text-align: right;
}

table.list td:nth-child(2){ padding-left: 7px; }
table tr:nth-child(even) td:nth-child(even){ background: #BBBBBB; }
table tr:nth-child(odd) td:nth-child(odd){ background: #F2F2F2; }
table tr:nth-child(even) td:nth-child(odd){ background: #DDDDDD; }
table tr:nth-child(odd) td:nth-child(even){ background: #E5E5E5; }
div.column { width: 320px; float: left; }
div.first{ padding-right: 20px; border-right: 1px grey solid; }
div.second{ margin-left: 30px; }
table{ margin-left: 20px; }
–>
</style>
</head>

"@

#seems like we cannot put -head and -fragment on the same ConvertTo-Html statemnet 
#$tableFragment = $getEventLogWithSelectedColumns | 
#    ConvertTo-Html -Fragment TimeGenerated,EntryType,Source,FriendlyDescr,Message,EventID 
	
$tableFragmentTemp = $getEventLogWithSelectedColumns | 
    ConvertTo-Html -Fragment TimeGenerated,EntryType,Source,FriendlyDescr,Message,EventID 
	
$tableTempFragments = "<h2><font color='yellow'>Application Event Log<font></h2>" + $tableFragmentTemp 

if ($getEventLogSys -ne $null) 
{
$tableFragmentTempSys = $getEventLogSys | 
    ConvertTo-Html -Fragment TimeGenerated,EntryType,Source,FriendlyDescr,Message,EventID 

$tableTempFragments = $tableTempFragments + "<h2><font color='yellow'>System Event Log<font></h2>" + $tableFragmentTempSys
}

$tableFragment = ConvertTo-HTML -head $head -PostContent $tableTempFragments 


# Production red-alert colors 
if ($environment -eq "PROD")
{
	$background = "Red" 
	$backgroundSolid = "DarkRed"
	$foreground = "White"
}
else
{
    # QA  colors 
	$background = "White" 
	$backgroundSolid = "Blue"
	$foreground = "Blue"
}

Write-Host "`$activeClusterName=$activeClusterName"

# assemble the HTML for our body of the email report.
$HTMLmessage = @"
<font color=""black"" face=""Arial, Verdana"" size=""3"">
<u><b>$Environment (Machine=$computerName $activeNodeMessage EventLog Error Report</b></u>
<br>This report was generated because of Errors found in the EventLog in the last reporting interval of $minutes minutes. 
<br>ActiveNode=$activeClusterName
<br>
<br>
<style type=""text/css"">body{font: .8em ""Lucida Grande"", Tahoma, Arial, Helvetica, sans-serif;}
ol{margin:0;padding: 0 1.5em;}
table{color:$foreground;background:$background;border-collapse:collapse;width:647px;border:5px solid $backgroundSolid;}
thead{}
thead th{padding:1em 1em .5em;border-bottom:1px dotted #FFF;font-size:120%;text-align:left;}
thead tr{}
td{padding:.5em 1em;}
tfoot{}
tfoot td{padding-bottom:1.5em;}
tfoot tr{}
#middle{background-color:$background;}
</style>
<body BGCOLOR="$foreground">
$tableFragment
</body>
"@

# Set up a regex search and match to look for any <td> tags in our body. These would only be present if the script above found disks below the threshold of free space.
# We use this regex matching method to determine whether or not we should send the email and report.
$regexsubject = $HTMLmessage
$regex = [regex] '(?im)<td>'
$traceDateTime = get-date

# if there was any row at all, send the email
if ($skipmail -ne "Y")
{
	if ($regex.IsMatch($regexsubject)) 
	{
	            $emailSubject = "$environment Event Log Error Report - Computer=$computerName"
                send-mailmessage -from $fromemail -to $users -subject $emailSubject  -BodyAsHTML -body $HTMLmessage -priority High -smtpServer $server
 		        Write-Host "$traceDateTime Email sent" 
                Add-Content $TraceFilename "$traceDateTime TRC95 Email Sent "
	}
        else
        {
                Write-Host "$traceDateTime No email was needed because no Errors in Application EventLog in the last $minutes minutes" 
                Add-Content $TraceFilename "$traceDateTime TRC96 No Email Sent "
        }
}
 
# End of Script

What I do is set up a .cmd or .bat file like this, and that is what I put in the scheduler. It simpley runs the Powershell and passes the necessary parms.
Make sure the first parm (10 in the example below) matches exactly how often you run the job in the task scheduler.

powershell -command "& 'E:\Scripts\EventLogExtract.ps1'" 10 NEW-BT-PROD

One day, I hope to be able to fully understand and explain the “Ambient Transaction” true/false option in BizTalk WCF-SQL and WCF-Custom send and receive ports.  Until then, I’m going to collect the most relevant info and blogs here:

The official Microsoft BizTalk parameter definition (for the WCF Send or Receive Port): http://msdn.microsoft.com/en-us/library/dd787981.aspx

Specifies whether the SQL adapter performs the operations using the transactional context provided by the caller. Default is true, which means that the adapter always performs the operations in a transactional context. If there are other resources participating in the transaction, and SQL Server also joins the transaction, the transaction gets elevated to an MSDTC transaction.

However, there can be scenarios where you do not want the adapter to perform operations in a transactional context. For example:

  • While performing a simple Select operation on a SQL Server database
  • While specifying a polling statement that performs a Select operation, and does not involve any changes to the table either through a Delete statement or by invoking a stored procedure.

Both these operations do not make any updates to the database table and, hence, elevating these operations to use an MSDTC transaction can be a performance overhead. In such scenarios, you can set the binding property to false so that the SQL adapter does not perform the operations in a transactional context.

Not performing operations in a transactional context is advisable only for operations that do not make changes to the database. For operations that update data in the database, we recommend setting the binding property to true; otherwise you might either experience message loss or duplicate messages, depending on whether you are performing inbound or outbound operations.

 

http://seroter.wordpress.com/biztalk-and-wcf-part-iii-transaction-patterns/  (Richard Seroter – usually has great blogs on BizTalk topics)

http://msdn.microsoft.com/en-us/library/ms973865.aspx (Richard Seroter above refers to the “Ambient Transaction” feature as being part of the Systems.Transaction in .NET Framework 2.0 and afterwards.  It seems like understanding Ambient Transactions from native C# first would be key to understanding how it works in BizTalk.

The above link provides the following information, but as for me now, it does not pass the “so what” and the “WIIFM” (What’s In It for Me) test.

System.Transactions defines a concept called an ambient transaction. The ambient transaction is the transaction that is present in the thread that the current application code is executing within. To obtain a reference to the ambient transaction call the static Current property of Transaction:

<em>Transaction ambientTransaction = Transaction.Current;</em>

If there is no ambient transaction, Current will return null.

The ambient transaction object is stored in the thread local storage (TLS). As a result, when the thread winds its way across multiple objects and methods, all objects and methods can access their ambient transaction.

Later it says:

The value of TransactionScopeOption lets you control whether the scope takes part in a transaction, and if so, whether it will join the ambient transaction or become the root scope of a new transaction.

 

A TransactionScope object has three options:

  • Join the ambient transaction.
  • Be a new scope root, that is, start a new transaction and have that transaction be the new ambient transaction inside its own scope.
  • Do not take part in a transaction at all.

 

This forum post tries to answer the question: “What exactly is an ambient transaction?”

http://social.msdn.microsoft.com/Forums/en-US/781c69f5-637f-4adb-a674-c065ea7ea69b/ambient-transactions?forum=csharplanguage

 

StackOverflow: http://stackoverflow.com/questions/224689/transactions-in-net

There are 2 main kinds of transactions; connection transactions and ambient transactions. A connection transaction (such as SqlTransaction) is tied directly to the db connection (such as SqlConnection), which means that you have to keep passing the connection around – OK in some cases, but doesn’t allow “create/use/release” usage, and doesn’t allow cross-db work.

The alternative is an ambient transaction; new in .NET 2.0, the TransactionScope object (System.Transactions.dll) allows use over a range of operations (suitable providers will automatically enlist in the ambient transaction). This makes it easy to retro-fit into existing (non-transactional) code, and to talk to multiple providers (although DTC will get involved if you talk to more than one).

Note here that the two methods can handle their own connections (open/use/close/dispose), yet they will silently become part of the ambient transaction without us having to pass anything in.

If your code errors, Dispose() will be called without Complete(), so it will be rolled back. The expected nesting etc is supported, although you can’t roll-back an inner transaction yet complete the outer transaction: if anybody is unhappy, the transaction is aborted.

The other advantage of TransactionScope is that it isn’t tied just to databases; any transaction-aware provider can use it. WCF, for example. Or there are even some TransactionScope-compatible object models around (i.e. .NET classes with rollback capability – perhaps easier than a memento, although I’ve never used this approach myself).

 

Blog on on the topic:  Transaction.Current and Ambient Transactions by Florin Lazar:

http://blogs.msdn.com/b/florinlazar/archive/2005/04/19/409570.aspx

Key phrase from above blog: “Transaction.Current can detect that the transaction was created inside a COM+ context and provide it to anyone asking for the ambient transaction.

StackOverflow: Difference Between Transaction and TransactionScope

http://stackoverflow.com/questions/6493173/difference-between-transaction-and-transactionscope

Key phrase: The ambient transaction is the transaction within which your code executes.

Transaction Scope Class (C# examples): http://msdn.microsoft.com/en-us/library/system.transactions.transactionscope%28v=vs.90%29.aspx

And final link – to a comparison of Transaction Handling in 1.0 and 2.0 .NET (rather old, but may help give the background of when the “Ambient Transaction” was birthed: http://msdn.microsoft.com/en-us/library/ms973865.aspx

CodeProject Sample Demo/Code: http://www.codeproject.com/Articles/690136/All-About-TransactionScope

A transaction which automatically identifies a code block that needs to support a transaction without explicitly mentioning any transaction related things. An ambient transaction is not tied just to a database, any transaction aware provider can be used. TransactionScope implements an ambient transaction. If you see the use of TransactionScope, you will not find transaction related anything sent to any method or setting any property. A code block is automatically attached with the transaction if that code is in any TransactionScope. A WCF transaction is another example of a transaction aware provider. Any one can write a transaction aware provider like the WCF implementation.

 

 

WCF-SQL and DTC Ambient Transactions in a cross domain scenario

http://adventuresinsidethemessagebox.wordpress.com/2014/03/30/wcf-sql-and-dtc-ambient-transactions-in-a-cross-domain-scenario/

The ambient transaction option ensures that the BizTalk adapter flows a transaction through to SQL Server and thus the SQL transaction will only commit when the message received by BizTalk is successfully written to the BizTalk message box database.  This is of course crucial in a guaranteed delivery based solution where you can’t afford to lose any messages.

I found that all worked well when ambient transactions were turned off, however when turned on it looked like the receive location just hangs, holding a lock on SQL resources (I tried to do a select on the table in question using SQL Server Management Studio and it couldn’t return any values due to locks being in place) which won’t be removed until the host instance is reset.

The above blog talks about using DTCPing and DTCTester to identify DTC issues, and fixing them by laxing the security options in the DTC configuration.

Conclusions (or lack thereof):

So for now, I suggest studying that second web page to get an idea of what the topic is really about.  If I had time, I would write some C# programs to test outside of BizTalk.

 

09/15/2014 14:46:28 Test
09/15/2014 14:47:02 Test
09/15/2014 14:48:02 Test
09/15/2014 14:49:02 Test

cls
$PSVersionTable
$Action1 = New-ScheduledTaskAction -Execute "I:\Scripts\TestClusteredTaskRS.cmd"
$Action2 = New-ScheduledTaskAction -Execute "E:\Scripts\TestClusteredTaskCW.cmd"
$DurationTimeSpan = New-TimeSpan -Minutes 1 
$DurationTimeSpanIndefinite	= ([TimeSpan]::MaxValue) 
$DurationTempTest = New-TimeSpan -Days 1000
$Trigger = New-ScheduledTaskTrigger -Once -At "00:01" -RepetitionInterval $DurationTimeSpan -RepetitionDuration $DurationTimeSpanIndefinite  

#Register-ScheduledTask -TaskName "RegularCalcTest2" -Action $action
#Register-ClusteredScheduledTask -TaskName "CalcTask01" -TaskType AnyNode -Action $Action1 -Trigger $Trigger -Cluster "YourClusterNameHere"
Register-ClusteredScheduledTask -TaskName "TestResourceSpecific" -TaskType ResourceSpecific  -Action $Action1 -Trigger $Trigger -Cluster "YourClusterNameHere" -Resource BizTalk2013
Register-ClusteredScheduledTask -TaskName "TestClusterWide"      -TaskType ClusterWide       -Action $Action2 -Trigger $Trigger -Cluster "YourClusterNameHere"

Contents of TestClusteredTaskCW.cmd

powershell -command "&amp; 'E:\Scripts\TestClusteredTaskCW.ps1'"

TestClusteredTaskCW.ps1

cls
$TraceFilename = "E:\scripts\TestClusterWide_Trace.txt"
$currentDateTime = Get-Date 
Add-Content $TraceFilename "$currentDateTime Test" 
Write-Host "Completed, see `$TraceFilename=$TraceFilename" 

ClusterName : YourClusterNameHere
CurrentOwner :
Resource : 6f04356c-2f34-306b-9afb-a64e9556942f
TaskDefinition : MSFT_ScheduledTask (TaskName = “TestResourceSpecific”, TaskPath)
TaskName : TestResourceSpecific
TaskType : ResourceSpecific
PSComputerName :

ClusterName : YourClusterNameHere
CurrentOwner :
Resource :
TaskDefinition : MSFT_ScheduledTask (TaskName = “TestClusterWide”, TaskPath)
TaskName : TestClusterWide
TaskType : ClusterWide
PSComputerName :

cls
Get-ClusteredScheduledTask
Write “==================”
Write “Expanded:”
foreach ($item in Get-ClusteredScheduledTask )
{
Write-Host $item.TaskName
}

TaskName TaskType
——– ——–
TestClusterWide ClusterWide
TestResourceS… ResourceSpecific
==================
Expanded:
TestClusterWide
TestResourceSpecific

 

Related Posts:

1. http://stackoverflow.com/questions/16767064/clustered-scheduled-task

2. http://serverfault.com/questions/623407/powershell-set-clusteredscheduledtask-error-incorrect-function – error trying to Register-ClusteredScheduledTask

3. http://serverfault.com/questions/628958/how-to-enable-or-disable-a-clusteredscheduledtask-in-powershell-4-0 – how to you enable/disable a ClusteredScheduledTask?

 

I’m still cleaning up this blog post.  Here are some points to consider.

 

1. With ClusteredScheduledTasks there is no GUI.  When you do register a ClusteredScheduleTask using Powershell, it shows up in the GUI, but even then it is not reliable to use the GUI to change anything.   I tried changing few things, and they certainly did NOT sync between the two servers.

2. The problem with regular ScheduledTasks is that you have to maintain one TaskScheduler on each machine in the node.  Not too bad with two nodes, but if you have more it can be tedious.  Even with two nodes, everyone that is an admin has to be a stickler for keeping them in sync.
We thought about trying to write some code to keep them in sync, but looks like that could take several days.  In my opinion, you have the task, the triggers, and the actions all to keep in sync, and the triggers themselves have quite a few options.

At my client, we had two nodes, and decided to live with regular scheduled tasks for now.

3. You can always buy a third party tool.  Where I used to work, they had “VisualCron“.  I recently asked their sales person if they supported clustering, and rather than giving me a straight forward yes or no, and he sent me to a forum post which implies it can be done, but if it is so easy, there would be that much discussion about it.

4. We considered using SQL Agent as our task scheduler, but then we might have to change some of our code to work “cross-machine”, i.e. from the SQL machine over to the machine being monitored.  (Some of our schedule tasks kick of BizTalk jobs, and others are for monitoring EventLog, creation of extract files, MQ Depth, etc…)

 

 

Suppose you run this:
Get-ClusteredScheduledTask

and get this as a result:

TaskName         TaskType                         
--------         --------                         
TestClusterWide  ClusterWide                      
TestResourceS... ResourceSpecific   

What a pain. So how do you see the value of the second TaskName above.
One idea would be to use names under 12 characters. If you must or do use longer names, here’s one solution:

cls
Get-ClusteredScheduledTask
Write "=================="
Write "Expanded:"
foreach ($item in Get-ClusteredScheduledTask )
{
   Write-Host "TaskName=$($item.TaskName) TaskType=$($item.TaskType)"
}

TaskName         TaskType                         
--------         --------                         
TestClusterWide  ClusterWide                      
TestResourceS... ResourceSpecific                 
==================
Expanded:
TaskName=TestClusterWide TaskType=ClusterWide
TaskName=TestResourceSpecific TaskType=ResourceSpecific

Function “IsActiveNode”

function IsActiveNode
{
$activeClusterName = (Get-WMIObject Win32_ComputerSystem -ComputerName BIZTALK2013).Name
$computerName = get-content env:computername
$message = "ActiveCluster=$activeClusterName computerName=$computerName" 
$isSame = $false 
if ($activeClusterName -eq $computerName) 
  {
	 $isSame = $true 
  }
$message = $message + " IsSame=$isSame"  
#Write-Host $message  
return $isSame 
}

cls
$IsActiveNodeResult = IsActiveNode
Write-Host "IsActiveNode=$IsActiveNodeResult"

Sample test:

. .\IsActiveNode.ps1
cls
$IsActiveNodeResult = IsActiveNode
Write-Host "IsActiveNode=$IsActiveNodeResult"
if ($IsActiveNodeResult -eq $false) 
  {
     Write-Host "exiting - no action to be taken on inactive node"
	 return 
  }
Write-Host "We are still here" 

No, BizTalk 2013 (nor prior release of BizTalk) do NOT support IBM WebSphere MQ 8.0. We confirmed this with a ticket to Microsoft.

What is confusing is the following on Microsoft page: http://msdn.microsoft.com/en-us/library/jj248684%28v=bts.80%29.aspx#BKMK_MQSeries

According to current information, supported IBM WebSphere MQ versions in Microsoft BizTalk Server 2013 and 2013 R2 :

    IBM WebSphere MQ 6.0.2.12 and later
    IBM WebSphere MQ 7.0.1.9 and later
    IBM WebSphere MQ 7.1.0.5 and later
    IBM WebSphere MQ 7.5.0.3 and later

Further information: Install MQSeries Prerequisites

“And later” apparently means only in the same 7.x series. Apparently WebSphere 8.0 added 64-bit support, and BizTalk has not yet been updated to use it. BizTalk will try to work, but apparently it has an issue not being able to load some IBM WebSphere .DLL that was moved to a different folder path.